@lumenflow/core 1.0.0 → 1.3.2
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/dist/arg-parser.d.ts +6 -0
- package/dist/arg-parser.js +57 -1
- package/dist/backlog-generator.js +1 -1
- package/dist/backlog-sync-validator.js +3 -3
- package/dist/branch-check.d.ts +21 -0
- package/dist/branch-check.js +77 -0
- package/dist/cli/is-agent-branch.d.ts +11 -0
- package/dist/cli/is-agent-branch.js +15 -0
- package/dist/code-paths-overlap.js +2 -2
- package/dist/error-handler.d.ts +1 -0
- package/dist/error-handler.js +4 -1
- package/dist/git-adapter.d.ts +23 -0
- package/dist/git-adapter.js +38 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/lane-checker.d.ts +36 -3
- package/dist/lane-checker.js +128 -17
- package/dist/lane-inference.js +3 -4
- package/dist/lumenflow-config-schema.d.ts +125 -0
- package/dist/lumenflow-config-schema.js +76 -0
- package/dist/lumenflow-home.d.ts +130 -0
- package/dist/lumenflow-home.js +208 -0
- package/dist/manual-test-validator.js +1 -1
- package/dist/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- package/dist/orphan-detector.d.ts +16 -0
- package/dist/orphan-detector.js +24 -0
- package/dist/path-classifiers.d.ts +1 -1
- package/dist/path-classifiers.js +1 -1
- package/dist/rebase-artifact-cleanup.d.ts +17 -0
- package/dist/rebase-artifact-cleanup.js +49 -8
- package/dist/spawn-strategy.d.ts +53 -0
- package/dist/spawn-strategy.js +106 -0
- package/dist/spec-branch-helpers.d.ts +118 -0
- package/dist/spec-branch-helpers.js +192 -0
- package/dist/stamp-utils.d.ts +10 -0
- package/dist/stamp-utils.js +17 -19
- package/dist/token-counter.js +2 -2
- package/dist/wu-consistency-checker.d.ts +2 -0
- package/dist/wu-consistency-checker.js +40 -6
- package/dist/wu-constants.d.ts +98 -3
- package/dist/wu-constants.js +108 -3
- package/dist/wu-create-validators.d.ts +40 -2
- package/dist/wu-create-validators.js +76 -2
- package/dist/wu-done-branch-only.js +9 -0
- package/dist/wu-done-branch-utils.d.ts +10 -0
- package/dist/wu-done-branch-utils.js +31 -0
- package/dist/wu-done-cleanup.d.ts +8 -0
- package/dist/wu-done-cleanup.js +122 -0
- package/dist/wu-done-docs-generate.d.ts +73 -0
- package/dist/wu-done-docs-generate.js +108 -0
- package/dist/wu-done-docs-only.d.ts +20 -0
- package/dist/wu-done-docs-only.js +65 -0
- package/dist/wu-done-errors.d.ts +17 -0
- package/dist/wu-done-errors.js +24 -0
- package/dist/wu-done-inputs.d.ts +12 -0
- package/dist/wu-done-inputs.js +51 -0
- package/dist/wu-done-metadata.d.ts +100 -0
- package/dist/wu-done-metadata.js +193 -0
- package/dist/wu-done-paths.d.ts +69 -0
- package/dist/wu-done-paths.js +237 -0
- package/dist/wu-done-preflight.d.ts +48 -0
- package/dist/wu-done-preflight.js +185 -0
- package/dist/wu-done-validation.d.ts +82 -0
- package/dist/wu-done-validation.js +340 -0
- package/dist/wu-done-validators.d.ts +13 -409
- package/dist/wu-done-validators.js +9 -1225
- package/dist/wu-done-worktree.d.ts +0 -1
- package/dist/wu-done-worktree.js +24 -30
- package/dist/wu-schema.js +4 -4
- package/dist/wu-spawn-skills.d.ts +19 -0
- package/dist/wu-spawn-skills.js +148 -0
- package/dist/wu-spawn.d.ts +17 -4
- package/dist/wu-spawn.js +113 -177
- package/dist/wu-validation.d.ts +1 -0
- package/dist/wu-validation.js +21 -1
- package/dist/wu-validator.d.ts +51 -0
- package/dist/wu-validator.js +108 -0
- package/package.json +12 -8
package/dist/wu-spawn.js
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*
|
|
12
12
|
* Output:
|
|
13
13
|
* A complete Task tool invocation block with:
|
|
14
|
-
* - Context loading preamble (CLAUDE
|
|
14
|
+
* - Context loading preamble (.claude/CLAUDE.md, README, lumenflow, WU YAML)
|
|
15
15
|
* - WU details and acceptance criteria
|
|
16
16
|
* - Skills Selection section (sub-agent reads catalogue and selects at runtime)
|
|
17
17
|
* - Mandatory agent advisory
|
|
@@ -25,9 +25,10 @@
|
|
|
25
25
|
* Codex Mode:
|
|
26
26
|
* When --codex is used, outputs a Codex/GPT-friendly Markdown prompt (no antml/XML escaping).
|
|
27
27
|
*
|
|
28
|
-
* @see {@link
|
|
28
|
+
* @see {@link docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-invocation-guide.md} - Context loading templates
|
|
29
29
|
*/
|
|
30
30
|
import { existsSync, readFileSync } from 'node:fs';
|
|
31
|
+
import { fileURLToPath } from 'node:url';
|
|
31
32
|
import path from 'node:path';
|
|
32
33
|
import { createWUParser, WU_OPTIONS } from './arg-parser.js';
|
|
33
34
|
import { WU_PATHS } from './wu-paths.js';
|
|
@@ -37,6 +38,9 @@ import { WU_STATUS, PATTERNS, EMOJI } from './wu-constants.js';
|
|
|
37
38
|
// WU-1603: Check lane lock status before spawning
|
|
38
39
|
import { checkLaneLock } from './lane-lock.js';
|
|
39
40
|
import { minimatch } from 'minimatch';
|
|
41
|
+
import { SpawnStrategyFactory } from './spawn-strategy.js';
|
|
42
|
+
import { getConfig } from './lumenflow-config.js';
|
|
43
|
+
import { generateClientSkillsGuidance, generateSkillsSelectionSection, resolveClientConfig, } from './wu-spawn-skills.js';
|
|
40
44
|
// WU-2252: Import invariants loader for spawn output injection
|
|
41
45
|
import { loadInvariants, INVARIANT_TYPES } from './invariants-runner.js';
|
|
42
46
|
import { validateSpawnArgs, generateExecutionModeSection, generateThinkToolGuidance, recordSpawnToRegistry, formatSpawnRecordedMessage, } from './wu-spawn-helpers.js';
|
|
@@ -51,27 +55,6 @@ const MANDATORY_TRIGGERS = {
|
|
|
51
55
|
'beacon-guardian': ['**/prompts/**', '**/classification/**', '**/detector/**', '**/llm/**'],
|
|
52
56
|
};
|
|
53
57
|
const LOG_PREFIX = '[wu:spawn]';
|
|
54
|
-
/** @type {string} */
|
|
55
|
-
const AGENTS_DIR = '.claude/agents';
|
|
56
|
-
/**
|
|
57
|
-
* Load skills configured in agent's frontmatter
|
|
58
|
-
*
|
|
59
|
-
* @param {string} agentName - Agent name (e.g., 'general-purpose')
|
|
60
|
-
* @returns {string[]} Array of skill names or empty array if not found
|
|
61
|
-
*/
|
|
62
|
-
function loadAgentConfiguredSkills(agentName) {
|
|
63
|
-
const agentPath = `${AGENTS_DIR}/${agentName}.md`;
|
|
64
|
-
if (!existsSync(agentPath)) {
|
|
65
|
-
return [];
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const content = readFileSync(agentPath, { encoding: 'utf-8' });
|
|
69
|
-
return []; // Removed: vendor-specific skill loading
|
|
70
|
-
}
|
|
71
|
-
catch {
|
|
72
|
-
return [];
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
58
|
/**
|
|
76
59
|
* Detect mandatory agents based on code paths.
|
|
77
60
|
*
|
|
@@ -106,6 +89,9 @@ function formatAcceptance(acceptance) {
|
|
|
106
89
|
/**
|
|
107
90
|
* Format spec_refs as markdown links
|
|
108
91
|
*
|
|
92
|
+
* WU-1062: Handles external paths (lumenflow://, ~/.lumenflow/, $LUMENFLOW_HOME/)
|
|
93
|
+
* by expanding them to absolute paths and adding a note about reading them.
|
|
94
|
+
*
|
|
109
95
|
* @param {string[]|undefined} specRefs - Spec references array
|
|
110
96
|
* @returns {string} Formatted references or empty string if none
|
|
111
97
|
*/
|
|
@@ -113,7 +99,17 @@ function formatSpecRefs(specRefs) {
|
|
|
113
99
|
if (!specRefs || specRefs.length === 0) {
|
|
114
100
|
return '';
|
|
115
101
|
}
|
|
116
|
-
|
|
102
|
+
const formattedRefs = specRefs.map((ref) => {
|
|
103
|
+
// WU-1062: Add note for external paths
|
|
104
|
+
if (ref.startsWith('lumenflow://') ||
|
|
105
|
+
ref.startsWith('~/') ||
|
|
106
|
+
ref.startsWith('$LUMENFLOW_HOME') ||
|
|
107
|
+
(ref.startsWith('/') && ref.includes('.lumenflow'))) {
|
|
108
|
+
return `- ${ref} (external - read with filesystem access)`;
|
|
109
|
+
}
|
|
110
|
+
return `- ${ref}`;
|
|
111
|
+
});
|
|
112
|
+
return formattedRefs.join('\n');
|
|
117
113
|
}
|
|
118
114
|
/**
|
|
119
115
|
* Format risks as markdown list
|
|
@@ -320,27 +316,15 @@ function generateTDDDirective() {
|
|
|
320
316
|
* @param {string} id - WU ID
|
|
321
317
|
* @returns {string} Context loading preamble
|
|
322
318
|
*/
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
Before running wu:claim, check docs/04-operations/tasks/status.md to ensure the lane is free.
|
|
334
|
-
Only ONE WU can be in_progress per lane at any time.
|
|
335
|
-
|
|
336
|
-
## Context Recovery (Session Resumption)
|
|
337
|
-
|
|
338
|
-
Before starting work, check for prior context from previous sessions:
|
|
339
|
-
|
|
340
|
-
1. \`pnpm mem:ready --wu ${id}\` — Query pending nodes (what's next?)
|
|
341
|
-
2. \`pnpm mem:inbox --wu ${id}\` — Check coordination signals from parallel agents
|
|
342
|
-
|
|
343
|
-
If prior context exists, resume from the last checkpoint. Otherwise, proceed with the task below.`;
|
|
319
|
+
/**
|
|
320
|
+
* Generate the context loading preamble using the strategy
|
|
321
|
+
*
|
|
322
|
+
* @param {string} id - WU ID
|
|
323
|
+
* @param {import('./spawn-strategy.js').SpawnStrategy} strategy - Client strategy
|
|
324
|
+
* @returns {string} Context loading preamble
|
|
325
|
+
*/
|
|
326
|
+
function generatePreamble(id, strategy) {
|
|
327
|
+
return strategy.getPreamble(id);
|
|
344
328
|
}
|
|
345
329
|
/**
|
|
346
330
|
* Generate the constraints block (appended at end per Lost in the Middle research)
|
|
@@ -740,7 +724,7 @@ pnpm mem:triage --wu ${id} # List discoveries for this WU
|
|
|
740
724
|
pnpm mem:triage --promote <node-id> --lane "<lane>" # Create Bug WU (human action)
|
|
741
725
|
\`\`\`
|
|
742
726
|
|
|
743
|
-
See:
|
|
727
|
+
See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-invocation-guide.md §Bug Discovery`;
|
|
744
728
|
}
|
|
745
729
|
/**
|
|
746
730
|
* Generate lane-specific guidance
|
|
@@ -809,128 +793,37 @@ Then implement following all standards above.
|
|
|
809
793
|
- Lane lock acquisition (WIP=1 enforcement)
|
|
810
794
|
- Session tracking for context recovery`;
|
|
811
795
|
}
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
* If an agentName is provided, that agent's configured skills (from frontmatter)
|
|
820
|
-
* are auto-loaded at the top.
|
|
821
|
-
*
|
|
822
|
-
* @param {object} doc - WU YAML document
|
|
823
|
-
* @param {string} [agentName='general-purpose'] - Agent to spawn
|
|
824
|
-
* @returns {string} Skills Selection section
|
|
825
|
-
*/
|
|
826
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity -- WU-2025: Pre-existing complexity, refactor tracked
|
|
827
|
-
function generateSkillsSection(doc, agentName = 'general-purpose') {
|
|
828
|
-
const lane = doc.lane || '';
|
|
829
|
-
const type = doc.type || 'feature';
|
|
830
|
-
const laneParent = lane.split(':')[0].trim();
|
|
831
|
-
// Load agent's configured skills from frontmatter
|
|
832
|
-
const agentSkills = loadAgentConfiguredSkills(agentName);
|
|
833
|
-
const hasAgentSkills = agentSkills.length > 0;
|
|
834
|
-
// Build auto-load section if agent has configured skills
|
|
835
|
-
const autoLoadSection = hasAgentSkills
|
|
836
|
-
? `### Auto-Loaded Skills (from ${agentName} agent config)
|
|
837
|
-
|
|
838
|
-
These skills are pre-configured for this agent and should be loaded first:
|
|
839
|
-
|
|
840
|
-
${agentSkills.map((s) => `- \`${s}\` — Load via \`/skill ${s}\``).join('\n')}
|
|
841
|
-
|
|
842
|
-
`
|
|
843
|
-
: '';
|
|
844
|
-
// Build context hints for the sub-agent
|
|
845
|
-
const contextHints = [];
|
|
846
|
-
// Universal baselines (only if not already in agent skills)
|
|
847
|
-
if (!agentSkills.includes('wu-lifecycle')) {
|
|
848
|
-
contextHints.push('- `wu-lifecycle` — ALL WUs need workflow automation');
|
|
849
|
-
}
|
|
850
|
-
if (!agentSkills.includes('worktree-discipline')) {
|
|
851
|
-
contextHints.push('- `worktree-discipline` — ALL WUs need path safety');
|
|
852
|
-
}
|
|
853
|
-
// Type-based hints
|
|
854
|
-
if ((type === 'feature' || type === 'enhancement') && !agentSkills.includes('tdd-workflow')) {
|
|
855
|
-
contextHints.push('- `tdd-workflow` — TDD is mandatory for feature/enhancement WUs');
|
|
856
|
-
}
|
|
857
|
-
if (type === 'bug' && !agentSkills.includes('bug-classification')) {
|
|
858
|
-
contextHints.push('- `bug-classification` — Bug severity assessment');
|
|
859
|
-
}
|
|
860
|
-
// Lane-based hints
|
|
861
|
-
if (laneParent === 'Operations' &&
|
|
862
|
-
lane.includes('Tooling') &&
|
|
863
|
-
!agentSkills.includes('lumenflow-gates')) {
|
|
864
|
-
contextHints.push('- `lumenflow-gates` — Tooling often affects gates');
|
|
865
|
-
}
|
|
866
|
-
if (laneParent === 'Intelligence') {
|
|
867
|
-
if (!agentSkills.includes('beacon-compliance')) {
|
|
868
|
-
contextHints.push('- `beacon-compliance` — Intelligence lane requires Beacon validation');
|
|
869
|
-
}
|
|
870
|
-
if (!agentSkills.includes('prompt-management')) {
|
|
871
|
-
contextHints.push('- `prompt-management` — For prompt template work');
|
|
872
|
-
}
|
|
873
|
-
}
|
|
874
|
-
if (laneParent === 'Experience' && !agentSkills.includes('frontend-design')) {
|
|
875
|
-
contextHints.push('- `frontend-design` — For UI component work');
|
|
876
|
-
}
|
|
877
|
-
const softPolicySection = contextHints.length > 0
|
|
878
|
-
? `### Soft Policy (baselines for this WU)
|
|
879
|
-
|
|
880
|
-
Based on WU context, consider loading:
|
|
881
|
-
|
|
882
|
-
${contextHints.join('\n')}
|
|
883
|
-
|
|
884
|
-
`
|
|
885
|
-
: '';
|
|
886
|
-
return `## Skills Selection
|
|
887
|
-
|
|
888
|
-
**IMPORTANT**: Before starting work, select and load relevant skills.
|
|
889
|
-
|
|
890
|
-
${autoLoadSection}### How to Select Skills
|
|
891
|
-
|
|
892
|
-
1. Read the skill catalogue frontmatter from \`.claude/skills/*/SKILL.md\`
|
|
893
|
-
2. Match skills to WU context (lane, type, code_paths, description)
|
|
894
|
-
3. Load selected skills via \`/skill <skill-name>\`
|
|
895
|
-
|
|
896
|
-
${softPolicySection}### Additional Skills (load if needed)
|
|
897
|
-
|
|
898
|
-
| Skill | Use When |
|
|
899
|
-
|-------|----------|
|
|
900
|
-
| lumenflow-gates | Gates fail, debugging format/lint/typecheck errors |
|
|
901
|
-
| bug-classification | Bug discovered mid-WU, need priority classification |
|
|
902
|
-
| beacon-compliance | Code touches LLM, prompts, classification |
|
|
903
|
-
| prompt-management | Working with prompt templates, golden datasets |
|
|
904
|
-
| frontend-design | Building UI components, pages |
|
|
905
|
-
| initiative-management | Multi-phase projects, INIT-XXX coordination |
|
|
906
|
-
| multi-agent-coordination | Spawning sub-agents, parallel WU work |
|
|
907
|
-
| orchestration | Agent coordination, mandatory agent checks |
|
|
908
|
-
| ops-maintenance | Metrics, validation, health checks |
|
|
909
|
-
|
|
910
|
-
### Graceful Degradation
|
|
911
|
-
|
|
912
|
-
If the skill catalogue is missing or invalid:
|
|
913
|
-
- Load baseline skills: \`/skill wu-lifecycle\`, \`/skill tdd-workflow\` (for features)
|
|
914
|
-
- Continue with implementation using Mandatory Standards below
|
|
915
|
-
`;
|
|
796
|
+
function generateClientBlocksSection(clientContext) {
|
|
797
|
+
if (!clientContext?.config?.blocks?.length)
|
|
798
|
+
return '';
|
|
799
|
+
const blocks = clientContext.config.blocks
|
|
800
|
+
.map((block) => `### ${block.title}\n\n${block.content}`)
|
|
801
|
+
.join('\n\n');
|
|
802
|
+
return `## Client Guidance (${clientContext.name})\n\n${blocks}`;
|
|
916
803
|
}
|
|
917
804
|
/**
|
|
918
805
|
* Generate the complete Task tool invocation
|
|
919
806
|
*
|
|
920
807
|
* @param {object} doc - WU YAML document
|
|
921
808
|
* @param {string} id - WU ID
|
|
809
|
+
* @param {SpawnStrategy} strategy - Client strategy
|
|
922
810
|
* @param {object} [options={}] - Thinking mode options
|
|
923
811
|
* @param {boolean} [options.thinking] - Whether extended thinking is enabled
|
|
924
812
|
* @param {boolean} [options.noThinking] - Whether thinking is explicitly disabled
|
|
925
813
|
* @param {string} [options.budget] - Token budget for thinking
|
|
926
814
|
* @returns {string} Complete Task tool invocation
|
|
927
815
|
*/
|
|
928
|
-
export function generateTaskInvocation(doc, id, options = {}) {
|
|
816
|
+
export function generateTaskInvocation(doc, id, strategy, options = {}) {
|
|
929
817
|
const codePaths = doc.code_paths || [];
|
|
930
818
|
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
931
|
-
const preamble = generatePreamble(id);
|
|
819
|
+
const preamble = generatePreamble(id, strategy);
|
|
932
820
|
const tddDirective = generateTDDDirective();
|
|
933
|
-
const
|
|
821
|
+
const clientContext = options.client;
|
|
822
|
+
const config = options.config || getConfig();
|
|
823
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
824
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
|
|
825
|
+
(clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
|
|
826
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
934
827
|
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
935
828
|
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
936
829
|
const bugDiscoverySection = generateBugDiscoverySection(id);
|
|
@@ -1009,7 +902,7 @@ ${thinkingBlock}${skillsSection}
|
|
|
1009
902
|
- **Documentation**: Update tooling docs if changing tools. Keep docs in sync with code
|
|
1010
903
|
- **Sub-agents**: Use Explore agent for codebase investigation. Activate mandatory agents (security-auditor for PHI/auth, beacon-guardian for LLM/prompts)
|
|
1011
904
|
|
|
1012
|
-
${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
|
|
905
|
+
${clientBlocks ? `---\n\n${clientBlocks}\n\n` : ''}${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
|
|
1013
906
|
|
|
1014
907
|
${bugDiscoverySection}
|
|
1015
908
|
|
|
@@ -1074,10 +967,10 @@ ${constraints}`;
|
|
|
1074
967
|
].join('\n');
|
|
1075
968
|
return invocation;
|
|
1076
969
|
}
|
|
1077
|
-
export function generateCodexPrompt(doc, id, options = {}) {
|
|
970
|
+
export function generateCodexPrompt(doc, id, strategy, options = {}) {
|
|
1078
971
|
const codePaths = doc.code_paths || [];
|
|
1079
972
|
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
1080
|
-
const preamble = generatePreamble(id);
|
|
973
|
+
const preamble = generatePreamble(id, strategy);
|
|
1081
974
|
const tddDirective = generateTDDDirective();
|
|
1082
975
|
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
1083
976
|
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
@@ -1085,6 +978,12 @@ export function generateCodexPrompt(doc, id, options = {}) {
|
|
|
1085
978
|
const implementationContext = generateImplementationContext(doc);
|
|
1086
979
|
const action = generateActionSection(doc, id);
|
|
1087
980
|
const constraints = generateCodexConstraints(id);
|
|
981
|
+
const clientContext = options.client;
|
|
982
|
+
const config = options.config || getConfig();
|
|
983
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
984
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
|
|
985
|
+
(clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
|
|
986
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
1088
987
|
const executionModeSection = generateExecutionModeSection(options);
|
|
1089
988
|
const thinkToolGuidance = generateThinkToolGuidance(options);
|
|
1090
989
|
const thinkingSections = [executionModeSection, thinkToolGuidance]
|
|
@@ -1127,6 +1026,10 @@ ${formatAcceptance(doc.acceptance)}
|
|
|
1127
1026
|
|
|
1128
1027
|
---
|
|
1129
1028
|
|
|
1029
|
+
${skillsSection}
|
|
1030
|
+
|
|
1031
|
+
---
|
|
1032
|
+
|
|
1130
1033
|
## Action
|
|
1131
1034
|
|
|
1132
1035
|
${action}
|
|
@@ -1140,7 +1043,7 @@ ${action}
|
|
|
1140
1043
|
|
|
1141
1044
|
---
|
|
1142
1045
|
|
|
1143
|
-
${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
|
|
1046
|
+
${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${clientBlocks ? `${clientBlocks}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
|
|
1144
1047
|
|
|
1145
1048
|
---
|
|
1146
1049
|
|
|
@@ -1196,12 +1099,19 @@ async function main() {
|
|
|
1196
1099
|
name: 'wu-spawn',
|
|
1197
1100
|
description: 'Generate Task tool invocation for sub-agent WU execution',
|
|
1198
1101
|
options: [
|
|
1102
|
+
WU_OPTIONS.id,
|
|
1103
|
+
WU_OPTIONS.thinking,
|
|
1104
|
+
WU_OPTIONS.noThinking,
|
|
1105
|
+
WU_OPTIONS.budget,
|
|
1106
|
+
WU_OPTIONS.codex,
|
|
1199
1107
|
WU_OPTIONS.id,
|
|
1200
1108
|
WU_OPTIONS.thinking,
|
|
1201
1109
|
WU_OPTIONS.noThinking,
|
|
1202
1110
|
WU_OPTIONS.budget,
|
|
1203
1111
|
WU_OPTIONS.codex,
|
|
1204
1112
|
WU_OPTIONS.parentWu, // WU-1945: Parent WU for spawn registry tracking
|
|
1113
|
+
WU_OPTIONS.client,
|
|
1114
|
+
WU_OPTIONS.vendor,
|
|
1205
1115
|
],
|
|
1206
1116
|
required: ['id'],
|
|
1207
1117
|
allowPositionalId: true,
|
|
@@ -1274,33 +1184,59 @@ async function main() {
|
|
|
1274
1184
|
noThinking: args.noThinking,
|
|
1275
1185
|
budget: args.budget,
|
|
1276
1186
|
};
|
|
1187
|
+
// Client Resolution
|
|
1188
|
+
const config = getConfig();
|
|
1189
|
+
let clientName = args.client;
|
|
1190
|
+
if (!clientName && args.vendor) {
|
|
1191
|
+
console.warn(`${LOG_PREFIX} ${EMOJI.WARNING} Warning: --vendor is deprecated. Use --client.`);
|
|
1192
|
+
clientName = args.vendor;
|
|
1193
|
+
}
|
|
1194
|
+
// Codex handling (deprecated legacy flag)
|
|
1277
1195
|
if (args.codex) {
|
|
1278
|
-
|
|
1196
|
+
if (!clientName) {
|
|
1197
|
+
console.warn(`${LOG_PREFIX} ${EMOJI.WARNING} Warning: --codex is deprecated. Use --client codex-cli.`);
|
|
1198
|
+
clientName = 'codex-cli';
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
if (!clientName) {
|
|
1202
|
+
clientName = config.agents.defaultClient || 'claude-code';
|
|
1203
|
+
}
|
|
1204
|
+
// Create strategy
|
|
1205
|
+
const strategy = SpawnStrategyFactory.create(clientName);
|
|
1206
|
+
const clientContext = { name: clientName, config: resolveClientConfig(config, clientName) };
|
|
1207
|
+
if (clientName === 'codex-cli' || args.codex) {
|
|
1208
|
+
const prompt = generateCodexPrompt(doc, id, strategy, {
|
|
1209
|
+
...thinkingOptions,
|
|
1210
|
+
client: clientContext,
|
|
1211
|
+
config,
|
|
1212
|
+
});
|
|
1279
1213
|
console.log(`${LOG_PREFIX} Generated Codex/GPT prompt for ${id}`);
|
|
1280
1214
|
console.log(`${LOG_PREFIX} Copy the Markdown below:\n`);
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
console.log(`${LOG_PREFIX} Copy the block below to spawn a sub-agent:\n`);
|
|
1288
|
-
console.log(invocation);
|
|
1289
|
-
// WU-1945: Record spawn event to registry (non-blocking)
|
|
1290
|
-
// Only record if --parent-wu is provided (orchestrator context)
|
|
1291
|
-
if (args.parentWu) {
|
|
1292
|
-
const registryResult = await recordSpawnToRegistry({
|
|
1293
|
-
parentWuId: args.parentWu,
|
|
1294
|
-
targetWuId: id,
|
|
1295
|
-
lane: doc.lane || 'Unknown',
|
|
1296
|
-
baseDir: '.beacon/state',
|
|
1215
|
+
// ...
|
|
1216
|
+
// Generate and output the Task invocation
|
|
1217
|
+
const invocation = generateTaskInvocation(doc, id, strategy, {
|
|
1218
|
+
...thinkingOptions,
|
|
1219
|
+
client: clientContext,
|
|
1220
|
+
config,
|
|
1297
1221
|
});
|
|
1298
|
-
|
|
1299
|
-
console.log(
|
|
1222
|
+
console.log(`${LOG_PREFIX} Generated Task tool invocation for ${id}`);
|
|
1223
|
+
console.log(`${LOG_PREFIX} Copy the block below to spawn a sub-agent:\n`);
|
|
1224
|
+
console.log(invocation);
|
|
1225
|
+
// WU-1945: Record spawn event to registry (non-blocking)
|
|
1226
|
+
// Only record if --parent-wu is provided (orchestrator context)
|
|
1227
|
+
if (args.parentWu) {
|
|
1228
|
+
const registryResult = await recordSpawnToRegistry({
|
|
1229
|
+
parentWuId: args.parentWu,
|
|
1230
|
+
targetWuId: id,
|
|
1231
|
+
lane: doc.lane || 'Unknown',
|
|
1232
|
+
baseDir: '.beacon/state',
|
|
1233
|
+
});
|
|
1234
|
+
const registryMessage = formatSpawnRecordedMessage(registryResult.spawnId, registryResult.error);
|
|
1235
|
+
console.log(`\n${registryMessage}`);
|
|
1236
|
+
}
|
|
1300
1237
|
}
|
|
1301
1238
|
}
|
|
1302
1239
|
// Guard main() for testability
|
|
1303
|
-
import { fileURLToPath } from 'node:url';
|
|
1304
1240
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
1305
1241
|
main();
|
|
1306
1242
|
}
|
package/dist/wu-validation.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* @see {@link tools/lib/wu-schema.mjs} - WU schema with exposure field
|
|
14
14
|
* @see {@link tools/lib/wu-constants.mjs} - WU_EXPOSURE values
|
|
15
15
|
*/
|
|
16
|
+
export declare function resolveExposureDefault(lane: any): "documentation" | "backend-only";
|
|
16
17
|
/**
|
|
17
18
|
* Warning message templates with remediation guidance.
|
|
18
19
|
* All messages include the WU ID for context.
|
package/dist/wu-validation.js
CHANGED
|
@@ -14,6 +14,26 @@
|
|
|
14
14
|
* @see {@link tools/lib/wu-constants.mjs} - WU_EXPOSURE values
|
|
15
15
|
*/
|
|
16
16
|
import { WU_EXPOSURE } from './wu-constants.js';
|
|
17
|
+
const LANE_PARENTS = {
|
|
18
|
+
CONTENT: 'content',
|
|
19
|
+
FRAMEWORK: 'framework',
|
|
20
|
+
OPERATIONS: 'operations',
|
|
21
|
+
};
|
|
22
|
+
function getLaneParent(lane) {
|
|
23
|
+
if (!lane || typeof lane !== 'string')
|
|
24
|
+
return '';
|
|
25
|
+
return lane.split(':')[0].trim().toLowerCase();
|
|
26
|
+
}
|
|
27
|
+
export function resolveExposureDefault(lane) {
|
|
28
|
+
const parent = getLaneParent(lane);
|
|
29
|
+
if (parent === LANE_PARENTS.CONTENT) {
|
|
30
|
+
return WU_EXPOSURE.DOCUMENTATION;
|
|
31
|
+
}
|
|
32
|
+
if (parent === LANE_PARENTS.FRAMEWORK || parent === LANE_PARENTS.OPERATIONS) {
|
|
33
|
+
return WU_EXPOSURE.BACKEND_ONLY;
|
|
34
|
+
}
|
|
35
|
+
return undefined;
|
|
36
|
+
}
|
|
17
37
|
/**
|
|
18
38
|
* UI verification keywords to search for in acceptance criteria.
|
|
19
39
|
* Case-insensitive patterns that indicate the acceptance criteria
|
|
@@ -99,7 +119,7 @@ export function validateExposure(wu, options = {}) {
|
|
|
99
119
|
return { valid: true, warnings: [] };
|
|
100
120
|
}
|
|
101
121
|
const wuId = wu.id || 'WU-???';
|
|
102
|
-
const exposure = wu.exposure;
|
|
122
|
+
const exposure = wu.exposure || resolveExposureDefault(wu.lane);
|
|
103
123
|
// Check 1: exposure field presence
|
|
104
124
|
if (!exposure) {
|
|
105
125
|
warnings.push(EXPOSURE_WARNING_MESSAGES.MISSING_EXPOSURE(wuId));
|
package/dist/wu-validator.d.ts
CHANGED
|
@@ -60,3 +60,54 @@ export declare function validateWUCodePaths(codePaths: any, options?: ValidateWU
|
|
|
60
60
|
errors: any[];
|
|
61
61
|
warnings: any[];
|
|
62
62
|
};
|
|
63
|
+
/**
|
|
64
|
+
* Result of placeholder validation
|
|
65
|
+
*/
|
|
66
|
+
export interface PlaceholderValidationResult {
|
|
67
|
+
/** Whether validation passed (no placeholders found) */
|
|
68
|
+
valid: boolean;
|
|
69
|
+
/** List of validation errors */
|
|
70
|
+
errors: string[];
|
|
71
|
+
/** Fields that contain placeholders */
|
|
72
|
+
fieldsWithPlaceholders: string[];
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* WU-1025: Validate that WU spec content does not contain PLACEHOLDER markers
|
|
76
|
+
*
|
|
77
|
+
* Used by wu:create (for inline content) and wu:claim (for full spec).
|
|
78
|
+
* Provides clear error messages telling the user which fields need to be fixed.
|
|
79
|
+
*
|
|
80
|
+
* @param {object} spec - WU spec content to validate
|
|
81
|
+
* @param {string} [spec.description] - WU description
|
|
82
|
+
* @param {string[]|object} [spec.acceptance] - Acceptance criteria (array or object)
|
|
83
|
+
* @returns {PlaceholderValidationResult} Validation result with errors and affected fields
|
|
84
|
+
*
|
|
85
|
+
* @example
|
|
86
|
+
* // wu:create validation (inline content only)
|
|
87
|
+
* const result = validateNoPlaceholders({ description: args.description });
|
|
88
|
+
* if (!result.valid) {
|
|
89
|
+
* die(`Cannot create WU:\n${result.errors.join('\n')}`);
|
|
90
|
+
* }
|
|
91
|
+
*
|
|
92
|
+
* @example
|
|
93
|
+
* // wu:claim validation (full spec)
|
|
94
|
+
* const result = validateNoPlaceholders(wuDoc);
|
|
95
|
+
* if (!result.valid) {
|
|
96
|
+
* die(`Cannot claim WU:\n${result.errors.join('\n')}`);
|
|
97
|
+
* }
|
|
98
|
+
*/
|
|
99
|
+
export declare function validateNoPlaceholders(spec: {
|
|
100
|
+
description?: string;
|
|
101
|
+
acceptance?: string[] | Record<string, string[]>;
|
|
102
|
+
}): PlaceholderValidationResult;
|
|
103
|
+
/**
|
|
104
|
+
* WU-1025: Build error message for placeholder validation failure
|
|
105
|
+
*
|
|
106
|
+
* Creates a user-friendly error message with actionable guidance.
|
|
107
|
+
*
|
|
108
|
+
* @param {string} command - Command that failed ('wu:create' or 'wu:claim')
|
|
109
|
+
* @param {PlaceholderValidationResult} result - Validation result
|
|
110
|
+
* @param {string} [wuId] - WU ID (for wu:claim error messages)
|
|
111
|
+
* @returns {string} Formatted error message
|
|
112
|
+
*/
|
|
113
|
+
export declare function buildPlaceholderErrorMessage(command: string, result: PlaceholderValidationResult, wuId?: string): string;
|
package/dist/wu-validator.js
CHANGED
|
@@ -14,6 +14,7 @@ import { readFileSync, existsSync } from 'node:fs';
|
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
import { execSync } from 'node:child_process';
|
|
16
16
|
import { STDIO } from './wu-constants.js';
|
|
17
|
+
import { PLACEHOLDER_SENTINEL } from './wu-schema.js';
|
|
17
18
|
/**
|
|
18
19
|
* Check if a file path is a test file
|
|
19
20
|
* @param {string} filePath - Path to check
|
|
@@ -323,3 +324,110 @@ function formatMockFindings(findings) {
|
|
|
323
324
|
msg += '\nVerify these are actual implementations, not placeholder code.';
|
|
324
325
|
return msg;
|
|
325
326
|
}
|
|
327
|
+
/**
|
|
328
|
+
* WU-1025: Validate that WU spec content does not contain PLACEHOLDER markers
|
|
329
|
+
*
|
|
330
|
+
* Used by wu:create (for inline content) and wu:claim (for full spec).
|
|
331
|
+
* Provides clear error messages telling the user which fields need to be fixed.
|
|
332
|
+
*
|
|
333
|
+
* @param {object} spec - WU spec content to validate
|
|
334
|
+
* @param {string} [spec.description] - WU description
|
|
335
|
+
* @param {string[]|object} [spec.acceptance] - Acceptance criteria (array or object)
|
|
336
|
+
* @returns {PlaceholderValidationResult} Validation result with errors and affected fields
|
|
337
|
+
*
|
|
338
|
+
* @example
|
|
339
|
+
* // wu:create validation (inline content only)
|
|
340
|
+
* const result = validateNoPlaceholders({ description: args.description });
|
|
341
|
+
* if (!result.valid) {
|
|
342
|
+
* die(`Cannot create WU:\n${result.errors.join('\n')}`);
|
|
343
|
+
* }
|
|
344
|
+
*
|
|
345
|
+
* @example
|
|
346
|
+
* // wu:claim validation (full spec)
|
|
347
|
+
* const result = validateNoPlaceholders(wuDoc);
|
|
348
|
+
* if (!result.valid) {
|
|
349
|
+
* die(`Cannot claim WU:\n${result.errors.join('\n')}`);
|
|
350
|
+
* }
|
|
351
|
+
*/
|
|
352
|
+
export function validateNoPlaceholders(spec) {
|
|
353
|
+
const errors = [];
|
|
354
|
+
const fieldsWithPlaceholders = [];
|
|
355
|
+
// Check description
|
|
356
|
+
if (spec.description && spec.description.includes(PLACEHOLDER_SENTINEL)) {
|
|
357
|
+
fieldsWithPlaceholders.push('description');
|
|
358
|
+
errors.push(`Description contains ${PLACEHOLDER_SENTINEL} marker.\n` +
|
|
359
|
+
` Fix: Replace placeholder text with actual description.\n` +
|
|
360
|
+
` Example: --description "Implement X feature to enable Y functionality"`);
|
|
361
|
+
}
|
|
362
|
+
// Check acceptance criteria (supports both array and object formats)
|
|
363
|
+
if (spec.acceptance) {
|
|
364
|
+
const hasPlaceholder = checkForPlaceholderInAcceptance(spec.acceptance);
|
|
365
|
+
if (hasPlaceholder) {
|
|
366
|
+
fieldsWithPlaceholders.push('acceptance');
|
|
367
|
+
errors.push(`Acceptance criteria contain ${PLACEHOLDER_SENTINEL} markers.\n` +
|
|
368
|
+
` Fix: Replace placeholder text with actual acceptance criteria.\n` +
|
|
369
|
+
` Example: --acceptance "Feature X works as expected" --acceptance "Tests pass"`);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return {
|
|
373
|
+
valid: errors.length === 0,
|
|
374
|
+
errors,
|
|
375
|
+
fieldsWithPlaceholders,
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
/**
|
|
379
|
+
* Helper: Recursively check acceptance criteria for placeholder markers
|
|
380
|
+
*
|
|
381
|
+
* Supports both formats:
|
|
382
|
+
* - Flat array: ["criterion 1", "criterion 2"]
|
|
383
|
+
* - Nested object: { functional: ["criterion"], technical: ["criterion"] }
|
|
384
|
+
*
|
|
385
|
+
* @param {string[]|object} acceptance - Acceptance criteria
|
|
386
|
+
* @returns {boolean} True if any placeholder found
|
|
387
|
+
*/
|
|
388
|
+
function checkForPlaceholderInAcceptance(acceptance) {
|
|
389
|
+
if (Array.isArray(acceptance)) {
|
|
390
|
+
return acceptance.some((item) => typeof item === 'string' && item.includes(PLACEHOLDER_SENTINEL));
|
|
391
|
+
}
|
|
392
|
+
if (typeof acceptance === 'object' && acceptance !== null) {
|
|
393
|
+
return Object.values(acceptance).some((value) => {
|
|
394
|
+
if (Array.isArray(value)) {
|
|
395
|
+
return value.some((item) => typeof item === 'string' && item.includes(PLACEHOLDER_SENTINEL));
|
|
396
|
+
}
|
|
397
|
+
return false;
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
/**
|
|
403
|
+
* WU-1025: Build error message for placeholder validation failure
|
|
404
|
+
*
|
|
405
|
+
* Creates a user-friendly error message with actionable guidance.
|
|
406
|
+
*
|
|
407
|
+
* @param {string} command - Command that failed ('wu:create' or 'wu:claim')
|
|
408
|
+
* @param {PlaceholderValidationResult} result - Validation result
|
|
409
|
+
* @param {string} [wuId] - WU ID (for wu:claim error messages)
|
|
410
|
+
* @returns {string} Formatted error message
|
|
411
|
+
*/
|
|
412
|
+
export function buildPlaceholderErrorMessage(command, result, wuId) {
|
|
413
|
+
const header = command === 'wu:create'
|
|
414
|
+
? `Cannot create WU with placeholder markers`
|
|
415
|
+
: `Cannot claim ${wuId || 'WU'} - spec contains placeholder markers`;
|
|
416
|
+
const fieldsText = result.fieldsWithPlaceholders.join(', ');
|
|
417
|
+
let message = `
|
|
418
|
+
❌ ${header}
|
|
419
|
+
|
|
420
|
+
Fields with ${PLACEHOLDER_SENTINEL} markers: ${fieldsText}
|
|
421
|
+
|
|
422
|
+
${result.errors.join('\n\n')}
|
|
423
|
+
`;
|
|
424
|
+
if (command === 'wu:claim' && wuId) {
|
|
425
|
+
message += `
|
|
426
|
+
To fix, edit the WU spec:
|
|
427
|
+
pnpm wu:edit --id ${wuId} --description "..." --acceptance "..."
|
|
428
|
+
|
|
429
|
+
Or manually edit: docs/04-operations/tasks/wu/${wuId}.yaml
|
|
430
|
+
`;
|
|
431
|
+
}
|
|
432
|
+
return message;
|
|
433
|
+
}
|