@lumenflow/cli 2.18.3 → 2.20.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 (128) hide show
  1. package/README.md +44 -42
  2. package/dist/agent-session.js +1 -1
  3. package/dist/agent-session.js.map +1 -1
  4. package/dist/commands/integrate.js +1 -0
  5. package/dist/commands/integrate.js.map +1 -1
  6. package/dist/commands.js +1 -0
  7. package/dist/commands.js.map +1 -1
  8. package/dist/delegation-list.js +140 -0
  9. package/dist/delegation-list.js.map +1 -0
  10. package/dist/docs-sync.js +1 -0
  11. package/dist/docs-sync.js.map +1 -1
  12. package/dist/doctor.js +36 -99
  13. package/dist/doctor.js.map +1 -1
  14. package/dist/gates-plan-resolvers.js +150 -0
  15. package/dist/gates-plan-resolvers.js.map +1 -0
  16. package/dist/gates-runners.js +533 -0
  17. package/dist/gates-runners.js.map +1 -0
  18. package/dist/gates-types.js +3 -0
  19. package/dist/gates-types.js.map +1 -1
  20. package/dist/gates-utils.js +316 -0
  21. package/dist/gates-utils.js.map +1 -0
  22. package/dist/gates.js +44 -1016
  23. package/dist/gates.js.map +1 -1
  24. package/dist/hooks/enforcement-generator.js +16 -880
  25. package/dist/hooks/enforcement-generator.js.map +1 -1
  26. package/dist/hooks/enforcement-sync.js +6 -5
  27. package/dist/hooks/enforcement-sync.js.map +1 -1
  28. package/dist/hooks/generators/auto-checkpoint.js +123 -0
  29. package/dist/hooks/generators/auto-checkpoint.js.map +1 -0
  30. package/dist/hooks/generators/enforce-worktree.js +188 -0
  31. package/dist/hooks/generators/enforce-worktree.js.map +1 -0
  32. package/dist/hooks/generators/index.js +16 -0
  33. package/dist/hooks/generators/index.js.map +1 -0
  34. package/dist/hooks/generators/pre-compact-checkpoint.js +134 -0
  35. package/dist/hooks/generators/pre-compact-checkpoint.js.map +1 -0
  36. package/dist/hooks/generators/require-wu.js +115 -0
  37. package/dist/hooks/generators/require-wu.js.map +1 -0
  38. package/dist/hooks/generators/session-start-recovery.js +101 -0
  39. package/dist/hooks/generators/session-start-recovery.js.map +1 -0
  40. package/dist/hooks/generators/signal-utils.js +52 -0
  41. package/dist/hooks/generators/signal-utils.js.map +1 -0
  42. package/dist/hooks/generators/warn-incomplete.js +65 -0
  43. package/dist/hooks/generators/warn-incomplete.js.map +1 -0
  44. package/dist/init-detection.js +228 -0
  45. package/dist/init-detection.js.map +1 -0
  46. package/dist/init-scaffolding.js +146 -0
  47. package/dist/init-scaffolding.js.map +1 -0
  48. package/dist/init-templates.js +1928 -0
  49. package/dist/init-templates.js.map +1 -0
  50. package/dist/init.js +137 -2425
  51. package/dist/init.js.map +1 -1
  52. package/dist/initiative-edit.js +42 -11
  53. package/dist/initiative-edit.js.map +1 -1
  54. package/dist/initiative-remove-wu.js +0 -0
  55. package/dist/initiative-status.js +29 -2
  56. package/dist/initiative-status.js.map +1 -1
  57. package/dist/mem-context.js +22 -9
  58. package/dist/mem-context.js.map +1 -1
  59. package/dist/orchestrate-init-status.js +32 -1
  60. package/dist/orchestrate-init-status.js.map +1 -1
  61. package/dist/orchestrate-initiative.js +2 -2
  62. package/dist/orchestrate-initiative.js.map +1 -1
  63. package/dist/orchestrate-monitor.js +38 -38
  64. package/dist/orchestrate-monitor.js.map +1 -1
  65. package/dist/plan-link.js +7 -14
  66. package/dist/plan-link.js.map +1 -1
  67. package/dist/public-manifest.js +19 -5
  68. package/dist/public-manifest.js.map +1 -1
  69. package/dist/shared-validators.js +1 -0
  70. package/dist/shared-validators.js.map +1 -1
  71. package/dist/spawn-list.js +0 -0
  72. package/dist/sync-templates.js +2 -1
  73. package/dist/sync-templates.js.map +1 -1
  74. package/dist/wu-claim-branch.js +121 -0
  75. package/dist/wu-claim-branch.js.map +1 -0
  76. package/dist/wu-claim-output.js +83 -0
  77. package/dist/wu-claim-output.js.map +1 -0
  78. package/dist/wu-claim-resume-handler.js +85 -0
  79. package/dist/wu-claim-resume-handler.js.map +1 -0
  80. package/dist/wu-claim-state.js +572 -0
  81. package/dist/wu-claim-state.js.map +1 -0
  82. package/dist/wu-claim-validation.js +439 -0
  83. package/dist/wu-claim-validation.js.map +1 -0
  84. package/dist/wu-claim-worktree.js +221 -0
  85. package/dist/wu-claim-worktree.js.map +1 -0
  86. package/dist/wu-claim.js +96 -1394
  87. package/dist/wu-claim.js.map +1 -1
  88. package/dist/wu-code-path-coverage.js +81 -0
  89. package/dist/wu-code-path-coverage.js.map +1 -0
  90. package/dist/wu-create-content.js +256 -0
  91. package/dist/wu-create-content.js.map +1 -0
  92. package/dist/wu-create-readiness.js +57 -0
  93. package/dist/wu-create-readiness.js.map +1 -0
  94. package/dist/wu-create-validation.js +124 -0
  95. package/dist/wu-create-validation.js.map +1 -0
  96. package/dist/wu-create.js +45 -442
  97. package/dist/wu-create.js.map +1 -1
  98. package/dist/wu-done.js +151 -249
  99. package/dist/wu-done.js.map +1 -1
  100. package/dist/wu-edit-operations.js +401 -0
  101. package/dist/wu-edit-operations.js.map +1 -0
  102. package/dist/wu-edit-validators.js +280 -0
  103. package/dist/wu-edit-validators.js.map +1 -0
  104. package/dist/wu-edit.js +43 -759
  105. package/dist/wu-edit.js.map +1 -1
  106. package/dist/wu-prep.js +43 -127
  107. package/dist/wu-prep.js.map +1 -1
  108. package/dist/wu-repair.js +1 -1
  109. package/dist/wu-repair.js.map +1 -1
  110. package/dist/wu-sandbox.js +253 -0
  111. package/dist/wu-sandbox.js.map +1 -0
  112. package/dist/wu-spawn-prompt-builders.js +1124 -0
  113. package/dist/wu-spawn-prompt-builders.js.map +1 -0
  114. package/dist/wu-spawn-strategy-resolver.js +319 -0
  115. package/dist/wu-spawn-strategy-resolver.js.map +1 -0
  116. package/dist/wu-spawn.js +9 -1398
  117. package/dist/wu-spawn.js.map +1 -1
  118. package/dist/wu-status.js +4 -0
  119. package/dist/wu-status.js.map +1 -1
  120. package/dist/wu-validate.js +1 -1
  121. package/dist/wu-validate.js.map +1 -1
  122. package/package.json +15 -11
  123. package/templates/core/LUMENFLOW.md.template +29 -99
  124. package/templates/core/UPGRADING.md.template +2 -2
  125. package/templates/core/ai/onboarding/agent-invocation-guide.md.template +1 -1
  126. package/templates/core/ai/onboarding/quick-ref-commands.md.template +29 -4
  127. package/templates/core/ai/onboarding/release-process.md.template +1 -1
  128. package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +8 -8
package/dist/wu-spawn.js CHANGED
@@ -26,1407 +26,18 @@
26
26
  * Codex Mode:
27
27
  * When --codex is used, outputs a Codex/GPT-friendly Markdown prompt (no antml/XML escaping).
28
28
  *
29
+ * Architecture (WU-1652):
30
+ * This file is a facade that re-exports from:
31
+ * - wu-spawn-prompt-builders.ts: All prompt section generators, formatters, template helpers
32
+ * - wu-spawn-strategy-resolver.ts: Client/strategy resolution, lane checks, CLI orchestration
33
+ *
29
34
  * @see {@link https://lumenflow.dev/reference/agent-invocation-guide/} - Context loading templates
30
35
  */
31
- import { existsSync, readFileSync } from 'node:fs';
32
- import path from 'node:path';
33
- import { createWUParser, WU_OPTIONS } from '@lumenflow/core/arg-parser';
34
- import { WU_PATHS } from '@lumenflow/core/wu-paths';
35
- import { parseYAML } from '@lumenflow/core/wu-yaml';
36
36
  import { die } from '@lumenflow/core/error-handler';
37
- import { WU_STATUS, PATTERNS, FILE_SYSTEM, EMOJI } from '@lumenflow/core/wu-constants';
38
- // WU-1603: Check lane lock status before spawning
39
- // WU-1325: Import lock policy getter for lane availability check
40
- import { checkLaneLock } from '@lumenflow/core/lane-lock';
41
- import { getLockPolicyForLane, getWipLimitForLane } from '@lumenflow/core/lane-checker';
42
- import { minimatch } from 'minimatch';
43
- // WU-2252: Import invariants loader for spawn output injection
44
- import { loadInvariants, INVARIANT_TYPES } from '@lumenflow/core/invariants-runner';
45
- import { validateSpawnArgs, generateExecutionModeSection, generateThinkToolGuidance, recordSpawnToRegistry, formatSpawnRecordedMessage, } from '@lumenflow/core/wu-spawn-helpers';
46
- import { SpawnStrategyFactory } from '@lumenflow/core/spawn-strategy';
47
- import { getConfig } from '@lumenflow/core/config';
48
- import { generateClientSkillsGuidance, generateSkillsSelectionSection, resolveClientConfig, } from '@lumenflow/core/wu-spawn-skills';
49
- // WU-1253: Template loader for extracted prompt templates
50
- import { loadTemplatesWithOverrides, replaceTokens } from '@lumenflow/core/template-loader';
51
- import { validateSpawnDependencies, formatDependencyError, } from '@lumenflow/core/dependency-validator';
52
- // WU-1192: Import prompt generation from Core (single source of truth)
53
- // WU-1203: Import generateAgentCoordinationSection from core for config-driven progress signals
54
- // WU-1288: Import policy-based test guidance and mandatory standards generators
55
- import { TRUNCATION_WARNING_BANNER, SPAWN_END_SENTINEL, generateTestGuidance, generateAgentCoordinationSection, generatePolicyBasedTestGuidance, generateMandatoryStandards, generateEnforcementSummary, } from '@lumenflow/core/wu-spawn';
56
- // WU-1288: Import resolvePolicy for methodology policy resolution
57
- import { resolvePolicy } from '@lumenflow/core/resolve-policy';
58
- // WU-1240: Import memory context integration for spawn prompts
59
- import { generateMemoryContextSection, checkMemoryLayerInitialized, getMemoryContextMaxSize, } from '@lumenflow/core/wu-spawn-context';
60
- // Re-export for backwards compatibility
61
- export { TRUNCATION_WARNING_BANNER, SPAWN_END_SENTINEL, generateTestGuidance, generateAgentCoordinationSection, };
62
- /**
63
- * Mandatory agent trigger patterns.
64
- * Mirrors MANDATORY_TRIGGERS from orchestration.constants.ts.
65
- *
66
- * Note: For LumenFlow framework development, this is empty since we don't have
67
- * application-specific concerns like PHI, auth, or RLS. Projects using LumenFlow
68
- * should configure their own triggers based on their domain requirements.
69
- */
70
- const MANDATORY_TRIGGERS = {
71
- // No mandatory triggers for LumenFlow framework development.
72
- };
73
- const BRIEF_LOG_PREFIX = '[wu:brief]';
74
- const DELEGATE_LOG_PREFIX = '[wu:delegate]';
75
- /**
76
- * Detect mandatory agents based on code paths.
77
- *
78
- * @param {string[]} codePaths - Array of file paths
79
- * @returns {string[]} Array of mandatory agent names
80
- */
81
- function detectMandatoryAgents(codePaths) {
82
- if (!codePaths || codePaths.length === 0) {
83
- return [];
84
- }
85
- const triggeredAgents = new Set();
86
- for (const [agentName, patterns] of Object.entries(MANDATORY_TRIGGERS)) {
87
- const isTriggered = codePaths.some((filePath) => patterns.some((pattern) => minimatch(filePath, pattern)));
88
- if (isTriggered) {
89
- triggeredAgents.add(agentName);
90
- }
91
- }
92
- return Array.from(triggeredAgents);
93
- }
94
- /**
95
- * Format acceptance criteria as markdown list
96
- *
97
- * @param {string[]|undefined} acceptance - Acceptance criteria array
98
- * @returns {string} Formatted acceptance criteria
99
- */
100
- function formatAcceptance(acceptance) {
101
- if (!acceptance || acceptance.length === 0) {
102
- return '- No acceptance criteria defined';
103
- }
104
- return acceptance.map((item) => `- [ ] ${item}`).join('\n');
105
- }
106
- /**
107
- * Format spec_refs as markdown links
108
- *
109
- * @param {string[]|undefined} specRefs - Spec references array
110
- * @returns {string} Formatted references or empty string if none
111
- */
112
- function formatSpecRefs(specRefs) {
113
- if (!specRefs || specRefs.length === 0) {
114
- return '';
115
- }
116
- return specRefs.map((ref) => `- ${ref}`).join('\n');
117
- }
118
- /**
119
- * Format risks as markdown list
120
- *
121
- * @param {string[]|undefined} risks - Risks array
122
- * @returns {string} Formatted risks or empty string if none
123
- */
124
- function formatRisks(risks) {
125
- if (!risks || risks.length === 0) {
126
- return '';
127
- }
128
- return risks.map((risk) => `- ${risk}`).join('\n');
129
- }
130
- /**
131
- * Format manual tests as markdown checklist
132
- *
133
- * @param {string[]|undefined} manualTests - Manual test steps
134
- * @returns {string} Formatted tests or empty string if none
135
- */
136
- function formatManualTests(manualTests) {
137
- if (!manualTests || manualTests.length === 0) {
138
- return '';
139
- }
140
- return manualTests.map((test) => `- [ ] ${test}`).join('\n');
141
- }
142
- function generateImplementationContext(doc) {
143
- const sections = [];
144
- // References (spec_refs)
145
- const refs = formatSpecRefs(doc.spec_refs);
146
- if (refs) {
147
- sections.push(`## References\n\n${refs}`);
148
- }
149
- // Implementation Notes
150
- if (doc.notes && doc.notes.trim()) {
151
- sections.push(`## Implementation Notes\n\n${doc.notes.trim()}`);
152
- }
153
- // Risks
154
- const risks = formatRisks(doc.risks);
155
- if (risks) {
156
- sections.push(`## Risks\n\n${risks}`);
157
- }
158
- // Manual Verification (tests.manual)
159
- const manualTests = formatManualTests(doc.tests?.manual);
160
- if (manualTests) {
161
- sections.push(`## Manual Verification\n\n${manualTests}`);
162
- }
163
- if (sections.length === 0) {
164
- return '';
165
- }
166
- return sections.join('\n\n---\n\n');
167
- }
168
- /**
169
- * Check if a code path matches an invariant based on type
170
- *
171
- * @param {object} invariant - Invariant definition
172
- * @param {string[]} codePaths - Array of code paths
173
- * @returns {boolean} True if code paths match the invariant
174
- */
175
- function codePathMatchesInvariant(invariant, codePaths) {
176
- switch (invariant.type) {
177
- case INVARIANT_TYPES.FORBIDDEN_FILE:
178
- case INVARIANT_TYPES.REQUIRED_FILE:
179
- return codePaths.some((p) => p === invariant.path || minimatch(p, invariant.path) || minimatch(invariant.path, p));
180
- case INVARIANT_TYPES.MUTUAL_EXCLUSIVITY:
181
- return codePaths.some((p) => invariant.paths.some((invPath) => p === invPath || minimatch(p, invPath)));
182
- case INVARIANT_TYPES.FORBIDDEN_PATTERN:
183
- case INVARIANT_TYPES.REQUIRED_PATTERN:
184
- return (invariant.scope?.some((scopePattern) => codePaths.some((p) => minimatch(p, scopePattern))) ?? false);
185
- // WU-2254: forbidden-import uses 'from' glob instead of 'scope'
186
- case INVARIANT_TYPES.FORBIDDEN_IMPORT:
187
- return invariant.from ? codePaths.some((p) => minimatch(p, invariant.from)) : false;
188
- default:
189
- return false;
190
- }
191
- }
192
- /**
193
- * Format a single invariant for output
194
- *
195
- * @param {object} inv - Invariant definition
196
- * @returns {string[]} Lines of formatted output
197
- */
198
- function formatInvariantForOutput(inv) {
199
- const lines = [`### ${inv.id} (${inv.type})`, '', inv.description, ''];
200
- if (inv.message) {
201
- lines.push(`**Action:** ${inv.message}`, '');
202
- }
203
- if (inv.path) {
204
- lines.push(`**Path:** \`${inv.path}\``);
205
- }
206
- if (inv.paths) {
207
- const formattedPaths = inv.paths.map((p) => `\`${p}\``).join(', ');
208
- lines.push(`**Paths:** ${formattedPaths}`);
209
- }
210
- // WU-2254: forbidden-import specific fields
211
- if (inv.from) {
212
- lines.push(`**From:** \`${inv.from}\``);
213
- }
214
- if (inv.cannot_import && Array.isArray(inv.cannot_import)) {
215
- const formattedImports = inv.cannot_import.map((m) => `\`${m}\``).join(', ');
216
- lines.push(`**Cannot Import:** ${formattedImports}`);
217
- }
218
- // WU-2254: required-pattern specific fields
219
- if (inv.pattern &&
220
- (inv.type === INVARIANT_TYPES.REQUIRED_PATTERN ||
221
- inv.type === INVARIANT_TYPES.FORBIDDEN_PATTERN)) {
222
- lines.push(`**Pattern:** \`${inv.pattern}\``);
223
- }
224
- if (inv.scope && Array.isArray(inv.scope)) {
225
- const formattedScope = inv.scope.map((s) => `\`${s}\``).join(', ');
226
- lines.push(`**Scope:** ${formattedScope}`);
227
- }
228
- lines.push('');
229
- return lines;
230
- }
231
- /**
232
- * WU-2252: Generate invariants/prior-art section for code_paths
233
- *
234
- * Loads relevant invariants from invariants.yml and generates a section
235
- * that surfaces constraints and prior-art for the WU's code_paths.
236
- *
237
- * @param {string[]} codePaths - Array of code paths from the WU
238
- * @returns {string} Invariants/prior-art section or empty string if none relevant
239
- */
240
- function generateInvariantsPriorArtSection(codePaths) {
241
- if (!codePaths || codePaths.length === 0) {
242
- return '';
243
- }
244
- // Try to load tools/invariants.yml
245
- const invariantsPath = path.resolve('tools/invariants.yml');
246
- if (!existsSync(invariantsPath)) {
247
- return '';
248
- }
249
- let invariants;
250
- try {
251
- invariants = loadInvariants(invariantsPath);
252
- }
253
- catch {
254
- return '';
255
- }
256
- if (!invariants || invariants.length === 0) {
257
- return '';
258
- }
259
- // Find relevant invariants based on code_paths
260
- const relevantInvariants = invariants.filter((inv) => codePathMatchesInvariant(inv, codePaths));
261
- if (relevantInvariants.length === 0) {
262
- return '';
263
- }
264
- // Format the section
265
- const lines = [
266
- '## Invariants/Prior-Art (WU-2252)',
267
- '',
268
- 'The following repo invariants are relevant to your code_paths:',
269
- '',
270
- ...relevantInvariants.flatMap(formatInvariantForOutput),
271
- '**IMPORTANT:** Do not create specs or acceptance criteria that conflict with these invariants.',
272
- ];
273
- return lines.join('\n');
274
- }
275
- // WU-1192: generateTestGuidance and related constants moved to @lumenflow/core
276
- // See imports from '@lumenflow/core/wu-spawn' above
277
- /**
278
- * Generate the context loading preamble
279
- *
280
- * Follows AGENTS.md context loading protocol (WU-2247):
281
- * 1. CLAUDE.md for workflow fundamentals
282
- * 2. README.md for project structure
283
- * 3. lumenflow-complete.md sections 1-7 (TDD, gates, DoD)
284
- * 4. WU YAML for specific task
285
- *
286
- * Includes context recovery section for session resumption (WU-1589).
287
- *
288
- * @param {string} id - WU ID
289
- * @returns {string} Context loading preamble
290
- */
291
- /**
292
- * Generate the context loading preamble using the strategy
293
- *
294
- * @param {string} id - WU ID
295
- * @param {SpawnStrategy} strategy - Client strategy
296
- * @returns {string} Context loading preamble
297
- */
298
- function generatePreamble(id, strategy) {
299
- return strategy.getPreamble(id);
300
- }
301
- /**
302
- * Generate the constraints block (appended at end per Lost in the Middle research)
303
- *
304
- * WU-2247: Aligned with LumenFlow §7.2 (stop-and-ask) and §7.3 (anti-loop guard).
305
- * Includes item 6: MEMORY LAYER COORDINATION (WU-1589).
306
- *
307
- * @param {string} id - WU ID
308
- * @returns {string} Constraints block
309
- */
310
- function generateConstraints(id) {
311
- return `---
312
-
313
- <constraints>
314
- CRITICAL RULES - ENFORCE BEFORE EVERY ACTION:
315
-
316
- 1. TDD CHECKPOINT (VERIFY BEFORE IMPLEMENTATION)
317
- - Did you write tests BEFORE implementation?
318
- - Is there at least one failing test for each acceptance criterion?
319
- - Never skip the RED phase — failing tests prove the test works
320
-
321
- 2. ANTI-LOOP GUARD (LumenFlow §7.3)
322
- - Max 3 attempts per unique error before escalating
323
- - If same error repeats 3x, STOP and report with full context
324
- - Retry with different approach, not same command
325
-
326
- 3. STOP-AND-ASK TRIGGERS (LumenFlow §7.2 - narrow scope)
327
- - Policy changes, auth/permissions modifications
328
- - PII/PHI/safety issues, cloud spend, secrets, backups
329
- - Same error repeats 3x
330
- - For ordinary errors: fix and retry autonomously (up to 3 attempts)
331
-
332
- 4. VERIFY COMPLETION before reporting success
333
- - Run: node packages/@lumenflow/agent/dist/agent-verification.js ${id} (from shared checkout)
334
- - Exit 0 = passed, Exit 1 = INCOMPLETE
335
- - Never report "done" if verification fails
336
-
337
- 5. NEVER FABRICATE COMPLETION
338
- - If blockers remain, report INCOMPLETE
339
- - If verification fails, summarize failures
340
- - Honesty over false completion
341
-
342
- 6. GIT WORKFLOW (CRITICAL - GitHub rules reject merge commits)
343
- - GitHub REJECTS merge commits on main
344
- - ALWAYS use \`git rebase origin/main\` before push
345
- - Push to main via \`git push origin lane/...:main\` (fast-forward only)
346
- - NEVER use \`git merge\` on main branch
347
- - Let \`pnpm wu:done\` handle the merge workflow
348
-
349
- 7. MEMORY LAYER COORDINATION (INIT-007)
350
- - Use \`pnpm mem:checkpoint --wu ${id}\` to save progress before risky operations
351
- - Check \`pnpm mem:inbox --wu ${id}\` periodically for parallel signals from other agents
352
- - Checkpoint triggers (WU-1943): checkpoint after each acceptance criterion completed, checkpoint before gates, checkpoint every 30 tool calls
353
-
354
- 8. SKIP-GATES AUTONOMY (WU-1142)
355
- - If gates fail, first check if failure is pre-existing on main: \`git checkout main && pnpm gates\`
356
- - If failure exists on main (not your change), use: \`pnpm wu:done --id ${id} --skip-gates --reason "pre-existing on main" --fix-wu WU-XXXX\`
357
- - Do NOT ask for approval - autonomous skip-gates for pre-existing failures is correct
358
- - This prevents getting stuck on infrastructure debt
359
- </constraints>`;
360
- }
361
- function generateCodexConstraints(id) {
362
- return `## Constraints (Critical)
363
-
364
- 1. **TDD checkpoint**: tests BEFORE implementation; never skip RED
365
- 2. **Stop on errors**: if any command fails, report BLOCKED (never DONE) with the error
366
- 3. **Verify before success**: run \`pnpm gates\` in the worktree, then run \`node packages/@lumenflow/agent/dist/agent-verification.js ${id}\` (from the shared checkout)
367
- 4. **No fabrication**: if blockers remain or verification fails, report INCOMPLETE
368
- 5. **Git workflow**: avoid merge commits; let \`pnpm wu:done\` handle completion
369
- 6. **Scope discipline**: stay within \`code_paths\`; capture out-of-scope issues via \`pnpm mem:create\`
370
- 7. **Skip-gates for pre-existing**: if gates fail due to pre-existing issue on main, use \`--skip-gates --reason "pre-existing" --fix-wu WU-XXX\``;
371
- }
372
- /**
373
- * Generate mandatory agent advisory section
374
- *
375
- * @param {string[]} mandatoryAgents - Array of mandatory agent names
376
- * @param {string} _id - WU ID (reserved for future use)
377
- * @returns {string} Mandatory agent section or empty string
378
- */
379
- function generateMandatoryAgentSection(mandatoryAgents, _id) {
380
- if (mandatoryAgents.length === 0) {
381
- return '';
382
- }
383
- const agentList = mandatoryAgents.map((agent) => ` - ${agent}`).join('\n');
384
- return `
385
- ## Mandatory Agents (MUST invoke before wu:done)
386
-
387
- Based on code_paths, the following agents MUST be invoked:
388
-
389
- ${agentList}
390
-
391
- Run: pnpm orchestrate:monitor to check agent status
392
- `;
393
- }
394
- /**
395
- * Generate effort scaling rules section (WU-1986)
396
- *
397
- * Based on Anthropic multi-agent research: helps agents decide when to
398
- * spawn sub-agents vs handle inline.
399
- *
400
- * @returns {string} Effort scaling section
401
- */
402
- export function generateEffortScalingRules() {
403
- return `## Effort Scaling (When to Spawn Sub-Agents)
404
-
405
- Use this heuristic to decide complexity:
406
-
407
- | Complexity | Approach | Tool Calls |
408
- |------------|----------|------------|
409
- | **Simple** (single file, <50 lines) | Handle inline | 3-10 |
410
- | **Moderate** (2-3 files, clear scope) | Handle inline | 10-20 |
411
- | **Complex** (4+ files, exploration needed) | Spawn Explore agent first | 20+ |
412
- | **Multi-domain** (cross-cutting concerns) | Spawn specialized sub-agents | Varies |
413
-
414
- **Rule**: If you need >30 tool calls for a subtask, consider spawning a sub-agent with a focused scope.`;
415
- }
416
- /**
417
- * Generate parallel tool call guidance (WU-1986)
418
- *
419
- * Based on Anthropic research: 3+ parallel tool calls significantly improve performance.
420
- *
421
- * @returns {string} Parallel tool call guidance
422
- */
423
- export function generateParallelToolCallGuidance() {
424
- return `## Parallel Tool Calls (Performance)
425
-
426
- **IMPORTANT**: Make 3+ tool calls in parallel when operations are independent.
427
-
428
- Good examples:
429
- - Reading multiple files simultaneously
430
- - Running independent grep searches
431
- - Spawning multiple Explore agents for different areas
432
-
433
- Bad examples:
434
- - Reading a file then editing it (sequential dependency)
435
- - Running tests then checking results (sequential)
436
-
437
- Parallelism reduces latency by 50-90% for complex tasks.`;
438
- }
439
- /**
440
- * Generate iterative search heuristics (WU-1986)
441
- *
442
- * Based on Anthropic research: start broad, narrow focus.
443
- *
444
- * @returns {string} Search heuristics section
445
- */
446
- export function generateIterativeSearchHeuristics() {
447
- return `## Search Strategy (Broad to Narrow)
448
-
449
- When exploring the codebase:
450
-
451
- 1. **Start broad**: Use Explore agent or glob patterns to understand structure
452
- 2. **Evaluate findings**: What patterns exist? What's relevant?
453
- 3. **Narrow focus**: Target specific files/functions based on findings
454
- 4. **Iterate**: Refine if initial approach misses the target
455
-
456
- Avoid: Jumping directly to specific file edits without understanding context.`;
457
- }
458
- /**
459
- * Generate token budget awareness section (WU-1986)
460
- *
461
- * @param {string} id - WU ID
462
- * @returns {string} Token budget section
463
- */
464
- export function generateTokenBudgetAwareness(id) {
465
- return `## Token Budget Awareness
466
-
467
- Context limit is ~200K tokens. Monitor your usage:
468
-
469
- - **At 50+ tool calls**: Create a checkpoint (\`pnpm mem:checkpoint --wu ${id}\`)
470
- - **At 100+ tool calls**: Consider spawning fresh sub-agent with focused scope
471
- - **Before risky operations**: Always checkpoint first
472
-
473
- If approaching limits, summarize progress and spawn continuation agent.`;
474
- }
475
- /**
476
- * Generate structured completion format (WU-1986)
477
- *
478
- * @param {string} id - WU ID
479
- * @returns {string} Completion format section
480
- */
481
- export function generateCompletionFormat(_id) {
482
- return `## Completion Report Format
483
-
484
- When finishing, provide structured output:
485
-
486
- \`\`\`
487
- ## Summary
488
- <1-3 sentences describing what was accomplished>
489
-
490
- ## Artifacts
491
- - Files modified: <list>
492
- - Tests added: <list>
493
- - Documentation updated: <list>
494
-
495
- ## Verification
496
- - Gates: <pass/fail>
497
- - Tests: <X passing, Y failing>
498
-
499
- ## Blockers (if any)
500
- - <blocker description>
501
-
502
- ## Follow-up (if needed)
503
- - <suggested next WU or action>
504
- \`\`\`
505
-
506
- This format enables orchestrator to track progress across waves.`;
507
- }
508
- // WU-1203: generateAgentCoordinationSection is now imported from @lumenflow/core
509
- // The core version reads config and generates dynamic guidance based on progress_signals settings
510
- /**
511
- * Generate quick fix commands section (WU-1987)
512
- *
513
- * Provides format/lint/typecheck commands for quick fixes before gates.
514
- *
515
- * @returns {string} Quick fix commands section
516
- */
517
- export function generateQuickFixCommands() {
518
- return `## Quick Fix Commands
519
-
520
- If gates fail, try these before investigating:
521
-
522
- \`\`\`bash
523
- pnpm format # Auto-fix formatting issues
524
- pnpm lint # Check linting (use --fix for auto-fix)
525
- pnpm typecheck # Check TypeScript types
526
- \`\`\`
527
-
528
- **Use before gates** to catch simple issues early. These are faster than full \`pnpm gates\`.`;
529
- }
530
- /**
531
- * Generate Worktree Block Recovery section (WU-1134)
532
- *
533
- * Provides guidance for agents when they're blocked by the worktree hook.
534
- * This happens when agents try to commit from main instead of the worktree.
535
- *
536
- * @param {string} worktreePath - Worktree path from WU YAML
537
- * @returns {string} Worktree block recovery section
538
- */
539
- export function generateWorktreeBlockRecoverySection(worktreePath) {
540
- return `## When Blocked by Worktree Hook
541
-
542
- If you encounter a "worktree required" or "commit blocked" error:
543
-
544
- 1. **Check existing worktrees**: \`git worktree list\`
545
- 2. **Navigate to the worktree**: \`cd ${worktreePath || 'worktrees/<lane>-wu-xxx'}\`
546
- 3. **Retry your operation** from within the worktree
547
- 4. **Use relative paths only** (never absolute paths starting with /)
548
-
549
- ### Common Causes
550
-
551
- - Running \`git commit\` from main checkout instead of worktree
552
- - Using absolute paths that bypass worktree isolation
553
- - Forgetting to \`cd\` to worktree after \`wu:claim\`
554
-
555
- ### Quick Fix
556
-
557
- \`\`\`bash
558
- # Check where you are
559
- pwd
560
- git worktree list
561
-
562
- # Navigate to your worktree
563
- cd ${worktreePath || 'worktrees/<lane>-wu-xxx'}
564
-
565
- # Retry your commit
566
- git add . && git commit -m "your message"
567
- \`\`\``;
568
- }
569
- /**
570
- * Generate Lane Selection section (WU-2107)
571
- *
572
- * Provides guidance on lane selection when creating new WUs.
573
- * Points agents to wu:infer-lane for automated lane suggestions.
574
- *
575
- * @returns {string} Lane Selection section
576
- */
577
- export function generateLaneSelectionSection() {
578
- return `## Lane Selection
579
-
580
- When creating new WUs, use the correct lane to enable parallelization:
581
-
582
- \`\`\`bash
583
- # Get lane suggestion based on code paths and description
584
- pnpm wu:infer-lane --id WU-XXX
585
-
586
- # Or infer from manual inputs
587
- pnpm wu:infer-lane --paths "tools/**" --desc "CLI improvements"
588
- \`\`\`
589
-
590
- **Lane taxonomy**: See \`.lumenflow.lane-inference.yaml\` for valid lanes and patterns.
591
-
592
- **Why lanes matter**: WIP=1 per lane means correct lane selection enables parallel work across lanes.`;
593
- }
594
- /**
595
- * Generate Worktree Path Guidance section (WU-2362)
596
- *
597
- * Provides guidance for sub-agents on working within worktrees, including
598
- * how to determine the worktree root and where to create stamps.
599
- *
600
- * Problem: CLAUDE_PROJECT_DIR is hook-only; sub-agents inherit parent cwd (main).
601
- * Solution: Use git rev-parse --show-toplevel to determine actual worktree root.
602
- *
603
- * @param {string|undefined} worktreePath - Worktree path from WU YAML
604
- * @returns {string} Worktree path guidance section
605
- */
606
- export function generateWorktreePathGuidance(worktreePath) {
607
- if (!worktreePath) {
608
- return '';
609
- }
610
- return `## Worktree Path Guidance (WU-2362)
611
-
612
- **Your worktree:** \`${worktreePath}\`
613
-
614
- ### Finding the Worktree Root
615
-
616
- Sub-agents may inherit the parent's cwd (main checkout). To find the actual worktree root:
617
-
618
- \`\`\`bash
619
- # Get the worktree root (not main checkout)
620
- git rev-parse --show-toplevel
621
- \`\`\`
622
-
623
- ### Stamp Creation
624
-
625
- When creating \`.lumenflow/\` stamps or other artifacts:
626
-
627
- 1. **ALWAYS** create stamps in the **worktree**, not main
628
- 2. Use \`git rev-parse --show-toplevel\` to get the correct base path
629
- 3. Stamps created on main will be lost when the worktree merges
630
-
631
- \`\`\`bash
632
- # CORRECT: Create stamp in worktree
633
- WORKTREE_ROOT=$(git rev-parse --show-toplevel)
634
- mkdir -p "$WORKTREE_ROOT/.lumenflow/agent-runs"
635
- touch "$WORKTREE_ROOT/.lumenflow/agent-runs/code-reviewer.stamp"
636
-
637
- # WRONG: Hardcoded path to main
638
- # touch /path/to/main/.lumenflow/agent-runs/code-reviewer.stamp
639
- \`\`\`
640
-
641
- ### Why This Matters
642
-
643
- - Stamps on main get overwritten by worktree merge
644
- - \`wu:done\` validates stamps exist in the worktree branch
645
- - Parallel WUs in other lanes won't see your stamps if on main`;
646
- }
647
- /**
648
- * Generate the Bug Discovery section (WU-1592, WU-2284)
649
- *
650
- * Instructs sub-agents to capture bugs found mid-WU via mem:create.
651
- * This enables scope-creep tracking and ensures discovered bugs
652
- * are not lost when agents encounter issues outside their WU scope.
653
- *
654
- * WU-2284: Added explicit prohibition against using wu:create directly
655
- * for discovered issues. Agents must use mem:create for capture, then
656
- * human triage decides whether to promote to a WU.
657
- *
658
- * @param {string} id - WU ID
659
- * @returns {string} Bug Discovery section
660
- */
661
- function generateBugDiscoverySection(id) {
662
- return `## Bug Discovery (Mid-WU Issue Capture)
663
-
664
- If you discover a bug or issue **outside the scope of this WU**:
665
-
666
- 1. **Capture it immediately** using:
667
- \`\`\`bash
668
- pnpm mem:create 'Bug: <description>' --type discovery --tags bug,scope-creep --wu ${id}
669
- \`\`\`
670
-
671
- 2. **Continue with your WU** — do not fix bugs outside your scope
672
- 3. **Reference in notes** — mention the mem node ID in your completion notes
673
-
674
- ### NEVER use wu:create for discovered issues
675
-
676
- **Do NOT use \`wu:create\` directly for bugs discovered mid-WU.**
677
-
678
- - \`mem:create\` = **capture** (immediate, no human approval needed)
679
- - \`wu:create\` = **planned work** (requires human triage and approval)
680
-
681
- Discovered issues MUST go through human triage before becoming WUs.
682
- Using \`wu:create\` directly bypasses the triage workflow and creates
683
- unreviewed work items.
684
-
685
- ### When to Capture
686
-
687
- - Found a bug in code NOT in your \`code_paths\`
688
- - Discovered an issue that would require >10 lines to fix
689
- - Encountered broken behaviour unrelated to your acceptance criteria
690
-
691
- ### Triage Workflow
692
-
693
- After WU completion, bugs can be promoted to Bug WUs by humans:
694
- \`\`\`bash
695
- pnpm mem:triage --wu ${id} # List discoveries for this WU
696
- pnpm mem:triage --promote <node-id> --lane "<lane>" # Create Bug WU (human action)
697
- \`\`\`
698
-
699
- See: https://lumenflow.dev/reference/agent-invocation-guide/ §Bug Discovery`;
700
- }
701
- /**
702
- * Generate lane-specific guidance
703
- *
704
- * @param {string} lane - Lane name
705
- * @returns {string} Lane-specific guidance or empty string
706
- */
707
- function generateLaneGuidance(lane) {
708
- if (!lane)
709
- return '';
710
- const laneParent = lane.split(':')[0].trim();
711
- const guidance = {
712
- Operations: `## Lane-Specific: Tooling
713
-
714
- - Update tool documentation in tools/README.md or relevant docs if adding new CLI commands`,
715
- Intelligence: `## Lane-Specific: Intelligence
716
-
717
- - All prompt changes require golden dataset evaluation (pnpm prompts:eval)
718
- - Follow prompt versioning guidelines in ai/prompts/README.md`,
719
- Experience: `## Lane-Specific: Experience
720
-
721
- - Follow design system tokens defined in the project
722
- - Ensure accessibility compliance (WCAG 2.1 AA)`,
723
- Core: `## Lane-Specific: Core
724
-
725
- - Maintain hexagonal architecture boundaries
726
- - Update domain model documentation if changing entities`,
727
- };
728
- return guidance[laneParent] || '';
729
- }
730
- /**
731
- * Generate the Action section based on WU claim status (WU-1745).
732
- *
733
- * If WU is already claimed (has claimed_at and worktree_path), tells agent
734
- * to continue in the existing worktree.
735
- *
736
- * If WU is unclaimed (status: ready), tells agent to run wu:claim first.
737
- *
738
- * @param {object} doc - WU YAML document
739
- * @param {string} id - WU ID
740
- * @returns {string} Action section content
741
- */
742
- export function generateActionSection(doc, id) {
743
- const isAlreadyClaimed = doc.claimed_at && doc.worktree_path;
744
- if (isAlreadyClaimed) {
745
- return `This WU is already claimed. Continue implementation in worktree following all standards above.
746
-
747
- cd ${doc.worktree_path}`;
748
- }
749
- // WU is unclaimed - agent needs to claim first
750
- const laneSlug = (doc.lane || 'unknown')
751
- .toLowerCase()
752
- .replace(/[:\s]+/g, '-')
753
- .replace(/-+/g, '-');
754
- return `**FIRST: Claim this WU before starting work:**
755
-
756
- \`\`\`bash
757
- pnpm wu:claim --id ${id} --lane "${doc.lane}"
758
- cd worktrees/${laneSlug}-${id.toLowerCase()}
759
- \`\`\`
760
-
761
- Then implement following all standards above.
762
-
763
- **CRITICAL:** Never use \`git worktree add\` directly. Always use \`pnpm wu:claim\` to ensure:
764
- - Event tracking in .lumenflow/state/wu-events.jsonl
765
- - Lane lock acquisition (WIP=1 enforcement)
766
- - Session tracking for context recovery`;
767
- }
768
- /**
769
- * Generate the Completion Workflow section for sub-agents (WU-2682).
770
- *
771
- * Explicitly instructs sub-agents to run wu:done autonomously after gates pass.
772
- * This prevents agents from asking permission instead of completing.
773
- *
774
- * @param {string} id - WU ID
775
- * @returns {string} Completion Workflow section
776
- */
777
- export function generateCompletionWorkflowSection(id) {
778
- return `## Completion Workflow
779
-
780
- **CRITICAL: Complete autonomously. Do NOT ask for permission.**
781
-
782
- After all acceptance criteria are satisfied:
783
-
784
- 1. Run gates in the worktree: \`pnpm gates\`
785
- 2. If gates pass, cd back to main checkout
786
- 3. Run: \`pnpm wu:done --id ${id}\`
787
-
788
- \`\`\`bash
789
- # From worktree, after gates pass:
790
- cd /path/to/main # NOT the worktree
791
- pnpm wu:done --id ${id}
792
- \`\`\`
793
-
794
- **wu:done** handles: merge to main, stamp creation, worktree cleanup.
795
-
796
- **Do not ask** "should I run wu:done?" — just run it when gates pass.`;
797
- }
798
- /**
799
- * WU-1253: Try to load templates for spawn prompt sections.
800
- *
801
- * Implements shadow mode: tries templates first, returns empty map
802
- * if templates aren't available (caller uses hardcoded fallback).
803
- *
804
- * @param clientName - Client name for overrides (e.g., 'claude-code', 'cursor')
805
- * @param context - Token values for replacement
806
- * @returns Map of template id to processed content, empty if templates unavailable
807
- */
808
- function tryLoadTemplates(clientName, context) {
809
- const result = new Map();
810
- try {
811
- const baseDir = process.cwd();
812
- const templates = loadTemplatesWithOverrides(baseDir, clientName);
813
- // Process each template: replace tokens
814
- for (const [id, template] of templates) {
815
- const processed = replaceTokens(template.content, context);
816
- result.set(id, processed);
817
- }
818
- }
819
- catch {
820
- // Template loading failed - return empty map for hardcoded fallback
821
- }
822
- return result;
823
- }
824
- /**
825
- * WU-1253: Build template context from WU document.
826
- *
827
- * @param doc - WU YAML document
828
- * @param id - WU ID
829
- * @returns Context for template token replacement
830
- */
831
- function buildSpawnTemplateContext(doc, id) {
832
- const lane = doc.lane || '';
833
- const laneParent = lane.split(':')[0]?.trim() || '';
834
- const type = (doc.type || 'feature').toLowerCase();
835
- return {
836
- WU_ID: id,
837
- LANE: lane,
838
- TYPE: type,
839
- TITLE: doc.title || '',
840
- DESCRIPTION: doc.description || '',
841
- WORKTREE_PATH: doc.worktree_path || '',
842
- laneParent,
843
- // Lowercase aliases for condition evaluation
844
- type,
845
- lane,
846
- worktreePath: doc.worktree_path || '',
847
- };
848
- }
849
- function generateClientBlocksSection(clientContext) {
850
- if (!clientContext?.config?.blocks?.length)
851
- return '';
852
- const blocks = clientContext.config.blocks
853
- .map((block) => `### ${block.title}\n\n${block.content}`)
854
- .join('\n\n');
855
- return `## Client Guidance (${clientContext.name})\n\n${blocks}`;
856
- }
857
- /**
858
- * Generate the complete Task tool invocation
859
- *
860
- * @param {object} doc - WU YAML document
861
- * @param {string} id - WU ID
862
- * @param {SpawnStrategy} strategy - Client strategy
863
- * @param {object} [options={}] - Thinking mode options
864
- * @param {boolean} [options.thinking] - Whether extended thinking is enabled
865
- * @param {boolean} [options.noThinking] - Whether thinking is explicitly disabled
866
- * @param {string} [options.budget] - Token budget for thinking
867
- * @returns {string} Complete Task tool invocation
868
- */
869
- export function generateTaskInvocation(doc, id, strategy, options = {}) {
870
- const codePaths = doc.code_paths || [];
871
- const mandatoryAgents = detectMandatoryAgents(codePaths);
872
- // WU-1253: Try loading templates (shadow mode - falls back to hardcoded if unavailable)
873
- const clientName = options.client?.name || 'claude-code';
874
- const templateContext = buildSpawnTemplateContext(doc, id);
875
- const templates = tryLoadTemplates(clientName, templateContext);
876
- const preamble = generatePreamble(id, strategy);
877
- const clientContext = options.client;
878
- const config = options.config || getConfig();
879
- // WU-1288: Resolve methodology policy from config
880
- const policy = resolvePolicy(config);
881
- // WU-1142: Use type-aware test guidance instead of hardcoded TDD directive
882
- // WU-1288: Use policy-based test guidance that respects methodology.testing config
883
- // WU-1253: Try template first, fall back to policy-based guidance
884
- const testGuidance = templates.get('tdd-directive') || generatePolicyBasedTestGuidance(doc.type, policy);
885
- // WU-1288: Generate enforcement summary from resolved policy
886
- const enforcementSummary = generateEnforcementSummary(policy);
887
- // WU-1288: Generate mandatory standards based on resolved policy
888
- const mandatoryStandards = generateMandatoryStandards(policy);
889
- // WU-1142: Pass lane to get byLane skills
890
- const clientSkillsGuidance = generateClientSkillsGuidance(clientContext, doc.lane);
891
- // WU-1253: Try template for skills-selection, build skills section
892
- const skillsTemplateContent = templates.get('skills-selection');
893
- const skillsGuidanceSuffix = clientSkillsGuidance ? '\n' + clientSkillsGuidance : '';
894
- const skillsBaseContent = skillsTemplateContent || generateSkillsSelectionSection(doc, config, clientContext?.name);
895
- const skillsSection = skillsBaseContent + skillsGuidanceSuffix;
896
- const clientBlocks = generateClientBlocksSection(clientContext);
897
- const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
898
- const laneGuidance = generateLaneGuidance(doc.lane);
899
- // WU-1253: Try template for bug-discovery
900
- const bugDiscoverySection = templates.get('bug-discovery') || generateBugDiscoverySection(id);
901
- // WU-1253: Try template for constraints
902
- const constraints = templates.get('constraints') || generateConstraints(id);
903
- const implementationContext = generateImplementationContext(doc);
904
- // WU-2252: Generate invariants/prior-art section for code_paths
905
- const invariantsPriorArt = generateInvariantsPriorArtSection(codePaths);
906
- // WU-1986: Anthropic multi-agent best practices sections
907
- // WU-1253: Try templates for these sections
908
- const effortScaling = templates.get('effort-scaling') || generateEffortScalingRules();
909
- const parallelToolCalls = templates.get('parallel-tool-calls') || generateParallelToolCallGuidance();
910
- const searchHeuristics = templates.get('search-heuristics') || generateIterativeSearchHeuristics();
911
- const tokenBudget = templates.get('token-budget') || generateTokenBudgetAwareness(id);
912
- const completionFormat = generateCompletionFormat(id);
913
- // WU-1987: Agent coordination and quick fix sections
914
- const agentCoordination = generateAgentCoordinationSection(id);
915
- // WU-1253: Try template for quick-fix-commands
916
- const quickFix = templates.get('quick-fix-commands') || generateQuickFixCommands();
917
- // WU-2107: Lane selection guidance
918
- // WU-1253: Try template for lane-selection
919
- const laneSelection = templates.get('lane-selection') || generateLaneSelectionSection();
920
- // WU-2362: Worktree path guidance for sub-agents
921
- const worktreeGuidance = generateWorktreePathGuidance(doc.worktree_path);
922
- // WU-1134: Worktree block recovery guidance
923
- // WU-1253: Try template for worktree-recovery
924
- const worktreeBlockRecovery = templates.get('worktree-recovery') || generateWorktreeBlockRecoverySection(doc.worktree_path);
925
- // WU-1240: Memory context section
926
- // Include if explicitly enabled and not disabled via noContext
927
- const shouldIncludeMemoryContext = options.includeMemoryContext && !options.noContext;
928
- const memoryContextSection = shouldIncludeMemoryContext ? options.memoryContextContent || '' : '';
929
- // Generate thinking mode sections if applicable
930
- const executionModeSection = generateExecutionModeSection(options);
931
- const thinkToolGuidance = generateThinkToolGuidance(options);
932
- // Build optional sections string
933
- const thinkingSections = [executionModeSection, thinkToolGuidance]
934
- .filter((section) => section.length > 0)
935
- .join('\n\n---\n\n');
936
- const thinkingBlock = thinkingSections ? `${thinkingSections}\n\n---\n\n` : '';
937
- // Build the task prompt
938
- // WU-1131: Warning banner at start, end sentinel after constraints
939
- // WU-1142: Type-aware test guidance (TDD for code, format-only for docs, etc.)
940
- const taskPrompt = `${TRUNCATION_WARNING_BANNER}<task>
941
- ${preamble}
942
- </task>
943
-
944
- ---
945
-
946
- ${testGuidance}
947
-
948
- ---
949
-
950
- # ${id}: ${doc.title || 'Untitled'}
951
-
952
- ## WU Details
953
-
954
- - **ID:** ${id}
955
- - **Lane:** ${doc.lane || 'Unknown'}
956
- - **Type:** ${doc.type || 'feature'}
957
- - **Status:** ${doc.status || 'unknown'}
958
- - **Worktree:** ${doc.worktree_path || `worktrees/<lane>-${id.toLowerCase()}`}
959
-
960
- ## Description
961
-
962
- ${doc.description || 'No description provided.'}
963
-
964
- ## Acceptance Criteria
965
-
966
- ${formatAcceptance(doc.acceptance)}
967
-
968
- ## Code Paths
969
-
970
- ${codePaths.length > 0 ? codePaths.map((p) => `- ${p}`).join('\n') : '- No code paths defined'}
971
- ${mandatorySection}${invariantsPriorArt ? `---\n\n${invariantsPriorArt}\n\n` : ''}${implementationContext ? `---\n\n${implementationContext}\n\n` : ''}---
972
-
973
- ${thinkingBlock}${skillsSection}
974
- ${memoryContextSection ? `---\n\n${memoryContextSection}\n\n` : ''}---
975
-
976
- ${mandatoryStandards}
977
-
978
- ---
979
-
980
- ${enforcementSummary}
981
-
982
- ${clientBlocks ? `---\n\n${clientBlocks}\n\n` : ''}${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
983
-
984
- ${bugDiscoverySection}
985
-
986
- ---
987
-
988
- ${effortScaling}
989
-
990
- ---
991
-
992
- ${parallelToolCalls}
993
-
994
- ---
995
-
996
- ${searchHeuristics}
997
-
998
- ---
999
-
1000
- ${tokenBudget}
1001
-
1002
- ---
1003
-
1004
- ${completionFormat}
1005
-
1006
- ---
1007
-
1008
- ${agentCoordination}
1009
-
1010
- ---
1011
-
1012
- ${quickFix}
1013
-
1014
- ---
1015
-
1016
- ${laneSelection}
1017
-
1018
- ---
1019
-
1020
- ${laneGuidance}${laneGuidance ? '\n\n---\n\n' : ''}## Action
1021
-
1022
- ${generateActionSection(doc, id)}
1023
-
1024
- ---
1025
-
1026
- ${worktreeBlockRecovery}
1027
-
1028
- ${constraints}
1029
-
1030
- ${SPAWN_END_SENTINEL}`;
1031
- // Escape special characters for XML output
1032
- const escapedPrompt = taskPrompt
1033
- .replace(/&/g, '&amp;')
1034
- .replace(/</g, '&lt;')
1035
- .replace(/>/g, '&gt;');
1036
- // Build the Task tool invocation block using antml format
1037
- // Using array join to avoid XML parsing issues
1038
- const openTag = '<' + 'antml:invoke name="Task">';
1039
- const closeTag = '</' + 'antml:invoke>';
1040
- const paramOpen = '<' + 'antml:parameter name="';
1041
- const paramClose = '</' + 'antml:parameter>';
1042
- const invocation = [
1043
- '<' + 'antml:function_calls>',
1044
- openTag,
1045
- `${paramOpen}subagent_type">general-purpose${paramClose}`,
1046
- `${paramOpen}description">Execute ${id}${paramClose}`,
1047
- `${paramOpen}prompt">${escapedPrompt}${paramClose}`,
1048
- closeTag,
1049
- '</' + 'antml:function_calls>',
1050
- ].join('\n');
1051
- return invocation;
1052
- }
1053
- export function generateCodexPrompt(doc, id, strategy, options = {}) {
1054
- const codePaths = doc.code_paths || [];
1055
- const mandatoryAgents = detectMandatoryAgents(codePaths);
1056
- const preamble = generatePreamble(id, strategy);
1057
- // WU-1142: Use type-aware test guidance instead of hardcoded TDD directive
1058
- const testGuidance = generateTestGuidance(doc.type);
1059
- const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
1060
- const laneGuidance = generateLaneGuidance(doc.lane);
1061
- const bugDiscoverySection = generateBugDiscoverySection(id);
1062
- const implementationContext = generateImplementationContext(doc);
1063
- const action = generateActionSection(doc, id);
1064
- const constraints = generateCodexConstraints(id);
1065
- const clientContext = options.client;
1066
- const config = options.config || getConfig();
1067
- // WU-1142: Pass lane to get byLane skills
1068
- const clientSkillsGuidance = generateClientSkillsGuidance(clientContext, doc.lane);
1069
- const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
1070
- (clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
1071
- const clientBlocks = generateClientBlocksSection(clientContext);
1072
- const executionModeSection = generateExecutionModeSection(options);
1073
- const thinkToolGuidance = generateThinkToolGuidance(options);
1074
- const thinkingSections = [executionModeSection, thinkToolGuidance]
1075
- .filter((section) => section.length > 0)
1076
- .join('\n\n---\n\n');
1077
- const thinkingBlock = thinkingSections ? `${thinkingSections}\n\n---\n\n` : '';
1078
- // WU-1134: Worktree block recovery guidance
1079
- const worktreeBlockRecovery = generateWorktreeBlockRecoverySection(doc.worktree_path);
1080
- // WU-1240: Memory context section
1081
- const shouldIncludeMemoryContext = options.includeMemoryContext && !options.noContext;
1082
- const memoryContextSection = shouldIncludeMemoryContext ? options.memoryContextContent || '' : '';
1083
- // WU-1131: Warning banner at start, end sentinel after constraints
1084
- // WU-1142: Type-aware test guidance
1085
- return `${TRUNCATION_WARNING_BANNER}# ${id}: ${doc.title || 'Untitled'}
1086
-
1087
- ${testGuidance}
1088
-
1089
- ---
1090
-
1091
- ## Context
1092
-
1093
- ${preamble}
1094
-
1095
- ---
1096
-
1097
- ## WU Details
1098
-
1099
- - **ID:** ${id}
1100
- - **Lane:** ${doc.lane || 'Unknown'}
1101
- - **Type:** ${doc.type || 'feature'}
1102
- - **Status:** ${doc.status || 'unknown'}
1103
- - **Worktree:** ${doc.worktree_path || `worktrees/<lane>-${id.toLowerCase()}`}
1104
-
1105
- ## Description
1106
-
1107
- ${doc.description || 'No description provided.'}
1108
-
1109
- ## Scope (code_paths)
1110
-
1111
- Only change files within these paths:
1112
-
1113
- ${codePaths.length > 0 ? codePaths.map((p) => `- ${p}`).join('\n') : '- No code paths defined'}
1114
-
1115
- ## Acceptance Criteria
1116
-
1117
- ${formatAcceptance(doc.acceptance)}
1118
-
1119
- ---
1120
-
1121
- ${skillsSection}
1122
- ${memoryContextSection ? `---\n\n${memoryContextSection}\n\n` : ''}---
1123
-
1124
- ## Action
1125
-
1126
- ${action}
1127
-
1128
- ---
1129
-
1130
- ## Verification
1131
-
1132
- - Run in worktree: \`pnpm gates\`
1133
- - From shared checkout: \`node packages/@lumenflow/agent/dist/agent-verification.js ${id}\`
1134
-
1135
- ---
1136
-
1137
- ${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${clientBlocks ? `${clientBlocks}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
1138
-
1139
- ---
1140
-
1141
- ${laneGuidance}${laneGuidance ? '\n\n---\n\n' : ''}${worktreeBlockRecovery}
1142
-
1143
- ---
1144
-
1145
- ${constraints}
1146
-
1147
- ${SPAWN_END_SENTINEL}
1148
- `;
1149
- }
1150
- /**
1151
- * WU-1603: Check if a lane is currently occupied by another WU
1152
- * WU-1325: Now considers lock_policy - lanes with policy=none are never occupied
1153
- *
1154
- * @param {string} lane - Lane name (e.g., "Operations: Tooling")
1155
- * @returns {import('@lumenflow/core/lane-lock').LockMetadata|null} Lock metadata if occupied, null otherwise
1156
- */
1157
- export function checkLaneOccupation(lane) {
1158
- // WU-1325: Lanes with lock_policy=none never report as occupied
1159
- const lockPolicy = getLockPolicyForLane(lane);
1160
- if (lockPolicy === 'none') {
1161
- return null;
1162
- }
1163
- const lockStatus = checkLaneLock(lane);
1164
- if (lockStatus.locked && lockStatus.metadata) {
1165
- return lockStatus.metadata;
1166
- }
1167
- return null;
1168
- }
1169
- export function generateLaneOccupationWarning(lockMetadata, targetWuId, options = {}) {
1170
- const { isStale = false } = options;
1171
- let warning = `⚠️ Lane "${lockMetadata.lane}" is occupied by ${lockMetadata.wuId}\n`;
1172
- // WU-1346: Use injected values if provided, otherwise look up from config
1173
- const lockPolicy = options.lockPolicy ?? getLockPolicyForLane(lockMetadata.lane);
1174
- const wipLimit = options.wipLimit ?? getWipLimitForLane(lockMetadata.lane);
1175
- warning += ` This violates WIP=${wipLimit} (lock_policy=${lockPolicy}).\n\n`;
1176
- if (isStale) {
1177
- warning += ` ⏰ This lock is STALE (>24 hours old) - the WU may be abandoned.\n`;
1178
- warning += ` Consider using pnpm wu:block --id ${lockMetadata.wuId} if work is stalled.\n\n`;
1179
- }
1180
- warning += ` Options:\n`;
1181
- warning += ` 1. Wait for ${lockMetadata.wuId} to complete or block\n`;
1182
- warning += ` 2. Choose a different lane for ${targetWuId}\n`;
1183
- warning += ` 3. Block ${lockMetadata.wuId} if work is stalled: pnpm wu:block --id ${lockMetadata.wuId}`;
1184
- return warning;
1185
- }
1186
- /**
1187
- * Emit prompt output and optionally persist parent/child lineage.
1188
- *
1189
- * WU-1604: Prompt generation (wu:brief) is side-effect free.
1190
- * Only explicit delegation mode records lineage intent.
1191
- */
1192
- export async function emitSpawnOutputWithRegistry(options, dependencies = {}) {
1193
- const { id, output, isCodexClient, parentWu, lane, recordDelegationIntent = false, logPrefix: prefix = BRIEF_LOG_PREFIX, } = options;
1194
- const log = dependencies.log ?? console.log;
1195
- const recordSpawn = dependencies.recordSpawn ?? recordSpawnToRegistry;
1196
- const formatSpawnMessage = dependencies.formatSpawnMessage ?? formatSpawnRecordedMessage;
1197
- if (isCodexClient) {
1198
- log(`${prefix} Generated Codex/GPT prompt for ${id}`);
1199
- log(`${prefix} Copy the Markdown below:\n`);
1200
- log(output.trimEnd());
1201
- }
1202
- else {
1203
- log(`${prefix} Generated Task tool invocation for ${id}`);
1204
- log(`${prefix} Copy the block below to spawn a sub-agent:\n`);
1205
- log(output);
1206
- }
1207
- if (!recordDelegationIntent || !parentWu) {
1208
- return;
1209
- }
1210
- const registryResult = await recordSpawn({
1211
- parentWuId: parentWu,
1212
- targetWuId: id,
1213
- lane: lane || 'Unknown',
1214
- baseDir: '.lumenflow/state',
1215
- });
1216
- const registryMessage = formatSpawnMessage(registryResult.spawnId, registryResult.error);
1217
- log(`\n${registryMessage}`);
1218
- }
1219
- const BRIEF_PARSER_CONFIG = {
1220
- name: 'wu-brief',
1221
- description: 'Generate handoff prompt for sub-agent WU execution',
1222
- };
1223
- const DELEGATE_PARSER_CONFIG = {
1224
- name: 'wu-delegate',
1225
- description: 'Generate delegation prompt and record explicit lineage intent',
1226
- };
1227
- function parseAndValidateArgs(parserConfig = BRIEF_PARSER_CONFIG) {
1228
- const args = createWUParser({
1229
- name: parserConfig.name,
1230
- description: parserConfig.description,
1231
- options: [
1232
- WU_OPTIONS.id,
1233
- WU_OPTIONS.thinking,
1234
- WU_OPTIONS.noThinking,
1235
- WU_OPTIONS.budget,
1236
- WU_OPTIONS.codex,
1237
- WU_OPTIONS.parentWu, // WU-1945: Parent WU for spawn registry tracking
1238
- WU_OPTIONS.client,
1239
- WU_OPTIONS.vendor,
1240
- WU_OPTIONS.noContext, // WU-1240: Skip memory context injection
1241
- ],
1242
- required: ['id'],
1243
- allowPositionalId: true,
1244
- });
1245
- // Validate thinking mode options
1246
- try {
1247
- validateSpawnArgs(args);
1248
- }
1249
- catch (e) {
1250
- die(e.message);
1251
- }
1252
- return args;
1253
- }
1254
- /**
1255
- * Load and validate WU document from YAML file
1256
- */
1257
- function loadWUDocument(id, wuPath) {
1258
- // Check if WU file exists
1259
- if (!existsSync(wuPath)) {
1260
- die(`WU file not found: ${wuPath}\n\n` +
1261
- `Cannot spawn a sub-agent for a WU that doesn't exist.\n\n` +
1262
- `Options:\n` +
1263
- ` 1. Create the WU first: pnpm wu:create --id ${id} --lane <lane> --title "..."\n` +
1264
- ` 2. Check if the WU ID is correct`);
1265
- }
1266
- // Read WU file
1267
- let text;
1268
- try {
1269
- text = readFileSync(wuPath, { encoding: FILE_SYSTEM.UTF8 });
1270
- }
1271
- catch (e) {
1272
- die(`Failed to read WU file: ${wuPath}\n\n` +
1273
- `Error: ${e.message}\n\n` +
1274
- `Options:\n` +
1275
- ` 1. Check file permissions: ls -la ${wuPath}\n` +
1276
- ` 2. Ensure the file exists and is readable`);
1277
- }
1278
- // Parse YAML
1279
- try {
1280
- return parseYAML(text);
1281
- }
1282
- catch (e) {
1283
- die(`Failed to parse WU YAML ${wuPath}\n\n` +
1284
- `Error: ${e.message}\n\n` +
1285
- `Options:\n` +
1286
- ` 1. Validate YAML syntax: pnpm wu:validate --id ${id}\n` +
1287
- ` 2. Fix YAML errors manually and retry`);
1288
- }
1289
- }
1290
- /**
1291
- * Resolve the client name from args and config
1292
- */
1293
- function resolveClientName(args, config, logPrefix = BRIEF_LOG_PREFIX) {
1294
- let clientName = args.client;
1295
- if (!clientName && args.vendor) {
1296
- console.warn(`${logPrefix} ${EMOJI.WARNING} Warning: --vendor is deprecated. Use --client.`);
1297
- clientName = args.vendor;
1298
- }
1299
- // Codex handling (deprecated legacy flag)
1300
- if (args.codex && !clientName) {
1301
- console.warn(`${logPrefix} ${EMOJI.WARNING} Warning: --codex is deprecated. Use --client codex-cli.`);
1302
- clientName = 'codex-cli';
1303
- }
1304
- return clientName || config.agents.defaultClient || 'claude-code';
1305
- }
1306
- /**
1307
- * Check lane occupation and warn if occupied by a different WU
1308
- */
1309
- async function checkAndWarnLaneOccupation(lane, id, logPrefix = BRIEF_LOG_PREFIX) {
1310
- if (!lane)
1311
- return;
1312
- const existingLock = checkLaneOccupation(lane);
1313
- if (existingLock && existingLock.wuId !== id) {
1314
- // Lane is occupied by a different WU
1315
- const { isLockStale } = await import('@lumenflow/core/lane-lock');
1316
- const isStale = isLockStale(existingLock);
1317
- const warning = generateLaneOccupationWarning(existingLock, id, { isStale });
1318
- console.warn(`${logPrefix} ${EMOJI.WARNING}\n${warning}\n`);
1319
- }
1320
- }
1321
- /**
1322
- * Shared entry point for wu:brief and wu:delegate.
1323
- */
1324
- export async function runBriefLogic(options = {}) {
1325
- const { mode = 'brief', parserConfig = mode === 'delegate' ? DELEGATE_PARSER_CONFIG : BRIEF_PARSER_CONFIG, logPrefix = BRIEF_LOG_PREFIX, } = options;
1326
- const args = parseAndValidateArgs(parserConfig);
1327
- const explicitDelegation = mode === 'delegate';
1328
- const effectiveLogPrefix = explicitDelegation ? DELEGATE_LOG_PREFIX : logPrefix;
1329
- // WU-2202: Validate dependencies BEFORE any other operation
1330
- // This prevents false lane occupancy reports when yaml package is missing
1331
- const commandLabel = explicitDelegation ? 'wu:delegate' : 'wu:brief';
1332
- const depResult = await validateSpawnDependencies();
1333
- if (!depResult.valid) {
1334
- die(formatDependencyError(commandLabel, depResult.missing));
1335
- }
1336
- if (explicitDelegation && !args.parentWu) {
1337
- die('wu:delegate requires --parent-wu to record delegation lineage intent.\n\n' +
1338
- 'Example:\n' +
1339
- ' pnpm wu:delegate --id WU-123 --parent-wu WU-100 --client claude-code');
1340
- }
1341
- if (!explicitDelegation && args.parentWu) {
1342
- console.warn(`${effectiveLogPrefix} ${EMOJI.WARNING} --parent-wu does not record lineage in generation-only mode.`);
1343
- console.warn(`${effectiveLogPrefix} ${EMOJI.WARNING} Use wu:delegate for explicit, side-effectful delegation intent tracking.`);
1344
- console.warn('');
1345
- }
1346
- const id = args.id.toUpperCase();
1347
- if (!PATTERNS.WU_ID.test(id)) {
1348
- die(`Invalid WU id '${args.id}'. Expected format WU-123`);
1349
- }
1350
- const wuPath = WU_PATHS.WU(id);
1351
- const doc = loadWUDocument(id, wuPath);
1352
- // Warn if WU is not in ready or in_progress status
1353
- const validStatuses = [WU_STATUS.READY, WU_STATUS.IN_PROGRESS];
1354
- if (!validStatuses.includes(doc.status)) {
1355
- console.warn(`${effectiveLogPrefix} ${EMOJI.WARNING} Warning: ${id} has status '${doc.status}'.`);
1356
- console.warn(`${effectiveLogPrefix} ${EMOJI.WARNING} Sub-agents typically work on ready or in_progress WUs.`);
1357
- console.warn('');
1358
- }
1359
- // WU-1603: Check if lane is already occupied and warn
1360
- await checkAndWarnLaneOccupation(doc.lane, id, effectiveLogPrefix);
1361
- // Build thinking mode options for task invocation
1362
- const thinkingOptions = {
1363
- thinking: args.thinking,
1364
- noThinking: args.noThinking,
1365
- budget: args.budget,
1366
- };
1367
- // Client Resolution
1368
- const config = getConfig();
1369
- const clientName = resolveClientName(args, config, effectiveLogPrefix);
1370
- // WU-1240: Generate memory context if not skipped
1371
- const baseDir = process.cwd();
1372
- let memoryContextContent = '';
1373
- const shouldIncludeMemoryContext = !args.noContext;
1374
- if (shouldIncludeMemoryContext) {
1375
- const isMemoryInitialized = await checkMemoryLayerInitialized(baseDir);
1376
- if (isMemoryInitialized) {
1377
- const maxSize = getMemoryContextMaxSize(config);
1378
- memoryContextContent = await generateMemoryContextSection(baseDir, {
1379
- wuId: id,
1380
- lane: doc.lane,
1381
- maxSize,
1382
- });
1383
- if (memoryContextContent) {
1384
- console.log(`${effectiveLogPrefix} Memory context loaded (${memoryContextContent.length} bytes)`);
1385
- }
1386
- }
1387
- }
1388
- // Create strategy
1389
- const strategy = SpawnStrategyFactory.create(clientName);
1390
- const clientContext = { name: clientName, config: resolveClientConfig(config, clientName) };
1391
- const isCodexClient = clientName === 'codex-cli' || args.codex;
1392
- if (isCodexClient) {
1393
- const prompt = generateCodexPrompt(doc, id, strategy, {
1394
- ...thinkingOptions,
1395
- client: clientContext,
1396
- config,
1397
- });
1398
- await emitSpawnOutputWithRegistry({
1399
- id,
1400
- output: prompt,
1401
- isCodexClient: true,
1402
- parentWu: args.parentWu,
1403
- lane: doc.lane,
1404
- recordDelegationIntent: explicitDelegation,
1405
- logPrefix: effectiveLogPrefix,
1406
- });
1407
- return;
1408
- }
1409
- // Generate and output the Task invocation
1410
- const invocation = generateTaskInvocation(doc, id, strategy, {
1411
- ...thinkingOptions,
1412
- client: clientContext,
1413
- config,
1414
- // WU-1240: Include memory context in spawn prompt
1415
- baseDir,
1416
- includeMemoryContext: shouldIncludeMemoryContext && memoryContextContent.length > 0,
1417
- memoryContextContent,
1418
- noContext: args.noContext,
1419
- });
1420
- await emitSpawnOutputWithRegistry({
1421
- id,
1422
- output: invocation,
1423
- isCodexClient: false,
1424
- parentWu: args.parentWu,
1425
- lane: doc.lane,
1426
- recordDelegationIntent: explicitDelegation,
1427
- logPrefix: effectiveLogPrefix,
1428
- });
1429
- }
37
+ // ─── Re-exports from prompt builders ───
38
+ export { TRUNCATION_WARNING_BANNER, SPAWN_END_SENTINEL, generateTestGuidance, generateAgentCoordinationSection, generateTaskInvocation, generateCodexPrompt, generateEffortScalingRules, generateParallelToolCallGuidance, generateIterativeSearchHeuristics, generateTokenBudgetAwareness, generateCompletionFormat, generateQuickFixCommands, generateWorktreeBlockRecoverySection, generateLaneSelectionSection, generateWorktreePathGuidance, generateActionSection, generateCompletionWorkflowSection, } from './wu-spawn-prompt-builders.js';
39
+ // ─── Re-exports from strategy resolver ───
40
+ export { checkLaneOccupation, generateLaneOccupationWarning, emitSpawnOutputWithRegistry, runBriefLogic, } from './wu-spawn-strategy-resolver.js';
1430
41
  /**
1431
42
  * Main entry point for removed wu:spawn command.
1432
43
  *