@lumenflow/core 1.0.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arg-parser.js +31 -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 +16 -0
- package/dist/git-adapter.js +23 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -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/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- 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/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.js +5 -5
- package/dist/wu-constants.d.ts +21 -3
- package/dist/wu-constants.js +28 -3
- 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-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 +12 -30
- package/dist/wu-schema.js +1 -3
- 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 +99 -176
- 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 +11 -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
|
*
|
|
@@ -320,27 +303,15 @@ function generateTDDDirective() {
|
|
|
320
303
|
* @param {string} id - WU ID
|
|
321
304
|
* @returns {string} Context loading preamble
|
|
322
305
|
*/
|
|
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.`;
|
|
306
|
+
/**
|
|
307
|
+
* Generate the context loading preamble using the strategy
|
|
308
|
+
*
|
|
309
|
+
* @param {string} id - WU ID
|
|
310
|
+
* @param {import('./spawn-strategy.js').SpawnStrategy} strategy - Client strategy
|
|
311
|
+
* @returns {string} Context loading preamble
|
|
312
|
+
*/
|
|
313
|
+
function generatePreamble(id, strategy) {
|
|
314
|
+
return strategy.getPreamble(id);
|
|
344
315
|
}
|
|
345
316
|
/**
|
|
346
317
|
* Generate the constraints block (appended at end per Lost in the Middle research)
|
|
@@ -740,7 +711,7 @@ pnpm mem:triage --wu ${id} # List discoveries for this WU
|
|
|
740
711
|
pnpm mem:triage --promote <node-id> --lane "<lane>" # Create Bug WU (human action)
|
|
741
712
|
\`\`\`
|
|
742
713
|
|
|
743
|
-
See:
|
|
714
|
+
See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-invocation-guide.md §Bug Discovery`;
|
|
744
715
|
}
|
|
745
716
|
/**
|
|
746
717
|
* Generate lane-specific guidance
|
|
@@ -809,128 +780,37 @@ Then implement following all standards above.
|
|
|
809
780
|
- Lane lock acquisition (WIP=1 enforcement)
|
|
810
781
|
- Session tracking for context recovery`;
|
|
811
782
|
}
|
|
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
|
-
`;
|
|
783
|
+
function generateClientBlocksSection(clientContext) {
|
|
784
|
+
if (!clientContext?.config?.blocks?.length)
|
|
785
|
+
return '';
|
|
786
|
+
const blocks = clientContext.config.blocks
|
|
787
|
+
.map((block) => `### ${block.title}\n\n${block.content}`)
|
|
788
|
+
.join('\n\n');
|
|
789
|
+
return `## Client Guidance (${clientContext.name})\n\n${blocks}`;
|
|
916
790
|
}
|
|
917
791
|
/**
|
|
918
792
|
* Generate the complete Task tool invocation
|
|
919
793
|
*
|
|
920
794
|
* @param {object} doc - WU YAML document
|
|
921
795
|
* @param {string} id - WU ID
|
|
796
|
+
* @param {SpawnStrategy} strategy - Client strategy
|
|
922
797
|
* @param {object} [options={}] - Thinking mode options
|
|
923
798
|
* @param {boolean} [options.thinking] - Whether extended thinking is enabled
|
|
924
799
|
* @param {boolean} [options.noThinking] - Whether thinking is explicitly disabled
|
|
925
800
|
* @param {string} [options.budget] - Token budget for thinking
|
|
926
801
|
* @returns {string} Complete Task tool invocation
|
|
927
802
|
*/
|
|
928
|
-
export function generateTaskInvocation(doc, id, options = {}) {
|
|
803
|
+
export function generateTaskInvocation(doc, id, strategy, options = {}) {
|
|
929
804
|
const codePaths = doc.code_paths || [];
|
|
930
805
|
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
931
|
-
const preamble = generatePreamble(id);
|
|
806
|
+
const preamble = generatePreamble(id, strategy);
|
|
932
807
|
const tddDirective = generateTDDDirective();
|
|
933
|
-
const
|
|
808
|
+
const clientContext = options.client;
|
|
809
|
+
const config = options.config || getConfig();
|
|
810
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
811
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
|
|
812
|
+
(clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
|
|
813
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
934
814
|
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
935
815
|
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
936
816
|
const bugDiscoverySection = generateBugDiscoverySection(id);
|
|
@@ -1009,7 +889,7 @@ ${thinkingBlock}${skillsSection}
|
|
|
1009
889
|
- **Documentation**: Update tooling docs if changing tools. Keep docs in sync with code
|
|
1010
890
|
- **Sub-agents**: Use Explore agent for codebase investigation. Activate mandatory agents (security-auditor for PHI/auth, beacon-guardian for LLM/prompts)
|
|
1011
891
|
|
|
1012
|
-
${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
|
|
892
|
+
${clientBlocks ? `---\n\n${clientBlocks}\n\n` : ''}${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
|
|
1013
893
|
|
|
1014
894
|
${bugDiscoverySection}
|
|
1015
895
|
|
|
@@ -1074,10 +954,10 @@ ${constraints}`;
|
|
|
1074
954
|
].join('\n');
|
|
1075
955
|
return invocation;
|
|
1076
956
|
}
|
|
1077
|
-
export function generateCodexPrompt(doc, id, options = {}) {
|
|
957
|
+
export function generateCodexPrompt(doc, id, strategy, options = {}) {
|
|
1078
958
|
const codePaths = doc.code_paths || [];
|
|
1079
959
|
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
1080
|
-
const preamble = generatePreamble(id);
|
|
960
|
+
const preamble = generatePreamble(id, strategy);
|
|
1081
961
|
const tddDirective = generateTDDDirective();
|
|
1082
962
|
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
1083
963
|
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
@@ -1085,6 +965,12 @@ export function generateCodexPrompt(doc, id, options = {}) {
|
|
|
1085
965
|
const implementationContext = generateImplementationContext(doc);
|
|
1086
966
|
const action = generateActionSection(doc, id);
|
|
1087
967
|
const constraints = generateCodexConstraints(id);
|
|
968
|
+
const clientContext = options.client;
|
|
969
|
+
const config = options.config || getConfig();
|
|
970
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
971
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
|
|
972
|
+
(clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
|
|
973
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
1088
974
|
const executionModeSection = generateExecutionModeSection(options);
|
|
1089
975
|
const thinkToolGuidance = generateThinkToolGuidance(options);
|
|
1090
976
|
const thinkingSections = [executionModeSection, thinkToolGuidance]
|
|
@@ -1127,6 +1013,10 @@ ${formatAcceptance(doc.acceptance)}
|
|
|
1127
1013
|
|
|
1128
1014
|
---
|
|
1129
1015
|
|
|
1016
|
+
${skillsSection}
|
|
1017
|
+
|
|
1018
|
+
---
|
|
1019
|
+
|
|
1130
1020
|
## Action
|
|
1131
1021
|
|
|
1132
1022
|
${action}
|
|
@@ -1140,7 +1030,7 @@ ${action}
|
|
|
1140
1030
|
|
|
1141
1031
|
---
|
|
1142
1032
|
|
|
1143
|
-
${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
|
|
1033
|
+
${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${clientBlocks ? `${clientBlocks}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
|
|
1144
1034
|
|
|
1145
1035
|
---
|
|
1146
1036
|
|
|
@@ -1196,12 +1086,19 @@ async function main() {
|
|
|
1196
1086
|
name: 'wu-spawn',
|
|
1197
1087
|
description: 'Generate Task tool invocation for sub-agent WU execution',
|
|
1198
1088
|
options: [
|
|
1089
|
+
WU_OPTIONS.id,
|
|
1090
|
+
WU_OPTIONS.thinking,
|
|
1091
|
+
WU_OPTIONS.noThinking,
|
|
1092
|
+
WU_OPTIONS.budget,
|
|
1093
|
+
WU_OPTIONS.codex,
|
|
1199
1094
|
WU_OPTIONS.id,
|
|
1200
1095
|
WU_OPTIONS.thinking,
|
|
1201
1096
|
WU_OPTIONS.noThinking,
|
|
1202
1097
|
WU_OPTIONS.budget,
|
|
1203
1098
|
WU_OPTIONS.codex,
|
|
1204
1099
|
WU_OPTIONS.parentWu, // WU-1945: Parent WU for spawn registry tracking
|
|
1100
|
+
WU_OPTIONS.client,
|
|
1101
|
+
WU_OPTIONS.vendor,
|
|
1205
1102
|
],
|
|
1206
1103
|
required: ['id'],
|
|
1207
1104
|
allowPositionalId: true,
|
|
@@ -1274,33 +1171,59 @@ async function main() {
|
|
|
1274
1171
|
noThinking: args.noThinking,
|
|
1275
1172
|
budget: args.budget,
|
|
1276
1173
|
};
|
|
1174
|
+
// Client Resolution
|
|
1175
|
+
const config = getConfig();
|
|
1176
|
+
let clientName = args.client;
|
|
1177
|
+
if (!clientName && args.vendor) {
|
|
1178
|
+
console.warn(`${LOG_PREFIX} ${EMOJI.WARNING} Warning: --vendor is deprecated. Use --client.`);
|
|
1179
|
+
clientName = args.vendor;
|
|
1180
|
+
}
|
|
1181
|
+
// Codex handling (deprecated legacy flag)
|
|
1277
1182
|
if (args.codex) {
|
|
1278
|
-
|
|
1183
|
+
if (!clientName) {
|
|
1184
|
+
console.warn(`${LOG_PREFIX} ${EMOJI.WARNING} Warning: --codex is deprecated. Use --client codex-cli.`);
|
|
1185
|
+
clientName = 'codex-cli';
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
if (!clientName) {
|
|
1189
|
+
clientName = config.agents.defaultClient || 'claude-code';
|
|
1190
|
+
}
|
|
1191
|
+
// Create strategy
|
|
1192
|
+
const strategy = SpawnStrategyFactory.create(clientName);
|
|
1193
|
+
const clientContext = { name: clientName, config: resolveClientConfig(config, clientName) };
|
|
1194
|
+
if (clientName === 'codex-cli' || args.codex) {
|
|
1195
|
+
const prompt = generateCodexPrompt(doc, id, strategy, {
|
|
1196
|
+
...thinkingOptions,
|
|
1197
|
+
client: clientContext,
|
|
1198
|
+
config,
|
|
1199
|
+
});
|
|
1279
1200
|
console.log(`${LOG_PREFIX} Generated Codex/GPT prompt for ${id}`);
|
|
1280
1201
|
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',
|
|
1202
|
+
// ...
|
|
1203
|
+
// Generate and output the Task invocation
|
|
1204
|
+
const invocation = generateTaskInvocation(doc, id, strategy, {
|
|
1205
|
+
...thinkingOptions,
|
|
1206
|
+
client: clientContext,
|
|
1207
|
+
config,
|
|
1297
1208
|
});
|
|
1298
|
-
|
|
1299
|
-
console.log(
|
|
1209
|
+
console.log(`${LOG_PREFIX} Generated Task tool invocation for ${id}`);
|
|
1210
|
+
console.log(`${LOG_PREFIX} Copy the block below to spawn a sub-agent:\n`);
|
|
1211
|
+
console.log(invocation);
|
|
1212
|
+
// WU-1945: Record spawn event to registry (non-blocking)
|
|
1213
|
+
// Only record if --parent-wu is provided (orchestrator context)
|
|
1214
|
+
if (args.parentWu) {
|
|
1215
|
+
const registryResult = await recordSpawnToRegistry({
|
|
1216
|
+
parentWuId: args.parentWu,
|
|
1217
|
+
targetWuId: id,
|
|
1218
|
+
lane: doc.lane || 'Unknown',
|
|
1219
|
+
baseDir: '.beacon/state',
|
|
1220
|
+
});
|
|
1221
|
+
const registryMessage = formatSpawnRecordedMessage(registryResult.spawnId, registryResult.error);
|
|
1222
|
+
console.log(`\n${registryMessage}`);
|
|
1223
|
+
}
|
|
1300
1224
|
}
|
|
1301
1225
|
}
|
|
1302
1226
|
// Guard main() for testability
|
|
1303
|
-
import { fileURLToPath } from 'node:url';
|
|
1304
1227
|
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
1305
1228
|
main();
|
|
1306
1229
|
}
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lumenflow/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Core WU lifecycle tools for LumenFlow workflow framework",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"lumenflow",
|
|
@@ -57,6 +57,9 @@
|
|
|
57
57
|
"./lib/*": "./dist/*"
|
|
58
58
|
},
|
|
59
59
|
"main": "./dist/index.js",
|
|
60
|
+
"bin": {
|
|
61
|
+
"is-agent-branch": "./dist/cli/is-agent-branch.js"
|
|
62
|
+
},
|
|
60
63
|
"files": [
|
|
61
64
|
"dist",
|
|
62
65
|
"LICENSE",
|
|
@@ -66,12 +69,11 @@
|
|
|
66
69
|
"change-case": "^5.4.4",
|
|
67
70
|
"cli-progress": "^3.12.0",
|
|
68
71
|
"cli-table3": "^0.6.5",
|
|
69
|
-
"commander": "^
|
|
72
|
+
"commander": "^14.0.2",
|
|
70
73
|
"date-fns": "^4.1.0",
|
|
71
74
|
"fast-glob": "^3.3.3",
|
|
72
75
|
"glob": "^11.0.0",
|
|
73
76
|
"gray-matter": "^4.0.3",
|
|
74
|
-
"js-yaml": "^4.1.0",
|
|
75
77
|
"micromatch": "^4.0.8",
|
|
76
78
|
"minimatch": "^10.1.1",
|
|
77
79
|
"picocolors": "^1.1.1",
|
|
@@ -79,16 +81,17 @@
|
|
|
79
81
|
"ps-list": "^8.1.1",
|
|
80
82
|
"simple-git": "^3.30.0",
|
|
81
83
|
"tiktoken": "^1.0.21",
|
|
82
|
-
"yaml": "^2.
|
|
84
|
+
"yaml": "^2.8.2",
|
|
83
85
|
"zod": "^4.3.5"
|
|
84
86
|
},
|
|
85
87
|
"devDependencies": {
|
|
86
|
-
"
|
|
87
|
-
"
|
|
88
|
+
"@vitest/coverage-v8": "^4.0.17",
|
|
89
|
+
"typescript": "^5.9.3",
|
|
90
|
+
"vitest": "^4.0.17"
|
|
88
91
|
},
|
|
89
92
|
"peerDependencies": {
|
|
90
|
-
"@lumenflow/memory": "1.
|
|
91
|
-
"@lumenflow/initiatives": "1.
|
|
93
|
+
"@lumenflow/memory": "1.3.0",
|
|
94
|
+
"@lumenflow/initiatives": "1.3.0"
|
|
92
95
|
},
|
|
93
96
|
"peerDependenciesMeta": {
|
|
94
97
|
"@lumenflow/memory": {
|