@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.
- package/README.md +44 -42
- package/dist/agent-session.js +1 -1
- package/dist/agent-session.js.map +1 -1
- package/dist/commands/integrate.js +1 -0
- package/dist/commands/integrate.js.map +1 -1
- package/dist/commands.js +1 -0
- package/dist/commands.js.map +1 -1
- package/dist/delegation-list.js +140 -0
- package/dist/delegation-list.js.map +1 -0
- package/dist/docs-sync.js +1 -0
- package/dist/docs-sync.js.map +1 -1
- package/dist/doctor.js +36 -99
- package/dist/doctor.js.map +1 -1
- package/dist/gates-plan-resolvers.js +150 -0
- package/dist/gates-plan-resolvers.js.map +1 -0
- package/dist/gates-runners.js +533 -0
- package/dist/gates-runners.js.map +1 -0
- package/dist/gates-types.js +3 -0
- package/dist/gates-types.js.map +1 -1
- package/dist/gates-utils.js +316 -0
- package/dist/gates-utils.js.map +1 -0
- package/dist/gates.js +44 -1016
- package/dist/gates.js.map +1 -1
- package/dist/hooks/enforcement-generator.js +16 -880
- package/dist/hooks/enforcement-generator.js.map +1 -1
- package/dist/hooks/enforcement-sync.js +6 -5
- package/dist/hooks/enforcement-sync.js.map +1 -1
- package/dist/hooks/generators/auto-checkpoint.js +123 -0
- package/dist/hooks/generators/auto-checkpoint.js.map +1 -0
- package/dist/hooks/generators/enforce-worktree.js +188 -0
- package/dist/hooks/generators/enforce-worktree.js.map +1 -0
- package/dist/hooks/generators/index.js +16 -0
- package/dist/hooks/generators/index.js.map +1 -0
- package/dist/hooks/generators/pre-compact-checkpoint.js +134 -0
- package/dist/hooks/generators/pre-compact-checkpoint.js.map +1 -0
- package/dist/hooks/generators/require-wu.js +115 -0
- package/dist/hooks/generators/require-wu.js.map +1 -0
- package/dist/hooks/generators/session-start-recovery.js +101 -0
- package/dist/hooks/generators/session-start-recovery.js.map +1 -0
- package/dist/hooks/generators/signal-utils.js +52 -0
- package/dist/hooks/generators/signal-utils.js.map +1 -0
- package/dist/hooks/generators/warn-incomplete.js +65 -0
- package/dist/hooks/generators/warn-incomplete.js.map +1 -0
- package/dist/init-detection.js +228 -0
- package/dist/init-detection.js.map +1 -0
- package/dist/init-scaffolding.js +146 -0
- package/dist/init-scaffolding.js.map +1 -0
- package/dist/init-templates.js +1928 -0
- package/dist/init-templates.js.map +1 -0
- package/dist/init.js +137 -2425
- package/dist/init.js.map +1 -1
- package/dist/initiative-edit.js +42 -11
- package/dist/initiative-edit.js.map +1 -1
- package/dist/initiative-remove-wu.js +0 -0
- package/dist/initiative-status.js +29 -2
- package/dist/initiative-status.js.map +1 -1
- package/dist/mem-context.js +22 -9
- package/dist/mem-context.js.map +1 -1
- package/dist/orchestrate-init-status.js +32 -1
- package/dist/orchestrate-init-status.js.map +1 -1
- package/dist/orchestrate-initiative.js +2 -2
- package/dist/orchestrate-initiative.js.map +1 -1
- package/dist/orchestrate-monitor.js +38 -38
- package/dist/orchestrate-monitor.js.map +1 -1
- package/dist/plan-link.js +7 -14
- package/dist/plan-link.js.map +1 -1
- package/dist/public-manifest.js +19 -5
- package/dist/public-manifest.js.map +1 -1
- package/dist/shared-validators.js +1 -0
- package/dist/shared-validators.js.map +1 -1
- package/dist/spawn-list.js +0 -0
- package/dist/sync-templates.js +2 -1
- package/dist/sync-templates.js.map +1 -1
- package/dist/wu-claim-branch.js +121 -0
- package/dist/wu-claim-branch.js.map +1 -0
- package/dist/wu-claim-output.js +83 -0
- package/dist/wu-claim-output.js.map +1 -0
- package/dist/wu-claim-resume-handler.js +85 -0
- package/dist/wu-claim-resume-handler.js.map +1 -0
- package/dist/wu-claim-state.js +572 -0
- package/dist/wu-claim-state.js.map +1 -0
- package/dist/wu-claim-validation.js +439 -0
- package/dist/wu-claim-validation.js.map +1 -0
- package/dist/wu-claim-worktree.js +221 -0
- package/dist/wu-claim-worktree.js.map +1 -0
- package/dist/wu-claim.js +96 -1394
- package/dist/wu-claim.js.map +1 -1
- package/dist/wu-code-path-coverage.js +81 -0
- package/dist/wu-code-path-coverage.js.map +1 -0
- package/dist/wu-create-content.js +256 -0
- package/dist/wu-create-content.js.map +1 -0
- package/dist/wu-create-readiness.js +57 -0
- package/dist/wu-create-readiness.js.map +1 -0
- package/dist/wu-create-validation.js +124 -0
- package/dist/wu-create-validation.js.map +1 -0
- package/dist/wu-create.js +45 -442
- package/dist/wu-create.js.map +1 -1
- package/dist/wu-done.js +151 -249
- package/dist/wu-done.js.map +1 -1
- package/dist/wu-edit-operations.js +401 -0
- package/dist/wu-edit-operations.js.map +1 -0
- package/dist/wu-edit-validators.js +280 -0
- package/dist/wu-edit-validators.js.map +1 -0
- package/dist/wu-edit.js +43 -759
- package/dist/wu-edit.js.map +1 -1
- package/dist/wu-prep.js +43 -127
- package/dist/wu-prep.js.map +1 -1
- package/dist/wu-repair.js +1 -1
- package/dist/wu-repair.js.map +1 -1
- package/dist/wu-sandbox.js +253 -0
- package/dist/wu-sandbox.js.map +1 -0
- package/dist/wu-spawn-prompt-builders.js +1124 -0
- package/dist/wu-spawn-prompt-builders.js.map +1 -0
- package/dist/wu-spawn-strategy-resolver.js +319 -0
- package/dist/wu-spawn-strategy-resolver.js.map +1 -0
- package/dist/wu-spawn.js +9 -1398
- package/dist/wu-spawn.js.map +1 -1
- package/dist/wu-status.js +4 -0
- package/dist/wu-status.js.map +1 -1
- package/dist/wu-validate.js +1 -1
- package/dist/wu-validate.js.map +1 -1
- package/package.json +15 -11
- package/templates/core/LUMENFLOW.md.template +29 -99
- package/templates/core/UPGRADING.md.template +2 -2
- package/templates/core/ai/onboarding/agent-invocation-guide.md.template +1 -1
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +29 -4
- package/templates/core/ai/onboarding/release-process.md.template +1 -1
- 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
|
-
|
|
38
|
-
|
|
39
|
-
//
|
|
40
|
-
|
|
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, '&')
|
|
1034
|
-
.replace(/</g, '<')
|
|
1035
|
-
.replace(/>/g, '>');
|
|
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
|
*
|