@lumenflow/cli 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/__tests__/flow-report.test.js +24 -0
- package/dist/__tests__/metrics-snapshot.test.js +24 -0
- package/dist/agent-issues-query.js +251 -0
- package/dist/agent-log-issue.js +67 -0
- package/dist/agent-session-end.js +36 -0
- package/dist/agent-session.js +46 -0
- package/dist/flow-bottlenecks.js +183 -0
- package/dist/flow-report.js +311 -0
- package/dist/gates.js +126 -49
- package/dist/init.js +297 -0
- package/dist/initiative-bulk-assign-wus.js +315 -0
- package/dist/initiative-create.js +3 -7
- package/dist/initiative-edit.js +3 -3
- package/dist/metrics-snapshot.js +314 -0
- package/dist/orchestrate-init-status.js +64 -0
- package/dist/orchestrate-initiative.js +100 -0
- package/dist/orchestrate-monitor.js +90 -0
- package/dist/wu-claim.js +313 -116
- package/dist/wu-cleanup.js +49 -3
- package/dist/wu-create.js +195 -121
- package/dist/wu-delete.js +241 -0
- package/dist/wu-done.js +146 -23
- package/dist/wu-edit.js +152 -61
- package/dist/wu-infer-lane.js +2 -2
- package/dist/wu-spawn.js +77 -158
- package/dist/wu-unlock-lane.js +158 -0
- package/package.json +30 -10
package/dist/wu-edit.js
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
* Part of WU-1274: Add wu:edit command for spec-only changes
|
|
27
27
|
* @see {@link tools/lib/micro-worktree.mjs} - Shared micro-worktree logic
|
|
28
28
|
*/
|
|
29
|
+
import { fileURLToPath } from 'node:url';
|
|
29
30
|
import { getGitForCwd, createGitForPath } from '@lumenflow/core/dist/git-adapter.js';
|
|
30
31
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
31
32
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
@@ -35,7 +36,9 @@ import { join, resolve } from 'node:path';
|
|
|
35
36
|
import { parseYAML, stringifyYAML, readWU } from '@lumenflow/core/dist/wu-yaml.js';
|
|
36
37
|
import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
|
|
37
38
|
import { WU_PATHS } from '@lumenflow/core/dist/wu-paths.js';
|
|
38
|
-
import { FILE_SYSTEM, EXIT_CODES, MICRO_WORKTREE_OPERATIONS, LOG_PREFIX, COMMIT_FORMATS, WU_STATUS, CLAIMED_MODES, getLaneBranch, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS, READINESS_UI,
|
|
39
|
+
import { FILE_SYSTEM, EXIT_CODES, MICRO_WORKTREE_OPERATIONS, LOG_PREFIX, COMMIT_FORMATS, WU_STATUS, CLAIMED_MODES, getLaneBranch, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS, READINESS_UI,
|
|
40
|
+
// WU-1039: Import exposure values for validation (Library-First, no magic strings)
|
|
41
|
+
WU_EXPOSURE_VALUES, } from '@lumenflow/core/dist/wu-constants.js';
|
|
39
42
|
// WU-1593: Use centralized validateWUIDFormat (DRY)
|
|
40
43
|
import { ensureOnMain, ensureMainUpToDate, validateWUIDFormat, } from '@lumenflow/core/dist/wu-helpers.js';
|
|
41
44
|
import { withMicroWorktree } from '@lumenflow/core/dist/micro-worktree.js';
|
|
@@ -57,6 +60,86 @@ import { normalizeWUSchema } from '@lumenflow/core/dist/wu-schema-normalization.
|
|
|
57
60
|
import { lintWUSpec, formatLintErrors } from '@lumenflow/core/dist/wu-lint.js';
|
|
58
61
|
/* eslint-disable security/detect-object-injection */
|
|
59
62
|
const PREFIX = LOG_PREFIX.EDIT;
|
|
63
|
+
/**
|
|
64
|
+
* WU-1039: Validate which edits are allowed on done WUs
|
|
65
|
+
*
|
|
66
|
+
* Done WUs only allow metadata reassignment: initiative, phase, and exposure.
|
|
67
|
+
* All other edits are blocked to preserve WU immutability after completion.
|
|
68
|
+
*
|
|
69
|
+
* @param opts - Parsed CLI options
|
|
70
|
+
* @returns { valid: boolean, disallowedEdits: string[] }
|
|
71
|
+
*/
|
|
72
|
+
export function validateDoneWUEdits(opts) {
|
|
73
|
+
const disallowedEdits = [];
|
|
74
|
+
// Check for disallowed edits on done WUs
|
|
75
|
+
if (opts.specFile)
|
|
76
|
+
disallowedEdits.push('--spec-file');
|
|
77
|
+
if (opts.description)
|
|
78
|
+
disallowedEdits.push('--description');
|
|
79
|
+
if (opts.acceptance && Array.isArray(opts.acceptance) && opts.acceptance.length > 0) {
|
|
80
|
+
disallowedEdits.push('--acceptance');
|
|
81
|
+
}
|
|
82
|
+
if (opts.notes)
|
|
83
|
+
disallowedEdits.push('--notes');
|
|
84
|
+
if (opts.codePaths && Array.isArray(opts.codePaths) && opts.codePaths.length > 0) {
|
|
85
|
+
disallowedEdits.push('--code-paths');
|
|
86
|
+
}
|
|
87
|
+
if (opts.lane)
|
|
88
|
+
disallowedEdits.push('--lane');
|
|
89
|
+
if (opts.type)
|
|
90
|
+
disallowedEdits.push('--type');
|
|
91
|
+
if (opts.priority)
|
|
92
|
+
disallowedEdits.push('--priority');
|
|
93
|
+
if (opts.testPathsManual &&
|
|
94
|
+
Array.isArray(opts.testPathsManual) &&
|
|
95
|
+
opts.testPathsManual.length > 0) {
|
|
96
|
+
disallowedEdits.push('--test-paths-manual');
|
|
97
|
+
}
|
|
98
|
+
if (opts.testPathsUnit && Array.isArray(opts.testPathsUnit) && opts.testPathsUnit.length > 0) {
|
|
99
|
+
disallowedEdits.push('--test-paths-unit');
|
|
100
|
+
}
|
|
101
|
+
if (opts.testPathsE2e && Array.isArray(opts.testPathsE2e) && opts.testPathsE2e.length > 0) {
|
|
102
|
+
disallowedEdits.push('--test-paths-e2e');
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
valid: disallowedEdits.length === 0,
|
|
106
|
+
disallowedEdits,
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* WU-1039: Validate exposure value against schema
|
|
111
|
+
*
|
|
112
|
+
* Uses WU_EXPOSURE_VALUES from core constants (Library-First, no magic strings).
|
|
113
|
+
*
|
|
114
|
+
* @param exposure - Exposure value to validate
|
|
115
|
+
* @returns { valid: boolean, error?: string }
|
|
116
|
+
*/
|
|
117
|
+
export function validateExposureValue(exposure) {
|
|
118
|
+
// WU_EXPOSURE_VALUES is readonly array, need to cast for includes check
|
|
119
|
+
const validValues = WU_EXPOSURE_VALUES;
|
|
120
|
+
if (!validValues.includes(exposure)) {
|
|
121
|
+
return {
|
|
122
|
+
valid: false,
|
|
123
|
+
error: `Invalid exposure value: "${exposure}"\n\nValid values: ${WU_EXPOSURE_VALUES.join(', ')}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return { valid: true };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* WU-1039: Apply exposure edit to WU object
|
|
130
|
+
*
|
|
131
|
+
* Returns a new WU object with updated exposure (immutable pattern).
|
|
132
|
+
*
|
|
133
|
+
* @param wu - Original WU object
|
|
134
|
+
* @param exposure - New exposure value
|
|
135
|
+
* @returns Updated WU object (does not mutate original)
|
|
136
|
+
*/
|
|
137
|
+
export function applyExposureEdit(wu, exposure) {
|
|
138
|
+
return {
|
|
139
|
+
...wu,
|
|
140
|
+
exposure,
|
|
141
|
+
};
|
|
142
|
+
}
|
|
60
143
|
/**
|
|
61
144
|
* Custom options for wu-edit (not in shared WU_OPTIONS)
|
|
62
145
|
*/
|
|
@@ -244,6 +327,8 @@ function parseArgs() {
|
|
|
244
327
|
// WU-2564: Add blocked_by and dependencies
|
|
245
328
|
EDIT_OPTIONS.blockedBy,
|
|
246
329
|
EDIT_OPTIONS.addDep,
|
|
330
|
+
// WU-1039: Add exposure for done WU metadata updates
|
|
331
|
+
WU_OPTIONS.exposure,
|
|
247
332
|
],
|
|
248
333
|
required: ['id'],
|
|
249
334
|
allowPositionalId: true,
|
|
@@ -629,6 +714,14 @@ function applyEdits(wu, opts) {
|
|
|
629
714
|
.filter(Boolean);
|
|
630
715
|
updated.dependencies = mergeArrayField(wu.dependencies, depIds, opts.append);
|
|
631
716
|
}
|
|
717
|
+
// WU-1039: Handle --exposure flag with validation
|
|
718
|
+
if (opts.exposure) {
|
|
719
|
+
const exposureResult = validateExposureValue(opts.exposure);
|
|
720
|
+
if (!exposureResult.valid) {
|
|
721
|
+
die(exposureResult.error);
|
|
722
|
+
}
|
|
723
|
+
updated.exposure = opts.exposure;
|
|
724
|
+
}
|
|
632
725
|
return updated;
|
|
633
726
|
}
|
|
634
727
|
/**
|
|
@@ -642,38 +735,18 @@ async function main() {
|
|
|
642
735
|
// Validate inputs
|
|
643
736
|
validateWUIDFormat(id);
|
|
644
737
|
const { wu: originalWU, editMode, isDone } = validateWUEditable(id);
|
|
645
|
-
// WU-
|
|
738
|
+
// WU-1039: Done WUs allow initiative/phase/exposure edits only (metadata reassignment)
|
|
739
|
+
// Uses validateDoneWUEdits (DRY - centralized validation logic)
|
|
646
740
|
if (isDone) {
|
|
647
|
-
const
|
|
648
|
-
if (
|
|
649
|
-
disallowedEdits.push('--spec-file');
|
|
650
|
-
if (opts.description)
|
|
651
|
-
disallowedEdits.push('--description');
|
|
652
|
-
if (opts.acceptance && opts.acceptance.length > 0)
|
|
653
|
-
disallowedEdits.push('--acceptance');
|
|
654
|
-
if (opts.notes)
|
|
655
|
-
disallowedEdits.push('--notes');
|
|
656
|
-
if (opts.codePaths && opts.codePaths.length > 0)
|
|
657
|
-
disallowedEdits.push('--code-paths');
|
|
658
|
-
if (opts.lane)
|
|
659
|
-
disallowedEdits.push('--lane');
|
|
660
|
-
if (opts.type)
|
|
661
|
-
disallowedEdits.push('--type');
|
|
662
|
-
if (opts.priority)
|
|
663
|
-
disallowedEdits.push('--priority');
|
|
664
|
-
if (opts.testPathsManual && opts.testPathsManual.length > 0)
|
|
665
|
-
disallowedEdits.push('--test-paths-manual');
|
|
666
|
-
if (opts.testPathsUnit && opts.testPathsUnit.length > 0)
|
|
667
|
-
disallowedEdits.push('--test-paths-unit');
|
|
668
|
-
if (opts.testPathsE2e && opts.testPathsE2e.length > 0)
|
|
669
|
-
disallowedEdits.push('--test-paths-e2e');
|
|
670
|
-
if (disallowedEdits.length > 0) {
|
|
741
|
+
const doneValidation = validateDoneWUEdits(opts);
|
|
742
|
+
if (!doneValidation.valid) {
|
|
671
743
|
die(`Cannot edit WU ${id}: WU is done/immutable.\n\n` +
|
|
672
|
-
`Completed WUs only allow initiative/phase reassignment.\n` +
|
|
673
|
-
`Disallowed edits: ${disallowedEdits.join(', ')}\n\n` +
|
|
744
|
+
`Completed WUs only allow initiative/phase/exposure reassignment.\n` +
|
|
745
|
+
`Disallowed edits: ${doneValidation.disallowedEdits.join(', ')}\n\n` +
|
|
674
746
|
`Allowed for done WUs:\n` +
|
|
675
747
|
` --initiative <initId> Reassign to different initiative\n` +
|
|
676
|
-
` --phase <number> Update phase within initiative`
|
|
748
|
+
` --phase <number> Update phase within initiative\n` +
|
|
749
|
+
` --exposure <type> Update exposure level`);
|
|
677
750
|
}
|
|
678
751
|
}
|
|
679
752
|
// Check we have something to edit
|
|
@@ -698,7 +771,9 @@ async function main() {
|
|
|
698
771
|
opts.phase ||
|
|
699
772
|
// WU-2564: Add blocked_by and add_dep to hasEdits check
|
|
700
773
|
opts.blockedBy ||
|
|
701
|
-
opts.addDep
|
|
774
|
+
opts.addDep ||
|
|
775
|
+
// WU-1039: Add exposure to hasEdits check
|
|
776
|
+
opts.exposure;
|
|
702
777
|
if (!hasEdits) {
|
|
703
778
|
die('No edits specified.\n\n' +
|
|
704
779
|
'Provide one of:\n' +
|
|
@@ -716,7 +791,8 @@ async function main() {
|
|
|
716
791
|
' --test-paths-unit <path> Add unit test paths (repeatable; use --append to add)\n' +
|
|
717
792
|
' --test-paths-e2e <path> Add e2e test paths (repeatable; use --append to add)\n' +
|
|
718
793
|
' --blocked-by <wuIds> WU IDs that block this WU (comma-separated; use --append to add)\n' +
|
|
719
|
-
' --add-dep <wuIds> Add WU IDs to dependencies (comma-separated; use --append to add)'
|
|
794
|
+
' --add-dep <wuIds> Add WU IDs to dependencies (comma-separated; use --append to add)\n' +
|
|
795
|
+
' --exposure <type> Update exposure level (ui, api, backend-only, documentation)');
|
|
720
796
|
}
|
|
721
797
|
// Apply edits to get updated WU
|
|
722
798
|
const updatedWU = applyEdits(originalWU, opts);
|
|
@@ -814,39 +890,54 @@ async function main() {
|
|
|
814
890
|
const oldInitiative = originalWU.initiative;
|
|
815
891
|
const newInitiative = opts.initiative;
|
|
816
892
|
const initiativeChanged = newInitiative && newInitiative !== oldInitiative;
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
893
|
+
const previousWuTool = process.env.LUMENFLOW_WU_TOOL;
|
|
894
|
+
process.env.LUMENFLOW_WU_TOOL = MICRO_WORKTREE_OPERATIONS.WU_EDIT;
|
|
895
|
+
try {
|
|
896
|
+
await withMicroWorktree({
|
|
897
|
+
operation: MICRO_WORKTREE_OPERATIONS.WU_EDIT,
|
|
898
|
+
id: id,
|
|
899
|
+
logPrefix: PREFIX,
|
|
900
|
+
execute: async ({ worktreePath }) => {
|
|
901
|
+
const files = [WU_PATHS.WU(id)];
|
|
902
|
+
// Write updated WU to micro-worktree (WU-1750: use normalized data)
|
|
903
|
+
const wuPath = join(worktreePath, WU_PATHS.WU(id));
|
|
904
|
+
// WU-1442: Normalize dates before dumping to prevent ISO timestamp corruption
|
|
905
|
+
normalizeWUDates(normalizedWU);
|
|
906
|
+
// Emergency fix Session 2: Use centralized stringifyYAML helper
|
|
907
|
+
const yamlContent = stringifyYAML(normalizedWU);
|
|
908
|
+
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes WU files
|
|
909
|
+
writeFileSync(wuPath, yamlContent, { encoding: FILE_SYSTEM.ENCODING });
|
|
910
|
+
console.log(`${PREFIX} ✅ Updated ${id}.yaml in micro-worktree`);
|
|
911
|
+
// WU-1929: Handle bidirectional initiative updates
|
|
912
|
+
if (initiativeChanged) {
|
|
913
|
+
const initiativeFiles = updateInitiativeWusArrays(worktreePath, id, oldInitiative, newInitiative);
|
|
914
|
+
files.push(...initiativeFiles);
|
|
915
|
+
}
|
|
916
|
+
return {
|
|
917
|
+
commitMessage: COMMIT_FORMATS.EDIT(id),
|
|
918
|
+
files,
|
|
919
|
+
};
|
|
920
|
+
},
|
|
921
|
+
});
|
|
922
|
+
}
|
|
923
|
+
finally {
|
|
924
|
+
if (previousWuTool === undefined) {
|
|
925
|
+
delete process.env.LUMENFLOW_WU_TOOL;
|
|
926
|
+
}
|
|
927
|
+
else {
|
|
928
|
+
process.env.LUMENFLOW_WU_TOOL = previousWuTool;
|
|
929
|
+
}
|
|
930
|
+
}
|
|
843
931
|
console.log(`${PREFIX} ✅ Successfully edited ${id}`);
|
|
844
932
|
console.log(`${PREFIX} Changes pushed to origin/main`);
|
|
845
933
|
// WU-1620: Display readiness summary
|
|
846
934
|
displayReadinessSummary(id);
|
|
847
935
|
}
|
|
848
936
|
}
|
|
849
|
-
main()
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
});
|
|
937
|
+
// Guard main() execution for testability (WU-1366)
|
|
938
|
+
if (process.argv[1] === fileURLToPath(import.meta.url)) {
|
|
939
|
+
main().catch((err) => {
|
|
940
|
+
console.error(`${PREFIX} ❌ ${err.message}`);
|
|
941
|
+
process.exit(EXIT_CODES.ERROR);
|
|
942
|
+
});
|
|
943
|
+
}
|
package/dist/wu-infer-lane.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
import { readFileSync, existsSync } from 'node:fs';
|
|
18
18
|
import path from 'node:path';
|
|
19
|
-
import
|
|
19
|
+
import { parseYAML } from '@lumenflow/core/dist/wu-yaml.js';
|
|
20
20
|
import { inferSubLane } from '@lumenflow/core/dist/lane-inference.js';
|
|
21
21
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
22
22
|
import { FILE_SYSTEM, EXIT_CODES } from '@lumenflow/core/dist/wu-constants.js';
|
|
@@ -81,7 +81,7 @@ function loadWuYaml(id) {
|
|
|
81
81
|
` 2. Ensure you have read access to the repository`);
|
|
82
82
|
}
|
|
83
83
|
try {
|
|
84
|
-
const doc =
|
|
84
|
+
const doc = parseYAML(content);
|
|
85
85
|
return doc;
|
|
86
86
|
}
|
|
87
87
|
catch (err) {
|
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,7 +25,7 @@
|
|
|
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
31
|
import path from 'node:path';
|
|
@@ -40,6 +40,9 @@ import { minimatch } from 'minimatch';
|
|
|
40
40
|
// WU-2252: Import invariants loader for spawn output injection
|
|
41
41
|
import { loadInvariants, INVARIANT_TYPES } from '@lumenflow/core/dist/invariants-runner.js';
|
|
42
42
|
import { validateSpawnArgs, generateExecutionModeSection, generateThinkToolGuidance, recordSpawnToRegistry, formatSpawnRecordedMessage, } from '@lumenflow/core/dist/wu-spawn-helpers.js';
|
|
43
|
+
import { SpawnStrategyFactory } from '@lumenflow/core/dist/spawn-strategy.js';
|
|
44
|
+
import { getConfig } from '@lumenflow/core/dist/lumenflow-config.js';
|
|
45
|
+
import { generateClientSkillsGuidance, generateSkillsSelectionSection, resolveClientConfig, } from '@lumenflow/core/dist/wu-spawn-skills.js';
|
|
43
46
|
import { validateSpawnDependencies, formatDependencyError, } from '@lumenflow/core/dist/dependency-validator.js';
|
|
44
47
|
/**
|
|
45
48
|
* Mandatory agent trigger patterns.
|
|
@@ -50,27 +53,6 @@ const MANDATORY_TRIGGERS = {
|
|
|
50
53
|
'beacon-guardian': ['**/prompts/**', '**/classification/**', '**/detector/**', '**/llm/**'],
|
|
51
54
|
};
|
|
52
55
|
const LOG_PREFIX = '[wu:spawn]';
|
|
53
|
-
/** @type {string} */
|
|
54
|
-
const AGENTS_DIR = '.claude/agents';
|
|
55
|
-
/**
|
|
56
|
-
* Load skills configured in agent's frontmatter
|
|
57
|
-
*
|
|
58
|
-
* @param {string} agentName - Agent name (e.g., 'general-purpose')
|
|
59
|
-
* @returns {string[]} Array of skill names or empty array if not found
|
|
60
|
-
*/
|
|
61
|
-
function loadAgentConfiguredSkills(agentName) {
|
|
62
|
-
const agentPath = `${AGENTS_DIR}/${agentName}.md`;
|
|
63
|
-
if (!existsSync(agentPath)) {
|
|
64
|
-
return [];
|
|
65
|
-
}
|
|
66
|
-
try {
|
|
67
|
-
const content = readFileSync(agentPath, { encoding: FILE_SYSTEM.UTF8 });
|
|
68
|
-
return []; // Skills loading removed - vendor agnostic
|
|
69
|
-
}
|
|
70
|
-
catch {
|
|
71
|
-
return [];
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
56
|
/**
|
|
75
57
|
* Detect mandatory agents based on code paths.
|
|
76
58
|
*
|
|
@@ -319,27 +301,15 @@ function generateTDDDirective() {
|
|
|
319
301
|
* @param {string} id - WU ID
|
|
320
302
|
* @returns {string} Context loading preamble
|
|
321
303
|
*/
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
Before running wu:claim, check docs/04-operations/tasks/status.md to ensure the lane is free.
|
|
333
|
-
Only ONE WU can be in_progress per lane at any time.
|
|
334
|
-
|
|
335
|
-
## Context Recovery (Session Resumption)
|
|
336
|
-
|
|
337
|
-
Before starting work, check for prior context from previous sessions:
|
|
338
|
-
|
|
339
|
-
1. \`pnpm mem:ready --wu ${id}\` — Query pending nodes (what's next?)
|
|
340
|
-
2. \`pnpm mem:inbox --wu ${id}\` — Check coordination signals from parallel agents
|
|
341
|
-
|
|
342
|
-
If prior context exists, resume from the last checkpoint. Otherwise, proceed with the task below.`;
|
|
304
|
+
/**
|
|
305
|
+
* Generate the context loading preamble using the strategy
|
|
306
|
+
*
|
|
307
|
+
* @param {string} id - WU ID
|
|
308
|
+
* @param {SpawnStrategy} strategy - Client strategy
|
|
309
|
+
* @returns {string} Context loading preamble
|
|
310
|
+
*/
|
|
311
|
+
function generatePreamble(id, strategy) {
|
|
312
|
+
return strategy.getPreamble(id);
|
|
343
313
|
}
|
|
344
314
|
/**
|
|
345
315
|
* Generate the constraints block (appended at end per Lost in the Middle research)
|
|
@@ -739,7 +709,7 @@ pnpm mem:triage --wu ${id} # List discoveries for this WU
|
|
|
739
709
|
pnpm mem:triage --promote <node-id> --lane "<lane>" # Create Bug WU (human action)
|
|
740
710
|
\`\`\`
|
|
741
711
|
|
|
742
|
-
See:
|
|
712
|
+
See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/agent-invocation-guide.md §Bug Discovery`;
|
|
743
713
|
}
|
|
744
714
|
/**
|
|
745
715
|
* Generate lane-specific guidance
|
|
@@ -838,128 +808,37 @@ pnpm wu:done --id ${id}
|
|
|
838
808
|
|
|
839
809
|
**Do not ask** "should I run wu:done?" — just run it when gates pass.`;
|
|
840
810
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
* If an agentName is provided, that agent's configured skills (from frontmatter)
|
|
849
|
-
* are auto-loaded at the top.
|
|
850
|
-
*
|
|
851
|
-
* @param {object} doc - WU YAML document
|
|
852
|
-
* @param {string} [agentName='general-purpose'] - Agent to spawn
|
|
853
|
-
* @returns {string} Skills Selection section
|
|
854
|
-
*/
|
|
855
|
-
// eslint-disable-next-line sonarjs/cognitive-complexity -- WU-2025: Pre-existing complexity, refactor tracked
|
|
856
|
-
function generateSkillsSection(doc, agentName = 'general-purpose') {
|
|
857
|
-
const lane = doc.lane || '';
|
|
858
|
-
const type = doc.type || 'feature';
|
|
859
|
-
const laneParent = lane.split(':')[0].trim();
|
|
860
|
-
// Load agent's configured skills from frontmatter
|
|
861
|
-
const agentSkills = loadAgentConfiguredSkills(agentName);
|
|
862
|
-
const hasAgentSkills = agentSkills.length > 0;
|
|
863
|
-
// Build auto-load section if agent has configured skills
|
|
864
|
-
const autoLoadSection = hasAgentSkills
|
|
865
|
-
? `### Auto-Loaded Skills (from ${agentName} agent config)
|
|
866
|
-
|
|
867
|
-
These skills are pre-configured for this agent and should be loaded first:
|
|
868
|
-
|
|
869
|
-
${agentSkills.map((s) => `- \`${s}\` — Load via \`/skill ${s}\``).join('\n')}
|
|
870
|
-
|
|
871
|
-
`
|
|
872
|
-
: '';
|
|
873
|
-
// Build context hints for the sub-agent
|
|
874
|
-
const contextHints = [];
|
|
875
|
-
// Universal baselines (only if not already in agent skills)
|
|
876
|
-
if (!agentSkills.includes('wu-lifecycle')) {
|
|
877
|
-
contextHints.push('- `wu-lifecycle` — ALL WUs need workflow automation');
|
|
878
|
-
}
|
|
879
|
-
if (!agentSkills.includes('worktree-discipline')) {
|
|
880
|
-
contextHints.push('- `worktree-discipline` — ALL WUs need path safety');
|
|
881
|
-
}
|
|
882
|
-
// Type-based hints
|
|
883
|
-
if ((type === 'feature' || type === 'enhancement') && !agentSkills.includes('tdd-workflow')) {
|
|
884
|
-
contextHints.push('- `tdd-workflow` — TDD is mandatory for feature/enhancement WUs');
|
|
885
|
-
}
|
|
886
|
-
if (type === 'bug' && !agentSkills.includes('bug-classification')) {
|
|
887
|
-
contextHints.push('- `bug-classification` — Bug severity assessment');
|
|
888
|
-
}
|
|
889
|
-
// Lane-based hints
|
|
890
|
-
if (laneParent === 'Operations' &&
|
|
891
|
-
lane.includes('Tooling') &&
|
|
892
|
-
!agentSkills.includes('lumenflow-gates')) {
|
|
893
|
-
contextHints.push('- `lumenflow-gates` — Tooling often affects gates');
|
|
894
|
-
}
|
|
895
|
-
if (laneParent === 'Intelligence') {
|
|
896
|
-
if (!agentSkills.includes('beacon-compliance')) {
|
|
897
|
-
contextHints.push('- `beacon-compliance` — Intelligence lane requires Beacon validation');
|
|
898
|
-
}
|
|
899
|
-
if (!agentSkills.includes('prompt-management')) {
|
|
900
|
-
contextHints.push('- `prompt-management` — For prompt template work');
|
|
901
|
-
}
|
|
902
|
-
}
|
|
903
|
-
if (laneParent === 'Experience' && !agentSkills.includes('frontend-design')) {
|
|
904
|
-
contextHints.push('- `frontend-design` — For UI component work');
|
|
905
|
-
}
|
|
906
|
-
const softPolicySection = contextHints.length > 0
|
|
907
|
-
? `### Soft Policy (baselines for this WU)
|
|
908
|
-
|
|
909
|
-
Based on WU context, consider loading:
|
|
910
|
-
|
|
911
|
-
${contextHints.join('\n')}
|
|
912
|
-
|
|
913
|
-
`
|
|
914
|
-
: '';
|
|
915
|
-
return `## Skills Selection
|
|
916
|
-
|
|
917
|
-
**IMPORTANT**: Before starting work, select and load relevant skills.
|
|
918
|
-
|
|
919
|
-
${autoLoadSection}### How to Select Skills
|
|
920
|
-
|
|
921
|
-
1. Read the skill catalogue frontmatter from \`.claude/skills/*/SKILL.md\`
|
|
922
|
-
2. Match skills to WU context (lane, type, code_paths, description)
|
|
923
|
-
3. Load selected skills via \`/skill <skill-name>\`
|
|
924
|
-
|
|
925
|
-
${softPolicySection}### Additional Skills (load if needed)
|
|
926
|
-
|
|
927
|
-
| Skill | Use When |
|
|
928
|
-
|-------|----------|
|
|
929
|
-
| lumenflow-gates | Gates fail, debugging format/lint/typecheck errors |
|
|
930
|
-
| bug-classification | Bug discovered mid-WU, need priority classification |
|
|
931
|
-
| beacon-compliance | Code touches LLM, prompts, classification |
|
|
932
|
-
| prompt-management | Working with prompt templates, golden datasets |
|
|
933
|
-
| frontend-design | Building UI components, pages |
|
|
934
|
-
| initiative-management | Multi-phase projects, INIT-XXX coordination |
|
|
935
|
-
| multi-agent-coordination | Spawning sub-agents, parallel WU work |
|
|
936
|
-
| orchestration | Agent coordination, mandatory agent checks |
|
|
937
|
-
| ops-maintenance | Metrics, validation, health checks |
|
|
938
|
-
|
|
939
|
-
### Graceful Degradation
|
|
940
|
-
|
|
941
|
-
If the skill catalogue is missing or invalid:
|
|
942
|
-
- Load baseline skills: \`/skill wu-lifecycle\`, \`/skill tdd-workflow\` (for features)
|
|
943
|
-
- Continue with implementation using Mandatory Standards below
|
|
944
|
-
`;
|
|
811
|
+
function generateClientBlocksSection(clientContext) {
|
|
812
|
+
if (!clientContext?.config?.blocks?.length)
|
|
813
|
+
return '';
|
|
814
|
+
const blocks = clientContext.config.blocks
|
|
815
|
+
.map((block) => `### ${block.title}\n\n${block.content}`)
|
|
816
|
+
.join('\n\n');
|
|
817
|
+
return `## Client Guidance (${clientContext.name})\n\n${blocks}`;
|
|
945
818
|
}
|
|
946
819
|
/**
|
|
947
820
|
* Generate the complete Task tool invocation
|
|
948
821
|
*
|
|
949
822
|
* @param {object} doc - WU YAML document
|
|
950
823
|
* @param {string} id - WU ID
|
|
824
|
+
* @param {SpawnStrategy} strategy - Client strategy
|
|
951
825
|
* @param {object} [options={}] - Thinking mode options
|
|
952
826
|
* @param {boolean} [options.thinking] - Whether extended thinking is enabled
|
|
953
827
|
* @param {boolean} [options.noThinking] - Whether thinking is explicitly disabled
|
|
954
828
|
* @param {string} [options.budget] - Token budget for thinking
|
|
955
829
|
* @returns {string} Complete Task tool invocation
|
|
956
830
|
*/
|
|
957
|
-
export function generateTaskInvocation(doc, id, options = {}) {
|
|
831
|
+
export function generateTaskInvocation(doc, id, strategy, options = {}) {
|
|
958
832
|
const codePaths = doc.code_paths || [];
|
|
959
833
|
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
960
|
-
const preamble = generatePreamble(id);
|
|
834
|
+
const preamble = generatePreamble(id, strategy);
|
|
961
835
|
const tddDirective = generateTDDDirective();
|
|
962
|
-
const
|
|
836
|
+
const clientContext = options.client;
|
|
837
|
+
const config = options.config || getConfig();
|
|
838
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
839
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
|
|
840
|
+
(clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
|
|
841
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
963
842
|
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
964
843
|
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
965
844
|
const bugDiscoverySection = generateBugDiscoverySection(id);
|
|
@@ -1038,7 +917,7 @@ ${thinkingBlock}${skillsSection}
|
|
|
1038
917
|
- **Documentation**: Update tooling docs if changing tools. Keep docs in sync with code
|
|
1039
918
|
- **Sub-agents**: Use Explore agent for codebase investigation. Activate mandatory agents (security-auditor for PHI/auth, beacon-guardian for LLM/prompts)
|
|
1040
919
|
|
|
1041
|
-
${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
|
|
920
|
+
${clientBlocks ? `---\n\n${clientBlocks}\n\n` : ''}${worktreeGuidance ? `---\n\n${worktreeGuidance}\n\n` : ''}---
|
|
1042
921
|
|
|
1043
922
|
${bugDiscoverySection}
|
|
1044
923
|
|
|
@@ -1103,10 +982,10 @@ ${constraints}`;
|
|
|
1103
982
|
].join('\n');
|
|
1104
983
|
return invocation;
|
|
1105
984
|
}
|
|
1106
|
-
export function generateCodexPrompt(doc, id, options = {}) {
|
|
985
|
+
export function generateCodexPrompt(doc, id, strategy, options = {}) {
|
|
1107
986
|
const codePaths = doc.code_paths || [];
|
|
1108
987
|
const mandatoryAgents = detectMandatoryAgents(codePaths);
|
|
1109
|
-
const preamble = generatePreamble(id);
|
|
988
|
+
const preamble = generatePreamble(id, strategy);
|
|
1110
989
|
const tddDirective = generateTDDDirective();
|
|
1111
990
|
const mandatorySection = generateMandatoryAgentSection(mandatoryAgents, id);
|
|
1112
991
|
const laneGuidance = generateLaneGuidance(doc.lane);
|
|
@@ -1114,6 +993,12 @@ export function generateCodexPrompt(doc, id, options = {}) {
|
|
|
1114
993
|
const implementationContext = generateImplementationContext(doc);
|
|
1115
994
|
const action = generateActionSection(doc, id);
|
|
1116
995
|
const constraints = generateCodexConstraints(id);
|
|
996
|
+
const clientContext = options.client;
|
|
997
|
+
const config = options.config || getConfig();
|
|
998
|
+
const clientSkillsGuidance = generateClientSkillsGuidance(clientContext);
|
|
999
|
+
const skillsSection = generateSkillsSelectionSection(doc, config, clientContext?.name) +
|
|
1000
|
+
(clientSkillsGuidance ? `\n${clientSkillsGuidance}` : '');
|
|
1001
|
+
const clientBlocks = generateClientBlocksSection(clientContext);
|
|
1117
1002
|
const executionModeSection = generateExecutionModeSection(options);
|
|
1118
1003
|
const thinkToolGuidance = generateThinkToolGuidance(options);
|
|
1119
1004
|
const thinkingSections = [executionModeSection, thinkToolGuidance]
|
|
@@ -1156,6 +1041,10 @@ ${formatAcceptance(doc.acceptance)}
|
|
|
1156
1041
|
|
|
1157
1042
|
---
|
|
1158
1043
|
|
|
1044
|
+
${skillsSection}
|
|
1045
|
+
|
|
1046
|
+
---
|
|
1047
|
+
|
|
1159
1048
|
## Action
|
|
1160
1049
|
|
|
1161
1050
|
${action}
|
|
@@ -1169,7 +1058,7 @@ ${action}
|
|
|
1169
1058
|
|
|
1170
1059
|
---
|
|
1171
1060
|
|
|
1172
|
-
${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
|
|
1061
|
+
${mandatorySection}${implementationContext ? `${implementationContext}\n\n---\n\n` : ''}${clientBlocks ? `${clientBlocks}\n\n---\n\n` : ''}${thinkingBlock}${bugDiscoverySection}
|
|
1173
1062
|
|
|
1174
1063
|
---
|
|
1175
1064
|
|
|
@@ -1223,6 +1112,8 @@ async function main() {
|
|
|
1223
1112
|
WU_OPTIONS.budget,
|
|
1224
1113
|
WU_OPTIONS.codex,
|
|
1225
1114
|
WU_OPTIONS.parentWu, // WU-1945: Parent WU for spawn registry tracking
|
|
1115
|
+
WU_OPTIONS.client,
|
|
1116
|
+
WU_OPTIONS.vendor,
|
|
1226
1117
|
],
|
|
1227
1118
|
required: ['id'],
|
|
1228
1119
|
allowPositionalId: true,
|
|
@@ -1295,15 +1186,43 @@ async function main() {
|
|
|
1295
1186
|
noThinking: args.noThinking,
|
|
1296
1187
|
budget: args.budget,
|
|
1297
1188
|
};
|
|
1189
|
+
// Client Resolution
|
|
1190
|
+
const config = getConfig();
|
|
1191
|
+
let clientName = args.client;
|
|
1192
|
+
if (!clientName && args.vendor) {
|
|
1193
|
+
console.warn(`${LOG_PREFIX} ${EMOJI.WARNING} Warning: --vendor is deprecated. Use --client.`);
|
|
1194
|
+
clientName = args.vendor;
|
|
1195
|
+
}
|
|
1196
|
+
// Codex handling (deprecated legacy flag)
|
|
1298
1197
|
if (args.codex) {
|
|
1299
|
-
|
|
1198
|
+
if (!clientName) {
|
|
1199
|
+
console.warn(`${LOG_PREFIX} ${EMOJI.WARNING} Warning: --codex is deprecated. Use --client codex-cli.`);
|
|
1200
|
+
clientName = 'codex-cli';
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
if (!clientName) {
|
|
1204
|
+
clientName = config.agents.defaultClient || 'claude-code';
|
|
1205
|
+
}
|
|
1206
|
+
// Create strategy
|
|
1207
|
+
const strategy = SpawnStrategyFactory.create(clientName);
|
|
1208
|
+
const clientContext = { name: clientName, config: resolveClientConfig(config, clientName) };
|
|
1209
|
+
if (clientName === 'codex-cli' || args.codex) {
|
|
1210
|
+
const prompt = generateCodexPrompt(doc, id, strategy, {
|
|
1211
|
+
...thinkingOptions,
|
|
1212
|
+
client: clientContext,
|
|
1213
|
+
config,
|
|
1214
|
+
});
|
|
1300
1215
|
console.log(`${LOG_PREFIX} Generated Codex/GPT prompt for ${id}`);
|
|
1301
1216
|
console.log(`${LOG_PREFIX} Copy the Markdown below:\n`);
|
|
1302
1217
|
console.log(prompt.trimEnd());
|
|
1303
1218
|
return;
|
|
1304
1219
|
}
|
|
1305
1220
|
// Generate and output the Task invocation
|
|
1306
|
-
const invocation = generateTaskInvocation(doc, id,
|
|
1221
|
+
const invocation = generateTaskInvocation(doc, id, strategy, {
|
|
1222
|
+
...thinkingOptions,
|
|
1223
|
+
client: clientContext,
|
|
1224
|
+
config,
|
|
1225
|
+
});
|
|
1307
1226
|
console.log(`${LOG_PREFIX} Generated Task tool invocation for ${id}`);
|
|
1308
1227
|
console.log(`${LOG_PREFIX} Copy the block below to spawn a sub-agent:\n`);
|
|
1309
1228
|
console.log(invocation);
|