@dv.nghiem/flowdeck 0.2.4 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/README.md +24 -41
  2. package/dist/hooks/memory-hook.d.ts +21 -0
  3. package/dist/hooks/memory-hook.d.ts.map +1 -0
  4. package/dist/hooks/orchestrator-guard-hook.d.ts.map +1 -1
  5. package/dist/hooks/todo-hook.d.ts +1 -7
  6. package/dist/hooks/todo-hook.d.ts.map +1 -1
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +709 -420
  9. package/dist/services/memory-store.d.ts +40 -0
  10. package/dist/services/memory-store.d.ts.map +1 -0
  11. package/dist/tools/memory-search.d.ts +3 -0
  12. package/dist/tools/memory-search.d.ts.map +1 -0
  13. package/docs/commands/fd-doctor.md +21 -0
  14. package/docs/commands/fd-quick.md +33 -0
  15. package/docs/commands/fd-reflect.md +23 -0
  16. package/docs/commands/fd-status.md +31 -0
  17. package/docs/commands/fd-translate-intent.md +17 -0
  18. package/docs/commands.md +209 -271
  19. package/docs/configuration.md +1 -2
  20. package/docs/index.md +22 -28
  21. package/docs/memory.md +69 -0
  22. package/docs/quick-start.md +1 -1
  23. package/package.json +1 -1
  24. package/src/commands/fd-deploy-check.md +131 -11
  25. package/src/commands/fd-new-project.md +14 -1
  26. package/src/commands/fd-quick.md +60 -0
  27. package/src/commands/fd-reflect.md +41 -2
  28. package/src/commands/fd-status.md +84 -0
  29. package/src/rules/README.md +8 -7
  30. package/src/skills/agent-harness-construction/SKILL.md +227 -0
  31. package/src/skills/api-design/SKILL.md +5 -0
  32. package/src/skills/backend-patterns/SKILL.md +105 -0
  33. package/src/skills/clean-architecture/SKILL.md +85 -0
  34. package/src/skills/cqrs/SKILL.md +230 -0
  35. package/src/skills/ddd-architecture/SKILL.md +104 -0
  36. package/src/skills/django-patterns/SKILL.md +304 -0
  37. package/src/skills/django-tdd/SKILL.md +297 -0
  38. package/src/skills/event-driven-architecture/SKILL.md +152 -0
  39. package/src/skills/frontend-pattern/SKILL.md +159 -0
  40. package/src/skills/hexagonal-architecture/SKILL.md +80 -0
  41. package/src/skills/layered-architecture/SKILL.md +64 -0
  42. package/src/skills/postgres-patterns/SKILL.md +74 -0
  43. package/src/skills/python-patterns/SKILL.md +5 -0
  44. package/src/skills/saga-architecture/SKILL.md +113 -0
  45. package/dist/tools/run-parallel.d.ts +0 -4
  46. package/dist/tools/run-parallel.d.ts.map +0 -1
  47. package/docs/command-migration.md +0 -175
  48. package/docs/commands/fd-analyze-change.md +0 -107
  49. package/docs/commands/fd-dashboard.md +0 -11
  50. package/docs/commands/fd-evaluate-risk.md +0 -134
  51. package/docs/commands/fd-guarded-edit.md +0 -105
  52. package/docs/commands/fd-progress.md +0 -11
  53. package/docs/commands/fd-review-code.md +0 -29
  54. package/docs/commands/fd-roadmap.md +0 -10
  55. package/docs/commands/fd-settings.md +0 -10
  56. package/docs/parallel-execution.md +0 -255
  57. package/src/commands/fd-analyze-change.md +0 -57
  58. package/src/commands/fd-approve.md +0 -64
  59. package/src/commands/fd-blast-radius.md +0 -49
  60. package/src/commands/fd-dashboard.md +0 -57
  61. package/src/commands/fd-evaluate-risk.md +0 -62
  62. package/src/commands/fd-guarded-edit.md +0 -69
  63. package/src/commands/fd-impact-radar.md +0 -51
  64. package/src/commands/fd-learn.md +0 -36
  65. package/src/commands/fd-progress.md +0 -50
  66. package/src/commands/fd-regression-predict.md +0 -57
  67. package/src/commands/fd-review-code.md +0 -96
  68. package/src/commands/fd-review-route.md +0 -54
  69. package/src/commands/fd-roadmap.md +0 -46
  70. package/src/commands/fd-settings.md +0 -57
  71. package/src/commands/fd-test-gap.md +0 -54
  72. package/src/commands/fd-volatility-map.md +0 -64
  73. package/src/commands/fd-workspace-status.md +0 -34
  74. package/src/skills/parallel-execute/SKILL.md +0 -92
@@ -0,0 +1,152 @@
1
+ # Event-Driven Architecture
2
+
3
+ ## When to Activate
4
+
5
+ Activate when:
6
+ - Designing or implementing message-based communication between services
7
+ - Building systems that require asynchronous processing
8
+ - Decoupling producers from consumers in distributed systems
9
+ - Implementing event sourcing or audit trails
10
+ - Setting up webhooks, message queues, or pub/sub patterns
11
+
12
+ ## Steps
13
+
14
+ ### 1. Identify Event Boundaries
15
+
16
+ Define what constitutes an "event" in your system:
17
+ - Events are **facts** about something that happened (past tense: `OrderPlaced`, `PaymentProcessed`)
18
+ - Commands are **requests** for an action (present tense: `PlaceOrder`, `ProcessPayment`)
19
+ - Events should be **immutable** once emitted
20
+
21
+ ### 2. Choose the Right Messaging Pattern
22
+
23
+ | Pattern | Use Case | Examples |
24
+ |---------|----------|----------|
25
+ | Pub/Sub | One-to-many notification | Notifications, audit logs |
26
+ | Message Queue | Point-to-point processing | Order processing, email sending |
27
+ | Event Streaming | Durable, replayable event log | Event sourcing, analytics |
28
+ | Webhooks | External system integration | HTTP callbacks |
29
+
30
+ ### 3. Design Event Schema
31
+
32
+ ```typescript
33
+ interface Event<T> {
34
+ id: string; // Unique event identifier (UUID)
35
+ type: string; // Event type (e.g., "ORDER_PLACED")
36
+ version: string; // Schema version for evolution
37
+ timestamp: string; // ISO 8601 timestamp
38
+ source: string; // Origin service name
39
+ data: T; // Event payload
40
+ metadata?: Record<string, unknown>; // Optional tracing/correlation
41
+ }
42
+ ```
43
+
44
+ ### 4. Handle Eventual Consistency
45
+
46
+ - Design consumers to be **idempotent** (safe to process twice)
47
+ - Use **correlation IDs** to track event chains
48
+ - Implement **dead letter queues** for failed processing
49
+ - Set **retry policies** with exponential backoff
50
+
51
+ ### 5. Ensure Durability
52
+
53
+ - Use persistent message storage (not in-memory)
54
+ - Acknowledge messages only after successful processing
55
+ - Implement **at-least-once** delivery semantics
56
+
57
+ ## Examples
58
+
59
+ ### TypeScript Event Emitter
60
+
61
+ ```typescript
62
+ interface OrderEvent {
63
+ orderId: string;
64
+ customerId: string;
65
+ total: number;
66
+ items: OrderItem[];
67
+ }
68
+
69
+ class OrderEventPublisher {
70
+ private emitter: EventEmitter;
71
+
72
+ async publishOrderPlaced(event: OrderEvent): Promise<void> {
73
+ const message: Event<OrderEvent> = {
74
+ id: crypto.randomUUID(),
75
+ type: 'ORDER_PLACED',
76
+ version: '1.0',
77
+ timestamp: new Date().toISOString(),
78
+ source: 'order-service',
79
+ data: event,
80
+ metadata: {
81
+ correlationId: event.orderId,
82
+ partitionKey: event.customerId
83
+ }
84
+ };
85
+
86
+ await this.messageBroker.publish('orders.placed', message);
87
+ }
88
+ }
89
+ ```
90
+
91
+ ### Message Consumer with Retry
92
+
93
+ ```typescript
94
+ class OrderEventConsumer {
95
+ async handleOrderPlaced(event: Event<OrderEvent>): Promise<void> {
96
+ try {
97
+ // Idempotent processing
98
+ const existingOrder = await this.orderRepo.findById(event.data.orderId);
99
+ if (existingOrder) {
100
+ logger.info('Order already processed, skipping', { orderId: event.data.orderId });
101
+ return;
102
+ }
103
+
104
+ await this.orderService.processOrder(event.data);
105
+ await this.messageBroker.ack(event.id);
106
+ } catch (error) {
107
+ if (error instanceof TransientError) {
108
+ // Requeue with delay for retry
109
+ await this.messageBroker.requeue(event.id, { delay: 5000 });
110
+ } else {
111
+ // Send to dead letter queue
112
+ await this.messageBroker.sendToDlq(event, error);
113
+ }
114
+ }
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### Event Schema Registry
120
+
121
+ ```typescript
122
+ // contracts/order-events.ts
123
+ export const OrderPlacedEventSchema = {
124
+ type: 'object',
125
+ required: ['orderId', 'customerId', 'total', 'items'],
126
+ properties: {
127
+ orderId: { type: 'string', format: 'uuid' },
128
+ customerId: { type: 'string', format: 'uuid' },
129
+ total: { type: 'number', minimum: 0 },
130
+ items: {
131
+ type: 'array',
132
+ items: {
133
+ type: 'object',
134
+ required: ['productId', 'quantity', 'price'],
135
+ properties: {
136
+ productId: { type: 'string' },
137
+ quantity: { type: 'number', minimum: 1 },
138
+ price: { type: 'number', minimum: 0 }
139
+ }
140
+ }
141
+ }
142
+ }
143
+ };
144
+ ```
145
+
146
+ ## Related Skills
147
+
148
+ - api-design
149
+ - backend-patterns
150
+ - cqrs
151
+ - event-sourcing
152
+ - message-queues
@@ -0,0 +1,159 @@
1
+ ---
2
+ name: frontend-pattern
3
+ description: Frontend development patterns — component composition, state management, URL-as-state, data fetching, and animation best practices for React/TypeScript web applications
4
+ origin: FlowDeck
5
+ ---
6
+
7
+ # Frontend Pattern Skill
8
+
9
+ Implements maintainable, performant frontend patterns using React and TypeScript.
10
+
11
+ ## When to Activate
12
+
13
+ Activate when:
14
+ - Building new UI components
15
+ - Setting up state management
16
+ - Implementing data fetching
17
+ - Adding animations or transitions
18
+ - Structuring a new feature module
19
+
20
+ ## Component Patterns
21
+
22
+ ### Compound Components
23
+
24
+ Use compound components when related UI shares state and interaction semantics:
25
+
26
+ ```tsx
27
+ <Tabs defaultValue="overview">
28
+ <Tabs.List>
29
+ <Tabs.Trigger value="overview">Overview</Tabs.Trigger>
30
+ <Tabs.Trigger value="settings">Settings</Tabs.Trigger>
31
+ </Tabs.List>
32
+ <Tabs.Content value="overview">...</Tabs.Content>
33
+ <Tabs.Content value="settings">...</Tabs.Content>
34
+ </Tabs>
35
+ ```
36
+
37
+ - Parent owns state via `useState` or context
38
+ - Children consume via context — no prop drilling
39
+ - Keeps keyboard handling, ARIA, and focus logic in the headless layer
40
+
41
+ ### Container / Presentational Split
42
+
43
+ ```tsx
44
+ // Container — owns data loading and side effects
45
+ function UserProfileContainer({ userId }: { userId: string }) {
46
+ const { data, isLoading } = useUser(userId);
47
+ if (isLoading) return <Skeleton />;
48
+ return <UserProfileView user={data} />;
49
+ }
50
+
51
+ // Presentational — receives props, renders UI
52
+ function UserProfileView({ user }: { user: User }) {
53
+ return (
54
+ <div>
55
+ <Avatar src={user.avatar} />
56
+ <h1>{user.name}</h1>
57
+ </div>
58
+ );
59
+ }
60
+ ```
61
+
62
+ ## State Management
63
+
64
+ | Concern | Tooling |
65
+ |---------|---------|
66
+ | Server state | TanStack Query, SWR, tRPC |
67
+ | Client state | Zustand, Jotai, signals |
68
+ | URL state | search params, route segments |
69
+ | Form state | React Hook Form or equivalent |
70
+
71
+ **Do not duplicate server state into client stores.** Derive values instead of storing redundant computed state.
72
+
73
+ ## URL As State
74
+
75
+ Persist shareable, bookmarkable state in the URL:
76
+
77
+ ```tsx
78
+ // Good: filters, sort, pagination in URL
79
+ const [searchParams, setSearchParams] = useSearchParams();
80
+ const filter = searchParams.get('filter') ?? 'all';
81
+
82
+ // Usage
83
+ <button onClick={() => setSearchParams({ filter: 'active' })}>
84
+ Active
85
+ </button>
86
+ ```
87
+
88
+ ## Data Fetching Patterns
89
+
90
+ ### Stale-While-Revalidate
91
+
92
+ Return cached data immediately, revalidate in background:
93
+
94
+ ```tsx
95
+ const { data } = useQuery({
96
+ queryKey: ['users', userId],
97
+ queryFn: () => fetchUser(userId),
98
+ staleTime: 5 * 60 * 1000, // 5 minutes
99
+ });
100
+ ```
101
+
102
+ ### Optimistic Updates
103
+
104
+ ```tsx
105
+ const mutation = useMutation({
106
+ mutationFn: updateUser,
107
+ onMutate: async (newData) => {
108
+ await queryClient.cancelQueries({ queryKey: ['user', newData.id] });
109
+ const previous = queryClient.getQueryData(['user', newData.id]);
110
+ queryClient.setQueryData(['user', newData.id], newData);
111
+ return { previous };
112
+ },
113
+ onError: (err, newData, context) => {
114
+ queryClient.setQueryData(['user', newData.id], context.previous);
115
+ },
116
+ });
117
+ ```
118
+
119
+ ## CSS Custom Properties
120
+
121
+ Define design tokens as CSS variables — do not hardcode values:
122
+
123
+ ```css
124
+ :root {
125
+ --color-surface: oklch(98% 0 0);
126
+ --color-text: oklch(18% 0 0);
127
+ --color-accent: oklch(68% 0.21 250);
128
+
129
+ --text-base: clamp(1rem, 0.92rem + 0.4vw, 1.125rem);
130
+ --space-section: clamp(4rem, 3rem + 5vw, 10rem);
131
+
132
+ --duration-fast: 150ms;
133
+ --duration-normal: 300ms;
134
+ --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
135
+ }
136
+ ```
137
+
138
+ ## Animation Guidelines
139
+
140
+ Use compositor-friendly properties only:
141
+
142
+ ```
143
+ ✅ transform, opacity, clip-path, filter
144
+ ❌ width, height, top, left, margin, padding, border, font-size
145
+ ```
146
+
147
+ ```tsx
148
+ // Good
149
+ const style = { opacity: isVisible ? 1 : 0, transform: `translateY(${isVisible ? 0 : 20}px)` };
150
+
151
+ // Bad
152
+ const style = { height: isVisible ? 'auto' : 0 };
153
+ ```
154
+
155
+ ## Related Skills
156
+
157
+ - [code-review](code-review) — Review frontend code for quality
158
+ - [security-scan](security-scan) — Check for XSS and injection vulnerabilities
159
+ - [test-coverage](test-coverage) — Ensure UI component tests exist
@@ -0,0 +1,80 @@
1
+ # hexagonal-architecture
2
+
3
+ ## When to Activate
4
+ When building applications that must remain flexible to changing external systems (databases, APIs, UI frameworks) and need to support multiple entry points (ports) for the same business logic.
5
+
6
+ ## Steps
7
+ 1. **Identify the core domain** - Isolate the pure business logic that makes no references to infrastructure.
8
+ 2. **Define inbound ports** - Create interfaces (ports) for primary/ driving actors (UI, API controllers) that trigger application logic.
9
+ 3. **Define outbound ports** - Create interfaces (ports) for secondary/ driven actors (databases, external services) that the domain calls.
10
+ 4. **Implement primary adapters** - Create adapters for inbound traffic (REST controllers, GraphQL resolvers, CLI commands).
11
+ 5. **Implement secondary adapters** - Create adapters for outbound traffic (Postgres repositories, Redis caches, email gateways).
12
+ 6. **Ensure domain has no external dependencies** - The domain layer should compile and run with no imports from adapters.
13
+ 7. **Wire via dependency injection** - Connect adapters to ports at application startup.
14
+
15
+ ## Examples
16
+ ```typescript
17
+ // Domain Core - Pure business logic, no infrastructure dependencies
18
+ class Transfer {
19
+ constructor(
20
+ public readonly fromAccountId: string,
21
+ public readonly toAccountId: string,
22
+ public readonly amount: number
23
+ ) {}
24
+
25
+ execute(accounts: Map<string, Account>): TransferResult {
26
+ const from = accounts.get(this.fromAccountId)
27
+ const to = accounts.get(this.toAccountId)
28
+
29
+ if (!from || !to) {
30
+ return TransferResult.failed('Account not found')
31
+ }
32
+
33
+ if (!from.canDebit(this.amount)) {
34
+ return TransferResult.failed('Insufficient funds')
35
+ }
36
+
37
+ from.debit(this.amount)
38
+ to.credit(this.amount)
39
+
40
+ return TransferResult.success()
41
+ }
42
+ }
43
+
44
+ // Inbound Port (Primary Port) - Interface for driving operations
45
+ interface TransferUseCase {
46
+ execute(transfer: Transfer): TransferResult
47
+ }
48
+
49
+ // Outbound Port (Secondary Port) - Interface for driven operations
50
+ interface AccountRepository {
51
+ findById(id: string): Promise<Account | null>
52
+ save(account: Account): Promise<void>
53
+ }
54
+
55
+ interface EventBus {
56
+ publish(event: DomainEvent): Promise<void>
57
+ }
58
+
59
+ // Primary Adapter - REST API
60
+ class TransferController implements TransferUseCase {
61
+ constructor(private readonly accounts: AccountRepository) {}
62
+
63
+ async execute(transfer: Transfer): Promise<TransferResult> {
64
+ const allAccounts = await this.accounts.findById(transfer.fromAccountId)
65
+ // ... handle via injected port
66
+ }
67
+ }
68
+
69
+ // Secondary Adapter - PostgreSQL implementation
70
+ class PostgresAccountRepository implements AccountRepository {
71
+ constructor(private readonly db: Database) {}
72
+ // ... implementation
73
+ }
74
+ ```
75
+
76
+ ## Related Skills
77
+ - clean-architecture
78
+ - layered-architecture
79
+ - ddd-architecture
80
+ - backend-patterns
@@ -0,0 +1,64 @@
1
+ # layered-architecture
2
+
3
+ ## When to Activate
4
+ When building traditional monolithic or client-server applications where clear vertical separation of concerns improves maintainability (e.g., MVC applications, REST APIs, data-driven apps).
5
+
6
+ ## Steps
7
+ 1. **Identify natural layers** - Determine the distinct vertical tiers based on responsibility (e.g., presentation, business logic, data access).
8
+ 2. **Define layer responsibilities** - Establish clear contracts for what each layer can and cannot depend on.
9
+ 3. **Implement top-down dependencies** - Higher layers (presentation) depend on lower layers (data), but never vice versa.
10
+ 4. **Create layer interfaces** - Use interfaces or abstract classes to define how adjacent layers communicate.
11
+ 5. **Enforce layer access rules** - Use module visibility, package private, or architectural linting tools to prevent cross-layer pollution.
12
+ 6. **Keep thin layers** - Avoid bloating any single layer; if the business logic layer grows large, consider extracting domain objects.
13
+
14
+ ## Examples
15
+ ```typescript
16
+ // Presentation Layer - Controllers/Handlers
17
+ class OrderController {
18
+ constructor(private readonly orderService: OrderService) {}
19
+
20
+ async createOrder(req: Request, res: Response): Promise<void> {
21
+ const order = await this.orderService.createOrder(req.body)
22
+ res.status(201).json(order)
23
+ }
24
+ }
25
+
26
+ // Business Logic Layer - Services
27
+ class OrderService {
28
+ constructor(
29
+ private readonly orderRepository: OrderRepository,
30
+ private readonly paymentGateway: PaymentGateway
31
+ ) {}
32
+
33
+ async createOrder(data: CreateOrderDto): Promise<Order> {
34
+ const order = new Order(data.items)
35
+
36
+ if (data.paymentMethod === 'prepaid') {
37
+ await this.paymentGateway.charge(order.total, data.paymentToken)
38
+ }
39
+
40
+ return this.orderRepository.save(order)
41
+ }
42
+ }
43
+
44
+ // Data Access Layer - Repositories
45
+ interface OrderRepository {
46
+ save(order: Order): Promise<void>
47
+ findById(id: string): Promise<Order | null>
48
+ findByCustomer(customerId: string): Promise<Order[]>
49
+ }
50
+
51
+ class PostgresOrderRepository implements OrderRepository {
52
+ constructor(private readonly db: Database) {}
53
+
54
+ async save(order: Order): Promise<void> {
55
+ await this.db.query('INSERT INTO orders (...) VALUES (...)', order.toDbFormat())
56
+ }
57
+ }
58
+ ```
59
+
60
+ ## Related Skills
61
+ - clean-architecture
62
+ - hexagonal-architecture
63
+ - ddd-architecture
64
+ - backend-patterns
@@ -0,0 +1,74 @@
1
+ # postgres-patterns
2
+
3
+ ## When to Activate
4
+ When designing database schemas, writing complex queries, or optimizing database performance. Use before creating migrations or writing SQL queries.
5
+
6
+ ## Steps
7
+ 1. **Design indexes strategically** - Create individual btree indexes on each column for multi-column searches (allows BitmapAnd)
8
+ 2. **Use EXPLAIN ANALYZE** - Always verify query plans before and after optimization
9
+ 3. **Choose correct index type** - B-tree for equality/range, Bloom for multi-column filters with high selectivity
10
+ 4. **Avoid multi-column btree on non-leading columns** - Queries on non-first columns of multi-column btree indexes will do sequential scans
11
+ 5. **Use parameterized queries** - Let the planner cache and reuse query plans
12
+ 6. **Run ANALYZE regularly** - Keep statistics fresh for optimal planner decisions
13
+
14
+ ## Examples
15
+
16
+ ```sql
17
+ -- AVOID: Multi-column btree for non-leading column queries
18
+ CREATE INDEX btreeidx ON tbloom (i1, i2, i3, i4, i5, i6);
19
+ -- Query on i2 and i5 will do sequential scan, not use the index
20
+
21
+ -- PREFER: Individual btree indexes for multi-column searches
22
+ CREATE INDEX btreeidx1 ON tbloom (i1);
23
+ CREATE INDEX btreeidx2 ON tbloom (i2);
24
+ CREATE INDEX btreeidx3 ON tbloom (i3);
25
+ CREATE INDEX btreeidx4 ON tbloom (i4);
26
+ CREATE INDEX btreeidx5 ON tbloom (i5);
27
+ CREATE INDEX btreeidx6 ON tbloom (i6);
28
+ -- Bitmap Index Scan with BitmapAnd is used for multi-column queries
29
+ ```
30
+
31
+ ```sql
32
+ -- Bloom Index for multi-column filtering (good for low selectivity columns)
33
+ CREATE INDEX bloomidx ON tbloom USING bloom (i1, i2, i3, i4, i5, i6);
34
+ -- More efficient than btree for queries filtering on many columns
35
+ -- Smaller index size, faster Bitmap Index Scans
36
+
37
+ -- Always verify with EXPLAIN ANALYZE
38
+ EXPLAIN ANALYZE SELECT * FROM tbloom WHERE i2 = 898732 AND i5 = 123451;
39
+ ```
40
+
41
+ ```sql
42
+ -- Query Planner Configuration (temporary fix only)
43
+ SET enable_hashjoin = off; -- Force nested-loop or merge join
44
+ SET enable_seqscan = off; -- Prefer index scans
45
+ SET random_page_cost = 1.1; -- Make index scans cheaper (SSD)
46
+ SET effective_cache_size = '8GB'; -- Help planner estimate
47
+
48
+ -- Better approaches:
49
+ -- 1. Run ANALYZE to update statistics
50
+ ANALYZE;
51
+ -- 2. Increase statistics for specific columns
52
+ ALTER TABLE orders SET STATISTICS = 500;
53
+ ANALYZE orders;
54
+ -- 3. Adjust planner cost constants (postgresql.conf)
55
+ ```
56
+
57
+ ```sql
58
+ -- Repository Pattern in SQL
59
+ -- Define standard operations
60
+ interface OrderRepository {
61
+ findAll(filter: OrderFilter, pagination: Pagination): Promise<Order[]>;
62
+ findById(id: string): Promise<Order | null>;
63
+ create(order: CreateOrderDTO): Promise<Order>;
64
+ update(id: string, attributes: UpdateOrderDTO): Promise<Order>;
65
+ delete(id: string): Promise<void>;
66
+ count(filter?: OrderFilter): Promise<number>;
67
+ }
68
+ ```
69
+
70
+ ## Related Skills
71
+ - api-design
72
+ - backend-patterns
73
+ - database-migrations
74
+ - postgres-performance
@@ -527,3 +527,8 @@ def get_thing():
527
527
 
528
528
  # ✅ Or restructure: extract shared types into a third module
529
529
  ```
530
+
531
+ ## Related Skills
532
+ - backend-patterns
533
+ - django-patterns
534
+ - python-testing
@@ -0,0 +1,113 @@
1
+ # saga-architecture
2
+
3
+ ## When to Activate
4
+ When coordinating distributed operations across multiple services or data stores where ACID transactions are not available and compensating actions are needed to maintain eventual consistency.
5
+
6
+ ## Steps
7
+ 1. **Identify the saga participants** - Determine which services or components participate in the distributed operation.
8
+ 2. **Define the saga choreography or orchestration** - Choose whether sagas will be choreographed (event-driven) or orchestrated (central coordinator).
9
+ 3. **Define each step with corresponding compensation** - For every forward action, define what compensating action undoes it.
10
+ 4. **Implement idempotent operations** - Ensure each step can be safely retried and compensation can be safely reapplied.
11
+ 5. **Handle saga failures with compensation** - On failure, execute compensations in reverse order (for orchestrating sagas) or react to failure events (for choreographing sagas).
12
+ 6. **Persist saga state** - Store saga state to survive process crashes and enable recovery.
13
+ 7. **Add timeout and retry logic** - Detect stuck sagas and advance or compensate accordingly.
14
+
15
+ ## Examples
16
+ ```typescript
17
+ // Saga State
18
+ interface SagaState<T> {
19
+ id: string
20
+ currentStep: number
21
+ data: T
22
+ status: 'pending' | 'in_progress' | 'completed' | 'compensating' | 'failed'
23
+ }
24
+
25
+ // Orchestrating Saga - Central coordinator manages steps
26
+ class OrderProcessingSaga {
27
+ private readonly steps: SagaStep[]
28
+
29
+ constructor(
30
+ private readonly sagaOrchestrator: SagaOrchestrator,
31
+ private readonly inventoryService: InventoryService,
32
+ private readonly paymentService: PaymentService,
33
+ private readonly shippingService: ShippingService
34
+ ) {
35
+ this.steps = [
36
+ {
37
+ name: 'reserve_inventory',
38
+ execute: (state) => this.inventoryService.reserve(state.orderId, state.items),
39
+ compensate: (state) => this.inventoryService.release(state.orderId, state.items)
40
+ },
41
+ {
42
+ name: 'process_payment',
43
+ execute: (state) => this.paymentService.charge(state.orderId, state.total),
44
+ compensate: (state) => this.paymentService.refund(state.orderId, state.total)
45
+ },
46
+ {
47
+ name: 'initiate_shipping',
48
+ execute: (state) => this.shippingService.createShipment(state.orderId),
49
+ compensate: (state) => this.shippingService.cancelShipment(state.shipmentId)
50
+ }
51
+ ]
52
+ }
53
+
54
+ async execute(orderId: string): Promise<void> {
55
+ const state: SagaState<OrderSagaData> = {
56
+ id: generateId(),
57
+ currentStep: 0,
58
+ data: { orderId, items: [], total: 0 },
59
+ status: 'in_progress'
60
+ }
61
+
62
+ await this.sagaOrchestrator.start(state, this.steps)
63
+ }
64
+ }
65
+
66
+ // Choreography-based Saga - Events trigger reactions
67
+ class OrderCreatedHandler {
68
+ constructor(private readonly eventBus: EventBus) {}
69
+
70
+ async handle(event: OrderCreatedEvent): Promise<void> {
71
+ // Step 1: Reserve inventory
72
+ try {
73
+ await this.inventoryService.reserve(event.orderId, event.items)
74
+ this.eventBus.publish(new InventoryReservedEvent(event.orderId))
75
+ } catch (error) {
76
+ this.eventBus.publish(new InventoryReservationFailedEvent(event.orderId, error.message))
77
+ }
78
+ }
79
+ }
80
+
81
+ class InventoryReservedHandler {
82
+ async handle(event: InventoryReservedEvent): Promise<void> {
83
+ // Step 2: Process payment
84
+ try {
85
+ await this.paymentService.charge(event.orderId, event.total)
86
+ this.eventBus.publish(new PaymentProcessedEvent(event.orderId))
87
+ } catch (error) {
88
+ // Compensate by releasing inventory
89
+ this.eventBus.publish(new InventoryReleaseRequestedEvent(event.orderId))
90
+ }
91
+ }
92
+ }
93
+
94
+ // Idempotent Step Implementation
95
+ class PaymentService {
96
+ async charge(orderId: string, amount: Money): Promise<TransactionId> {
97
+ const existingTx = await this.transactionRepo.findByOrderId(orderId)
98
+ if (existingTx) {
99
+ return existingTx.id // Idempotent: return existing instead of charging again
100
+ }
101
+
102
+ const transaction = await this.paymentGateway.charge(amount)
103
+ await this.transactionRepo.save({ orderId, transaction })
104
+ return transaction.id
105
+ }
106
+ }
107
+ ```
108
+
109
+ ## Related Skills
110
+ - clean-architecture
111
+ - hexagonal-architecture
112
+ - ddd-architecture
113
+ - backend-patterns
@@ -1,4 +0,0 @@
1
- import { type ToolDefinition } from "@opencode-ai/plugin";
2
- import type { OpencodeClient } from "@opencode-ai/sdk";
3
- export declare function createRunParallelTool(client: OpencodeClient): ToolDefinition;
4
- //# sourceMappingURL=run-parallel.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"run-parallel.d.ts","sourceRoot":"","sources":["../../src/tools/run-parallel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,KAAK,cAAc,EAAE,MAAM,qBAAqB,CAAA;AAC/D,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AAsBtD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,cAAc,CAiK5E"}