wogiflow 2.4.2 → 2.4.4

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 (210) hide show
  1. package/.claude/commands/wogi-start.md +124 -0
  2. package/.claude/docs/claude-code-compatibility.md +51 -0
  3. package/.claude/docs/explore-agents.md +11 -0
  4. package/.claude/settings.json +12 -1
  5. package/.workflow/models/registry.json +1 -1
  6. package/bin/flow +11 -1
  7. package/lib/workspace-contracts.js +599 -0
  8. package/lib/workspace-intelligence.js +600 -0
  9. package/lib/workspace-messages.js +441 -0
  10. package/lib/workspace-routing.js +485 -0
  11. package/lib/workspace-sync.js +339 -0
  12. package/lib/workspace.js +1073 -0
  13. package/package.json +4 -4
  14. package/scripts/MEMORY-ARCHITECTURE.md +1 -1
  15. package/scripts/base-workflow-step.js +136 -0
  16. package/scripts/flow-adaptive-learning.js +8 -9
  17. package/scripts/flow-aggregate.js +11 -6
  18. package/scripts/flow-api-index.js +4 -6
  19. package/scripts/flow-assumption-detector.js +0 -2
  20. package/scripts/flow-audit.js +15 -2
  21. package/scripts/flow-auto-context.js +8 -12
  22. package/scripts/flow-auto-learn.js +49 -49
  23. package/scripts/flow-background.js +5 -6
  24. package/scripts/flow-bridge-state.js +8 -10
  25. package/scripts/flow-bulk-loop.js +1 -3
  26. package/scripts/flow-bulk-orchestrator.js +1 -3
  27. package/scripts/flow-cascade-completion.js +0 -2
  28. package/scripts/flow-cascade.js +4 -4
  29. package/scripts/flow-checkpoint.js +10 -13
  30. package/scripts/flow-code-intelligence.js +10 -12
  31. package/scripts/flow-community-sync.js +4 -4
  32. package/scripts/flow-community.js +12 -20
  33. package/scripts/flow-config-defaults.js +28 -2
  34. package/scripts/flow-config-interactive.js +9 -5
  35. package/scripts/flow-config-loader.js +49 -92
  36. package/scripts/flow-config-substitution.js +0 -2
  37. package/scripts/flow-context-estimator.js +4 -4
  38. package/scripts/flow-context-init.js +10 -12
  39. package/scripts/flow-context-manager.js +0 -2
  40. package/scripts/flow-context-scoring.js +2 -2
  41. package/scripts/flow-contract-scan.js +6 -9
  42. package/scripts/flow-correct.js +29 -27
  43. package/scripts/flow-correction-detector.js +5 -1
  44. package/scripts/flow-damage-control.js +47 -54
  45. package/scripts/flow-decisions-merge.js +4 -14
  46. package/scripts/flow-diff.js +5 -8
  47. package/scripts/flow-done-gates.js +786 -0
  48. package/scripts/flow-done-report.js +123 -0
  49. package/scripts/flow-done.js +71 -717
  50. package/scripts/flow-entropy-monitor.js +1 -3
  51. package/scripts/flow-eval-calibration.js +257 -0
  52. package/scripts/flow-eval-judge.js +10 -1
  53. package/scripts/flow-eval.js +14 -5
  54. package/scripts/flow-extraction-review.js +1 -0
  55. package/scripts/flow-failure-categories.js +0 -2
  56. package/scripts/flow-figma-confirm.js +5 -9
  57. package/scripts/flow-figma-generate.js +8 -10
  58. package/scripts/flow-figma-index.js +8 -10
  59. package/scripts/flow-figma-match.js +3 -5
  60. package/scripts/flow-figma-mcp-server.js +2 -4
  61. package/scripts/flow-figma-orchestrator.js +2 -3
  62. package/scripts/flow-figma-registry.js +2 -3
  63. package/scripts/flow-framework-resolver.js +0 -2
  64. package/scripts/flow-function-index.js +4 -6
  65. package/scripts/flow-gate-confidence.js +2 -2
  66. package/scripts/flow-gitignore.js +0 -2
  67. package/scripts/flow-guided-edit.js +5 -6
  68. package/scripts/flow-health.js +5 -6
  69. package/scripts/flow-hook-errors.js +6 -0
  70. package/scripts/flow-hook-status.js +263 -0
  71. package/scripts/flow-hooks.js +17 -29
  72. package/scripts/flow-http-client.js +9 -8
  73. package/scripts/flow-hybrid-interactive.js +7 -12
  74. package/scripts/flow-hybrid-test.js +12 -13
  75. package/scripts/flow-instruction-richness.js +1 -1
  76. package/scripts/flow-io.js +21 -4
  77. package/scripts/flow-knowledge-router.js +9 -3
  78. package/scripts/flow-learning-orchestrator.js +318 -13
  79. package/scripts/flow-links.js +5 -7
  80. package/scripts/flow-long-input-association.js +275 -0
  81. package/scripts/flow-long-input-chunking.js +1 -0
  82. package/scripts/flow-long-input-cli.js +0 -2
  83. package/scripts/flow-long-input-complexity.js +0 -2
  84. package/scripts/flow-long-input-constants.js +0 -2
  85. package/scripts/flow-long-input-contradictions.js +351 -0
  86. package/scripts/flow-long-input-detection.js +0 -2
  87. package/scripts/flow-long-input-passes.js +885 -0
  88. package/scripts/flow-long-input-stories.js +1 -1
  89. package/scripts/flow-long-input-voice.js +0 -2
  90. package/scripts/flow-long-input.js +425 -3005
  91. package/scripts/flow-loop-retry-learning.js +2 -3
  92. package/scripts/flow-lsp.js +3 -3
  93. package/scripts/flow-mcp-docs.js +3 -4
  94. package/scripts/flow-memory-db.js +6 -8
  95. package/scripts/flow-memory-sync.js +18 -11
  96. package/scripts/flow-metrics.js +1 -2
  97. package/scripts/flow-model-adapter.js +2 -3
  98. package/scripts/flow-model-config.js +72 -104
  99. package/scripts/flow-model-router.js +2 -2
  100. package/scripts/flow-model-types.js +0 -2
  101. package/scripts/flow-multi-approach.js +5 -6
  102. package/scripts/flow-orchestrate-context.js +3 -7
  103. package/scripts/flow-orchestrate-rollback.js +3 -8
  104. package/scripts/flow-orchestrate-state.js +8 -14
  105. package/scripts/flow-orchestrate-templates.js +2 -6
  106. package/scripts/flow-orchestrate-validator.js +5 -9
  107. package/scripts/flow-orchestrate.js +126 -103
  108. package/scripts/flow-output.js +0 -2
  109. package/scripts/flow-parallel.js +1 -1
  110. package/scripts/flow-paths.js +23 -2
  111. package/scripts/flow-pattern-enforcer.js +30 -28
  112. package/scripts/flow-pattern-extractor.js +3 -4
  113. package/scripts/flow-pending.js +0 -2
  114. package/scripts/flow-permissions.js +2 -3
  115. package/scripts/flow-plugin-registry.js +10 -12
  116. package/scripts/flow-prd-manager.js +1 -1
  117. package/scripts/flow-progress.js +7 -9
  118. package/scripts/flow-prompt-composer.js +3 -3
  119. package/scripts/flow-prompt-template.js +2 -2
  120. package/scripts/flow-providers.js +7 -4
  121. package/scripts/flow-registry-manager.js +7 -12
  122. package/scripts/flow-regression.js +9 -11
  123. package/scripts/flow-roadmap.js +2 -2
  124. package/scripts/flow-run-trace.js +16 -15
  125. package/scripts/flow-safety.js +2 -5
  126. package/scripts/flow-scanner-base.js +5 -7
  127. package/scripts/flow-scenario-engine.js +1 -5
  128. package/scripts/flow-security.js +29 -0
  129. package/scripts/flow-session-end.js +32 -41
  130. package/scripts/flow-session-learning.js +53 -49
  131. package/scripts/flow-setup-hooks.js +2 -3
  132. package/scripts/flow-skill-create.js +7 -12
  133. package/scripts/flow-skill-generator.js +12 -16
  134. package/scripts/flow-skill-learn.js +17 -8
  135. package/scripts/flow-skill-matcher.js +1 -2
  136. package/scripts/flow-spec-generator.js +2 -4
  137. package/scripts/flow-stack-wizard.js +5 -7
  138. package/scripts/flow-standards-learner.js +35 -16
  139. package/scripts/flow-start.js +2 -0
  140. package/scripts/flow-stats-collector.js +2 -2
  141. package/scripts/flow-status.js +10 -10
  142. package/scripts/flow-statusline-setup.js +2 -2
  143. package/scripts/flow-step-changelog.js +2 -3
  144. package/scripts/flow-step-comments.js +66 -81
  145. package/scripts/flow-step-complexity.js +50 -70
  146. package/scripts/flow-step-coverage.js +3 -5
  147. package/scripts/flow-step-knowledge.js +2 -3
  148. package/scripts/flow-step-pr-tests.js +64 -74
  149. package/scripts/flow-step-regression.js +3 -5
  150. package/scripts/flow-step-review.js +86 -103
  151. package/scripts/flow-step-security.js +111 -121
  152. package/scripts/flow-step-silent-failures.js +56 -83
  153. package/scripts/flow-step-simplifier.js +52 -70
  154. package/scripts/flow-story.js +4 -7
  155. package/scripts/flow-strict-adherence.js +3 -4
  156. package/scripts/flow-task-checkpoint.js +36 -5
  157. package/scripts/flow-task-enforcer.js +2 -24
  158. package/scripts/flow-tech-debt.js +1 -1
  159. package/scripts/flow-template-extractor.js +1 -0
  160. package/scripts/flow-templates.js +11 -13
  161. package/scripts/flow-test-api.js +9 -13
  162. package/scripts/flow-test-discovery.js +1 -1
  163. package/scripts/flow-test-generate.js +5 -9
  164. package/scripts/flow-test-integrity.js +3 -7
  165. package/scripts/flow-test-ui.js +5 -9
  166. package/scripts/flow-testing-deps.js +1 -3
  167. package/scripts/flow-tiered-learning.js +4 -4
  168. package/scripts/flow-todowrite-sync.js +1 -1
  169. package/scripts/flow-tokens.js +0 -2
  170. package/scripts/flow-verification-profile.js +6 -10
  171. package/scripts/flow-verify.js +12 -16
  172. package/scripts/flow-version-check.js +4 -12
  173. package/scripts/flow-webmcp-generator.js +3 -5
  174. package/scripts/flow-workflow-steps.js +0 -2
  175. package/scripts/flow-workflow.js +9 -11
  176. package/scripts/hooks/adapters/claude-code.js +31 -0
  177. package/scripts/hooks/core/config-change.js +1 -0
  178. package/scripts/hooks/core/extension-registry.js +0 -2
  179. package/scripts/hooks/core/instructions-loaded.js +1 -1
  180. package/scripts/hooks/core/observation-capture.js +5 -5
  181. package/scripts/hooks/core/phase-gate.js +5 -0
  182. package/scripts/hooks/core/post-compact.js +1 -12
  183. package/scripts/hooks/core/research-gate.js +2 -12
  184. package/scripts/hooks/core/routing-gate.js +6 -0
  185. package/scripts/hooks/core/task-completed.js +12 -0
  186. package/scripts/hooks/core/task-created.js +83 -0
  187. package/scripts/hooks/core/worktree-lifecycle.js +1 -1
  188. package/scripts/hooks/entry/claude-code/config-change.js +6 -29
  189. package/scripts/hooks/entry/claude-code/instructions-loaded.js +5 -30
  190. package/scripts/hooks/entry/claude-code/post-compact.js +4 -31
  191. package/scripts/hooks/entry/claude-code/post-tool-use.js +121 -172
  192. package/scripts/hooks/entry/claude-code/pre-tool-use.js +260 -361
  193. package/scripts/hooks/entry/claude-code/session-end.js +4 -28
  194. package/scripts/hooks/entry/claude-code/session-start.js +205 -243
  195. package/scripts/hooks/entry/claude-code/setup.js +8 -49
  196. package/scripts/hooks/entry/claude-code/stop.js +40 -72
  197. package/scripts/hooks/entry/claude-code/task-completed.js +4 -28
  198. package/scripts/hooks/entry/claude-code/task-created.js +15 -0
  199. package/scripts/hooks/entry/claude-code/user-prompt-submit.js +113 -195
  200. package/scripts/hooks/entry/claude-code/worktree-create.js +6 -25
  201. package/scripts/hooks/entry/claude-code/worktree-remove.js +6 -25
  202. package/scripts/hooks/entry/shared/hook-runner.js +99 -0
  203. package/scripts/hooks/entry/shared/read-stdin.js +0 -2
  204. package/scripts/postinstall.js +2 -0
  205. package/scripts/registries/api-registry.js +0 -2
  206. package/scripts/registries/component-registry.js +5 -9
  207. package/scripts/registries/contract-scanner.js +2 -9
  208. package/scripts/registries/function-registry.js +0 -2
  209. package/scripts/registries/schema-registry.js +14 -18
  210. package/scripts/registries/service-registry.js +23 -27
@@ -0,0 +1,485 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Wogi Workspace — Task Routing & Sub-Agent Delegation
5
+ *
6
+ * Story 3 (wf-824638e4): The manager agent's brain — analyzes tasks,
7
+ * determines which repo(s) to target, determines ordering,
8
+ * and generates sub-agent delegation instructions.
9
+ */
10
+
11
+ const fs = require('node:fs');
12
+ const path = require('node:path');
13
+ const crypto = require('node:crypto');
14
+
15
+ // ============================================================
16
+ // Routing Keywords (Criterion 1)
17
+ // ============================================================
18
+
19
+ const ROLE_KEYWORDS = {
20
+ consumer: [
21
+ 'page', 'component', 'ui', 'style', 'css', 'layout', 'form', 'button',
22
+ 'modal', 'screen', 'view', 'frontend', 'client', 'browser', 'render',
23
+ 'hook', 'state', 'redux', 'zustand', 'store', 'theme', 'responsive',
24
+ 'animation', 'toast', 'notification-ui', 'sidebar', 'navbar', 'header'
25
+ ],
26
+ provider: [
27
+ 'endpoint', 'route', 'controller', 'model', 'database', 'migration',
28
+ 'schema', 'backend', 'server', 'api', 'query', 'mutation', 'resolver',
29
+ 'middleware', 'auth', 'jwt', 'session', 'seed', 'fixture', 'orm',
30
+ 'sql', 'table', 'index', 'relation', 'service-layer'
31
+ ],
32
+ library: [
33
+ 'shared', 'utility', 'types', 'common', 'helper', 'constant', 'enum',
34
+ 'interface', 'typedef', 'lib', 'package', 'module'
35
+ ],
36
+ crossRepo: [
37
+ 'api', 'contract', 'schema', 'integration', 'full-stack', 'end-to-end',
38
+ 'e2e', 'both', 'cross', 'sync', 'together'
39
+ ]
40
+ };
41
+
42
+ // ============================================================
43
+ // Route Analysis (Criterion 1)
44
+ // ============================================================
45
+
46
+ /**
47
+ * Analyze a task description and determine which repo(s) should handle it.
48
+ *
49
+ * @param {string} taskDescription — the user's task description
50
+ * @param {Object} manifest — workspace-manifest.json content
51
+ * @returns {Object} routing decision
52
+ */
53
+ function analyzeTaskRouting(taskDescription, manifest) {
54
+ if (!manifest?.members) {
55
+ return {
56
+ type: 'single-repo',
57
+ target: null,
58
+ scores: {},
59
+ reason: 'No members in manifest'
60
+ };
61
+ }
62
+
63
+ const desc = taskDescription.toLowerCase();
64
+ const scores = {}; // memberName → score
65
+ const crossRepoScore = scoreKeywords(desc, ROLE_KEYWORDS.crossRepo);
66
+
67
+ for (const [name, member] of Object.entries(manifest.members)) {
68
+ let score = 0;
69
+
70
+ // Score based on role keywords
71
+ const roleKeywords = ROLE_KEYWORDS[member.role] || [];
72
+ score += scoreKeywords(desc, roleKeywords);
73
+
74
+ // Score based on member name appearing in description
75
+ if (desc.includes(name.toLowerCase())) score += 3;
76
+
77
+ // Score based on specific endpoint mentions
78
+ for (const ep of (member.provides || [])) {
79
+ const epPath = ep.split(' ').slice(1).join(' ').toLowerCase();
80
+ if (desc.includes(epPath)) score += 2;
81
+ }
82
+ for (const ep of (member.consumes || [])) {
83
+ const epPath = ep.split(' ').slice(1).join(' ').toLowerCase();
84
+ if (desc.includes(epPath)) score += 2;
85
+ }
86
+
87
+ scores[name] = score;
88
+ }
89
+
90
+ // Determine routing
91
+ const sortedMembers = Object.entries(scores)
92
+ .sort((a, b) => b[1] - a[1]);
93
+
94
+ const topScore = sortedMembers[0]?.[1] || 0;
95
+ const secondScore = sortedMembers[1]?.[1] || 0;
96
+
97
+ // Cross-repo if:
98
+ // 1. Cross-repo keywords are dominant
99
+ // 2. Two repos score similarly (within 30%)
100
+ // 3. No repo scored above threshold
101
+ const isCrossRepo =
102
+ crossRepoScore >= 2 ||
103
+ (topScore > 0 && secondScore > 0 && secondScore >= topScore * 0.7) ||
104
+ topScore === 0;
105
+
106
+ if (isCrossRepo && Object.keys(manifest.members).length >= 2) {
107
+ return {
108
+ type: 'cross-repo',
109
+ targets: sortedMembers.filter(([_, s]) => s > 0).map(([name]) => name),
110
+ allMembers: Object.keys(manifest.members),
111
+ scores,
112
+ crossRepoScore,
113
+ reason: crossRepoScore >= 2
114
+ ? 'Cross-repo keywords detected'
115
+ : topScore === 0
116
+ ? 'No clear repo match — routing to all'
117
+ : 'Multiple repos score similarly'
118
+ };
119
+ }
120
+
121
+ return {
122
+ type: 'single-repo',
123
+ target: sortedMembers[0]?.[0] || Object.keys(manifest.members)[0],
124
+ scores,
125
+ reason: `Best match: ${sortedMembers[0]?.[0]} (score: ${topScore})`
126
+ };
127
+ }
128
+
129
+ function scoreKeywords(text, keywords) {
130
+ let score = 0;
131
+ for (const kw of keywords) {
132
+ const escaped = kw.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
133
+ if (new RegExp('\\b' + escaped + '\\b', 'i').test(text)) score++;
134
+ }
135
+ return score;
136
+ }
137
+
138
+ // ============================================================
139
+ // Single-Repo Delegation (Criterion 2)
140
+ // ============================================================
141
+
142
+ /**
143
+ * Generate a sub-agent delegation prompt for a single repo.
144
+ *
145
+ * @param {string} workspaceRoot
146
+ * @param {string} repoName
147
+ * @param {string} task — task description
148
+ * @param {Object} manifest
149
+ * @returns {Object} delegation instruction
150
+ */
151
+ function generateSingleRepoDelegation(workspaceRoot, repoName, task, manifest) {
152
+ const member = manifest.members[repoName];
153
+ if (!member) throw new Error(`Unknown repo: ${repoName}`);
154
+
155
+ const memberPath = member.path || `./${repoName}`;
156
+ const repoPath = path.resolve(workspaceRoot, memberPath);
157
+ const decisionsPath = path.join(repoPath, '.workflow', 'state', 'decisions.md');
158
+
159
+ // Read repo's decisions for context injection
160
+ let decisions = '';
161
+ try {
162
+ if (fs.existsSync(decisionsPath)) {
163
+ decisions = fs.readFileSync(decisionsPath, 'utf-8').slice(0, 3000);
164
+ }
165
+ } catch (_err) {
166
+ // Non-critical
167
+ }
168
+
169
+ // Get unread messages for this repo
170
+ let messages = [];
171
+ try {
172
+ const { getUnreadMessages } = require('./workspace-messages');
173
+ messages = getUnreadMessages(workspaceRoot, repoName);
174
+ } catch (_err) {
175
+ // Non-critical
176
+ }
177
+
178
+ const messageContext = messages.length > 0
179
+ ? `\n\nUnread messages for your repo:\n${messages.map(m => `- [${m.type}] from ${m.from}: ${m.subject}`).join('\n')}`
180
+ : '';
181
+
182
+ // Read relevant contracts
183
+ let contractContext = '';
184
+ try {
185
+ const contractsDir = path.join(workspaceRoot, '.workspace', 'contracts');
186
+ if (fs.existsSync(contractsDir)) {
187
+ const files = fs.readdirSync(contractsDir).filter(f => f.endsWith('.json') || f.endsWith('.yaml'));
188
+ if (files.length > 0) {
189
+ contractContext = '\n\nShared contracts available in .workspace/contracts/: ' + files.join(', ');
190
+ }
191
+ }
192
+ } catch (_err) {
193
+ // Non-critical
194
+ }
195
+
196
+ return {
197
+ repoName,
198
+ repoPath: member.path,
199
+ prompt: `You are working in the ${repoName} repo (${member.stack?.language || 'unknown'}/${member.stack?.framework || 'unknown'}).
200
+
201
+ Task: ${task}
202
+
203
+ Your repo's role: ${member.role}
204
+ ${decisions ? `\nProject rules (from decisions.md):\n${decisions}` : ''}${messageContext}${contractContext}
205
+
206
+ After completing the task:
207
+ 1. Commit your changes
208
+ 2. If your changes affect the API contract (endpoints, request/response shapes), write a message to .workspace/messages/ notifying other repos`,
209
+
210
+ agentConfig: {
211
+ description: `${repoName}: ${task.substring(0, 50)}...`,
212
+ // The sub-agent should work within the repo directory
213
+ // The orchestrator (workspace manager) will read results after completion
214
+ }
215
+ };
216
+ }
217
+
218
+ // ============================================================
219
+ // Cross-Repo Delegation (Criterion 3)
220
+ // ============================================================
221
+
222
+ /**
223
+ * Generate a cross-repo execution plan.
224
+ * Order: contract update → provider(s) → consumer(s) → integration verify
225
+ *
226
+ * @param {string} workspaceRoot
227
+ * @param {string} task
228
+ * @param {Object} manifest
229
+ * @param {Object} routing — from analyzeTaskRouting()
230
+ * @returns {Object} execution plan with ordered steps
231
+ */
232
+ function generateCrossRepoPlan(workspaceRoot, task, manifest, routing) {
233
+ const steps = [];
234
+ const members = manifest.members;
235
+
236
+ // Step 1: Contract update (if API changes are involved)
237
+ const apiKeywords = ['endpoint', 'api', 'route', 'schema', 'contract'];
238
+ const needsContractUpdate = apiKeywords.some(kw => task.toLowerCase().includes(kw));
239
+
240
+ if (needsContractUpdate) {
241
+ steps.push({
242
+ phase: 'contract',
243
+ action: 'Update shared contract in .workspace/contracts/',
244
+ executor: 'manager',
245
+ description: 'Define the API contract before implementation'
246
+ });
247
+ }
248
+
249
+ // Step 2: Provider repos first (they create the API)
250
+ const providers = Object.entries(members)
251
+ .filter(([_, m]) => m.role === 'provider' || m.role === 'both')
252
+ .filter(([name]) => routing.targets?.includes(name) || routing.allMembers?.includes(name));
253
+
254
+ for (const [name] of providers) {
255
+ steps.push({
256
+ phase: 'provider',
257
+ action: `Implement provider-side changes in ${name}/`,
258
+ executor: name,
259
+ description: `${name} implements the API/backend side`,
260
+ delegation: generateSingleRepoDelegation(workspaceRoot, name, task, manifest)
261
+ });
262
+ }
263
+
264
+ // Step 3: Consumer repos (they use the API)
265
+ const consumers = Object.entries(members)
266
+ .filter(([_, m]) => m.role === 'consumer' || m.role === 'both')
267
+ .filter(([name]) => routing.targets?.includes(name) || routing.allMembers?.includes(name))
268
+ .filter(([name]) => !providers.some(([pName]) => pName === name)); // Skip if already in providers
269
+
270
+ for (const [name] of consumers) {
271
+ steps.push({
272
+ phase: 'consumer',
273
+ action: `Implement consumer-side changes in ${name}/`,
274
+ executor: name,
275
+ description: `${name} implements the frontend/client side`,
276
+ delegation: generateSingleRepoDelegation(workspaceRoot, name, task, manifest)
277
+ });
278
+ }
279
+
280
+ // Step 4: Library repos (if affected)
281
+ const libraries = Object.entries(members)
282
+ .filter(([_, m]) => m.role === 'library')
283
+ .filter(([name]) => routing.targets?.includes(name));
284
+
285
+ for (const [name] of libraries) {
286
+ // Libraries go first (before providers and consumers)
287
+ steps.unshift({
288
+ phase: 'library',
289
+ action: `Update shared library ${name}/`,
290
+ executor: name,
291
+ description: `${name} updates shared types/utilities`,
292
+ delegation: generateSingleRepoDelegation(workspaceRoot, name, task, manifest)
293
+ });
294
+ }
295
+
296
+ // Step 5: Integration verification
297
+ steps.push({
298
+ phase: 'verify',
299
+ action: 'Verify cross-repo integration',
300
+ executor: 'manager',
301
+ description: 'Check that provider and consumer sides work together'
302
+ });
303
+
304
+ return {
305
+ task,
306
+ type: 'cross-repo',
307
+ totalSteps: steps.length,
308
+ executionOrder: steps,
309
+ providers: providers.map(([n]) => n),
310
+ consumers: consumers.map(([n]) => n),
311
+ libraries: libraries.map(([n]) => n)
312
+ };
313
+ }
314
+
315
+ // ============================================================
316
+ // Parallel Investigation (Criterion 4)
317
+ // ============================================================
318
+
319
+ /**
320
+ * Generate parallel investigation instructions for bug reports.
321
+ * Spawns one investigator per potentially affected repo.
322
+ *
323
+ * @param {string} workspaceRoot
324
+ * @param {string} bugDescription
325
+ * @param {Object} manifest
326
+ * @returns {Object} investigation plan
327
+ */
328
+ function generateParallelInvestigation(workspaceRoot, bugDescription, manifest) {
329
+ const investigators = [];
330
+
331
+ for (const [name, member] of Object.entries(manifest.members)) {
332
+ const repoPath = path.resolve(workspaceRoot, member.path);
333
+
334
+ investigators.push({
335
+ repoName: name,
336
+ repoPath: member.path,
337
+ role: member.role,
338
+ prompt: `You are investigating a bug in the ${name} repo (${member.role}).
339
+
340
+ Bug report: ${bugDescription}
341
+
342
+ Your job:
343
+ 1. Check if the issue originates from YOUR repo
344
+ 2. Check recent changes (git log) that might have caused this
345
+ 3. Check relevant API endpoints, components, or services
346
+ 4. Report your findings clearly:
347
+ - Is the issue on YOUR side? (yes/no/maybe)
348
+ - What did you find?
349
+ - If yes: what's the fix?
350
+ - If no: what should the OTHER repo(s) check?
351
+
352
+ Be specific about file names, line numbers, and error messages.`,
353
+ agentConfig: {
354
+ description: `Investigate: ${name} — ${bugDescription.substring(0, 40)}...`,
355
+ model: 'sonnet' // Use cheaper model for investigation
356
+ }
357
+ });
358
+ }
359
+
360
+ return {
361
+ type: 'parallel-investigation',
362
+ bugDescription,
363
+ investigators,
364
+ synthesisPrompt: `Multiple investigators checked their repos. Synthesize their findings:
365
+ - Which repo is the root cause?
366
+ - What's the fix?
367
+ - Are there improvements needed in other repos?
368
+ - Create tasks in the appropriate repo(s) ready.json`
369
+ };
370
+ }
371
+
372
+ // ============================================================
373
+ // Task Decomposition (Criterion 5)
374
+ // ============================================================
375
+
376
+ /**
377
+ * Decompose a workspace-level task into repo-level tasks.
378
+ * Creates entries in each affected repo's ready.json.
379
+ *
380
+ * @param {string} workspaceRoot
381
+ * @param {Object} workspaceTask — { title, description, criteria }
382
+ * @param {Object} plan — from generateCrossRepoPlan()
383
+ * @returns {Array<Object>} created repo-level tasks
384
+ */
385
+ function decomposeToRepoTasks(workspaceRoot, workspaceTask, plan) {
386
+ const configPath = path.join(workspaceRoot, 'wogi-workspace.json');
387
+ const config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
388
+ const createdTasks = [];
389
+
390
+ for (const step of plan.executionOrder) {
391
+ if (step.executor === 'manager') continue; // Manager steps don't create repo tasks
392
+
393
+ const memberConfig = config.members[step.executor];
394
+ if (!memberConfig) continue;
395
+
396
+ const memberPath = path.resolve(workspaceRoot, memberConfig.path);
397
+ const readyPath = path.join(memberPath, '.workflow', 'state', 'ready.json');
398
+
399
+ if (!fs.existsSync(readyPath)) continue;
400
+
401
+ try {
402
+ const ready = JSON.parse(fs.readFileSync(readyPath, 'utf-8'));
403
+ const taskId = 'wf-' + crypto.randomBytes(4).toString('hex');
404
+
405
+ const repoTask = {
406
+ id: taskId,
407
+ title: `[Workspace] ${workspaceTask.title} — ${step.executor} (${step.phase})`,
408
+ type: workspaceTask.type || 'feature',
409
+ level: 'L2',
410
+ priority: 'P0',
411
+ source: `workspace:${workspaceTask.id || 'direct'}`,
412
+ status: 'ready',
413
+ description: step.description + '\n\n' + (workspaceTask.description || ''),
414
+ createdAt: new Date().toISOString()
415
+ };
416
+
417
+ if (!ready.ready) ready.ready = [];
418
+ ready.ready.push(repoTask);
419
+ ready.lastUpdated = new Date().toISOString();
420
+ fs.writeFileSync(readyPath, JSON.stringify(ready, null, 2));
421
+
422
+ createdTasks.push({ repo: step.executor, phase: step.phase, task: repoTask });
423
+ } catch (_err) {
424
+ // Non-critical — log and continue
425
+ }
426
+ }
427
+
428
+ return createdTasks;
429
+ }
430
+
431
+ // ============================================================
432
+ // Dependency-Aware Ordering (Criterion 6)
433
+ // ============================================================
434
+
435
+ /**
436
+ * Determine execution order respecting dependencies:
437
+ * library → provider → consumer
438
+ *
439
+ * @param {Object} manifest
440
+ * @param {string[]} targetRepos — repos involved in the task
441
+ * @returns {Array<{ name: string, phase: string, order: number }>}
442
+ */
443
+ function getExecutionOrder(manifest, targetRepos) {
444
+ const order = [];
445
+ const phaseOrder = { library: 0, provider: 1, both: 1, consumer: 2, standalone: 3 };
446
+
447
+ for (const name of targetRepos) {
448
+ const member = manifest.members[name];
449
+ if (!member) continue;
450
+ order.push({
451
+ name,
452
+ role: member.role,
453
+ phase: member.role,
454
+ order: phaseOrder[member.role] ?? 3
455
+ });
456
+ }
457
+
458
+ // Sort by phase order (library first, then provider, then consumer)
459
+ order.sort((a, b) => a.order - b.order);
460
+
461
+ return order;
462
+ }
463
+
464
+ // ============================================================
465
+ // Exports
466
+ // ============================================================
467
+
468
+ module.exports = {
469
+ // Routing
470
+ analyzeTaskRouting,
471
+ ROLE_KEYWORDS,
472
+
473
+ // Delegation
474
+ generateSingleRepoDelegation,
475
+ generateCrossRepoPlan,
476
+
477
+ // Investigation
478
+ generateParallelInvestigation,
479
+
480
+ // Decomposition
481
+ decomposeToRepoTasks,
482
+
483
+ // Ordering
484
+ getExecutionOrder
485
+ };