@lumenflow/cli 2.2.1 → 2.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +147 -57
  2. package/dist/__tests__/agent-log-issue.test.js +56 -0
  3. package/dist/__tests__/cli-entry-point.test.js +66 -17
  4. package/dist/__tests__/cli-subprocess.test.js +25 -0
  5. package/dist/__tests__/init.test.js +298 -0
  6. package/dist/__tests__/initiative-plan.test.js +340 -0
  7. package/dist/__tests__/mem-cleanup-execution.test.js +19 -0
  8. package/dist/__tests__/merge-block.test.js +220 -0
  9. package/dist/__tests__/release.test.js +28 -0
  10. package/dist/__tests__/safe-git.test.js +191 -0
  11. package/dist/__tests__/state-doctor.test.js +274 -0
  12. package/dist/__tests__/wu-done.test.js +36 -0
  13. package/dist/__tests__/wu-edit.test.js +119 -0
  14. package/dist/__tests__/wu-prep.test.js +108 -0
  15. package/dist/agent-issues-query.js +4 -3
  16. package/dist/agent-log-issue.js +25 -4
  17. package/dist/backlog-prune.js +5 -4
  18. package/dist/cli-entry-point.js +11 -1
  19. package/dist/doctor.js +368 -0
  20. package/dist/flow-bottlenecks.js +6 -5
  21. package/dist/flow-report.js +4 -3
  22. package/dist/gates.js +468 -116
  23. package/dist/guard-locked.js +4 -3
  24. package/dist/guard-worktree-commit.js +4 -3
  25. package/dist/init.js +508 -86
  26. package/dist/initiative-add-wu.js +4 -3
  27. package/dist/initiative-bulk-assign-wus.js +8 -5
  28. package/dist/initiative-create.js +73 -37
  29. package/dist/initiative-edit.js +37 -21
  30. package/dist/initiative-list.js +4 -3
  31. package/dist/initiative-plan.js +337 -0
  32. package/dist/initiative-status.js +4 -3
  33. package/dist/lane-health.js +377 -0
  34. package/dist/lane-suggest.js +382 -0
  35. package/dist/mem-checkpoint.js +2 -2
  36. package/dist/mem-cleanup.js +2 -2
  37. package/dist/mem-context.js +306 -0
  38. package/dist/mem-create.js +2 -2
  39. package/dist/mem-delete.js +293 -0
  40. package/dist/mem-inbox.js +2 -2
  41. package/dist/mem-index.js +211 -0
  42. package/dist/mem-init.js +1 -1
  43. package/dist/mem-profile.js +207 -0
  44. package/dist/mem-promote.js +254 -0
  45. package/dist/mem-ready.js +2 -2
  46. package/dist/mem-signal.js +2 -2
  47. package/dist/mem-start.js +2 -2
  48. package/dist/mem-summarize.js +2 -2
  49. package/dist/mem-triage.js +2 -2
  50. package/dist/merge-block.js +222 -0
  51. package/dist/metrics-cli.js +7 -4
  52. package/dist/metrics-snapshot.js +4 -3
  53. package/dist/orchestrate-initiative.js +10 -4
  54. package/dist/orchestrate-monitor.js +379 -31
  55. package/dist/signal-cleanup.js +296 -0
  56. package/dist/spawn-list.js +6 -5
  57. package/dist/state-bootstrap.js +5 -4
  58. package/dist/state-cleanup.js +360 -0
  59. package/dist/state-doctor-fix.js +196 -0
  60. package/dist/state-doctor.js +501 -0
  61. package/dist/validate-agent-skills.js +4 -3
  62. package/dist/validate-agent-sync.js +4 -3
  63. package/dist/validate-backlog-sync.js +7 -84
  64. package/dist/validate-skills-spec.js +4 -3
  65. package/dist/validate.js +7 -107
  66. package/dist/wu-block.js +3 -3
  67. package/dist/wu-claim.js +208 -98
  68. package/dist/wu-cleanup.js +5 -4
  69. package/dist/wu-create.js +71 -46
  70. package/dist/wu-delete.js +88 -60
  71. package/dist/wu-deps.js +6 -5
  72. package/dist/wu-done-check.js +34 -0
  73. package/dist/wu-done.js +60 -24
  74. package/dist/wu-edit.js +63 -28
  75. package/dist/wu-infer-lane.js +7 -6
  76. package/dist/wu-preflight.js +23 -81
  77. package/dist/wu-prep.js +125 -0
  78. package/dist/wu-prune.js +4 -3
  79. package/dist/wu-recover.js +88 -22
  80. package/dist/wu-repair.js +7 -6
  81. package/dist/wu-spawn.js +226 -270
  82. package/dist/wu-status.js +4 -3
  83. package/dist/wu-unblock.js +5 -5
  84. package/dist/wu-unlock-lane.js +4 -3
  85. package/dist/wu-validate.js +5 -4
  86. package/package.json +16 -7
  87. package/templates/core/.lumenflow/constraints.md.template +192 -0
  88. package/templates/core/.lumenflow/rules/git-safety.md.template +27 -0
  89. package/templates/core/.lumenflow/rules/wu-workflow.md.template +48 -0
  90. package/templates/core/AGENTS.md.template +60 -0
  91. package/templates/core/LUMENFLOW.md.template +255 -0
  92. package/templates/core/UPGRADING.md.template +121 -0
  93. package/templates/core/ai/onboarding/agent-safety-card.md.template +106 -0
  94. package/templates/core/ai/onboarding/first-wu-mistakes.md.template +198 -0
  95. package/templates/core/ai/onboarding/quick-ref-commands.md.template +186 -0
  96. package/templates/core/ai/onboarding/release-process.md.template +362 -0
  97. package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +159 -0
  98. package/templates/core/ai/onboarding/wu-create-checklist.md.template +117 -0
  99. package/templates/vendors/aider/.aider.conf.yml.template +27 -0
  100. package/templates/vendors/claude/.claude/CLAUDE.md.template +52 -0
  101. package/templates/vendors/claude/.claude/settings.json.template +49 -0
  102. package/templates/vendors/claude/.claude/skills/bug-classification/SKILL.md.template +192 -0
  103. package/templates/vendors/claude/.claude/skills/code-quality/SKILL.md.template +152 -0
  104. package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +155 -0
  105. package/templates/vendors/claude/.claude/skills/execution-memory/SKILL.md.template +304 -0
  106. package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +131 -0
  107. package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +164 -0
  108. package/templates/vendors/claude/.claude/skills/library-first/SKILL.md.template +98 -0
  109. package/templates/vendors/claude/.claude/skills/lumenflow-gates/SKILL.md.template +87 -0
  110. package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +84 -0
  111. package/templates/vendors/claude/.claude/skills/ops-maintenance/SKILL.md.template +254 -0
  112. package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +189 -0
  113. package/templates/vendors/claude/.claude/skills/tdd-workflow/SKILL.md.template +139 -0
  114. package/templates/vendors/claude/.claude/skills/worktree-discipline/SKILL.md.template +138 -0
  115. package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +106 -0
  116. package/templates/vendors/cline/.clinerules.template +53 -0
  117. package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +34 -0
  118. package/templates/vendors/cursor/.cursor/rules.md.template +28 -0
  119. package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +34 -0
package/dist/wu-done.js CHANGED
@@ -31,10 +31,16 @@
31
31
  * WU-2542: This script imports utilities from @lumenflow/core package.
32
32
  * Full migration to thin shim pending @lumenflow/core CLI export implementation.
33
33
  */
34
+ // WU-1153: wu:done guard for uncommitted code_paths is implemented in core package
35
+ // The guard runs in executeWorktreeCompletion() before metadata transaction
36
+ // See: packages/@lumenflow/core/src/wu-done-validation.ts
34
37
  import { execSync } from 'node:child_process';
35
38
  import prettyMs from 'pretty-ms';
39
+ import { runGates } from './gates.js';
36
40
  import { getGitForCwd } from '@lumenflow/core/dist/git-adapter.js';
37
41
  import { die } from '@lumenflow/core/dist/error-handler.js';
42
+ // WU-1223: Location detection for worktree check
43
+ import { resolveLocation } from '@lumenflow/core/dist/context/location-resolver.js';
38
44
  import { existsSync, readFileSync, mkdirSync, appendFileSync, unlinkSync, statSync } from 'node:fs';
39
45
  import path from 'node:path';
40
46
  // WU-1825: Import from unified code-path-validator (consolidates 3 validators)
@@ -53,7 +59,9 @@ validateAllPreCommitHooks,
53
59
  validateTypeVsCodePathsPreflight, buildTypeVsCodePathsErrorMessage, } from '@lumenflow/core/dist/wu-done-validators.js';
54
60
  // WU-1825: validateCodePathsExist moved to unified code-path-validator
55
61
  import { validateCodePathsExist } from '@lumenflow/core/dist/code-path-validator.js';
56
- import { BRANCHES, REMOTES, PATTERNS, DEFAULTS, LOG_PREFIX, EMOJI, GIT, SESSION, WU_STATUS, PKG_MANAGER, SCRIPTS, CLI_FLAGS, FILE_SYSTEM, EXIT_CODES, STRING_LITERALS, MICRO_WORKTREE_OPERATIONS, TELEMETRY_STEPS, SKIP_GATES_REASONS, CHECKPOINT_MESSAGES, } from '@lumenflow/core/dist/wu-constants.js';
62
+ import { BRANCHES, REMOTES, PATTERNS, DEFAULTS, LOG_PREFIX, EMOJI, GIT, SESSION, WU_STATUS, PKG_MANAGER, SCRIPTS, CLI_FLAGS, FILE_SYSTEM, EXIT_CODES, STRING_LITERALS, MICRO_WORKTREE_OPERATIONS, TELEMETRY_STEPS, SKIP_GATES_REASONS, CHECKPOINT_MESSAGES,
63
+ // WU-1223: Location types for worktree detection
64
+ CONTEXT_VALIDATION, } from '@lumenflow/core/dist/wu-constants.js';
57
65
  import { printGateFailureBox, printStatusPreview } from '@lumenflow/core/dist/wu-done-ui.js';
58
66
  import { ensureOnMain } from '@lumenflow/core/dist/wu-helpers.js';
59
67
  import { WU_PATHS } from '@lumenflow/core/dist/wu-paths.js';
@@ -85,6 +93,7 @@ import { SpawnStatus } from '@lumenflow/core/dist/spawn-registry-schema.js';
85
93
  // WU-1999: Exposure validation for UI pairing
86
94
  // WU-2022: Feature accessibility validation (blocking)
87
95
  import { validateExposure, validateFeatureAccessibility, } from '@lumenflow/core/dist/wu-validation.js';
96
+ import { ensureCleanWorktree } from './wu-done-check.js';
88
97
  // WU-1588: Memory layer constants
89
98
  const MEMORY_SIGNAL_TYPES = {
90
99
  WU_COMPLETION: 'wu_completion',
@@ -147,7 +156,7 @@ async function validateClaimMetadataBeforeGates(id, worktreePath, yamlStatus) {
147
156
  ` pnpm wu:repair-claim --id ${id}\n\n` +
148
157
  `After repair, retry:\n` +
149
158
  ` pnpm wu:done --id ${id}\n\n` +
150
- `See: docs/04-operations/_frameworks/lumenflow/agent/onboarding/troubleshooting-wu-done.md for more recovery options.`);
159
+ `See: https://lumenflow.dev/reference/troubleshooting-wu-done/ for more recovery options.`);
151
160
  }
152
161
  export function printExposureWarnings(wu, options = {}) {
153
162
  // Validate exposure
@@ -444,7 +453,7 @@ export async function isBranchAlreadyMerged(branch) {
444
453
  return false;
445
454
  }
446
455
  }
447
- // WU-1281: isDocsOnlyByPaths removed - use shouldSkipWebTests from path-classifiers.mjs
456
+ // WU-1281: isDocsOnlyByPaths removed - use shouldSkipWebTests from path-classifiers.ts
448
457
  // The validators already use shouldSkipWebTests via detectDocsOnlyByPaths wrapper.
449
458
  // Keeping the export for backward compatibility but re-exporting the canonical function.
450
459
  export { shouldSkipWebTests as isDocsOnlyByPaths } from '@lumenflow/core/dist/path-classifiers.js';
@@ -500,7 +509,7 @@ function getCommitHeaderLimit() {
500
509
  return DEFAULTS.MAX_COMMIT_SUBJECT; // Fallback if config is malformed or missing
501
510
  }
502
511
  }
503
- // ensureOnMain() moved to wu-helpers.mjs (WU-1256)
512
+ // ensureOnMain() moved to wu-helpers.ts (WU-1256)
504
513
  /**
505
514
  * Ensure working tree is clean before wu:done operations.
506
515
  *
@@ -718,7 +727,7 @@ async function ensureMainUpToDate() {
718
727
  * by calling /usr/bin/git directly or if PATH was not set up correctly.
719
728
  *
720
729
  * Context: WU-630 (detective layer, Layer 3 of 4)
721
- * See: docs/04-operations/_frameworks/lumenflow/02-playbook.md §4.6
730
+ * See: https://lumenflow.dev/reference/playbook/ §4.6
722
731
  */
723
732
  function runTripwireCheck() {
724
733
  const violations = scanLogForViolations();
@@ -763,7 +772,7 @@ function runTripwireCheck() {
763
772
  console.error(' 3. Escalate to human if critical files were deleted\n');
764
773
  }
765
774
  console.error('📖 See detailed recovery steps:');
766
- console.error(' docs/04-operations/_frameworks/lumenflow/02-playbook.md §4.6\n');
775
+ console.error(' https://lumenflow.dev/reference/playbook/ §4.6\n');
767
776
  console.error('🚫 DO NOT proceed with wu:done until violations are remediated.\n');
768
777
  console.error('Fix violations first, then retry wu:done.\n');
769
778
  // Also rotate log (cleanup old entries)
@@ -846,7 +855,7 @@ async function auditSkipCosGates(id, reason) {
846
855
  appendFileSync(auditPath, `${line}\n`, { encoding: FILE_SYSTEM.UTF8 });
847
856
  console.log(`${LOG_PREFIX.DONE} ${EMOJI.MEMO} Skip-COS-gates event logged to ${auditPath}`);
848
857
  }
849
- // WU-2308: validateAllPreCommitHooks moved to wu-done-validators.mjs
858
+ // WU-2308: validateAllPreCommitHooks moved to wu-done-validators.ts
850
859
  // Now accepts worktreePath parameter to run audit from worktree context
851
860
  /**
852
861
  * Check if node_modules in worktree may be stale
@@ -915,14 +924,13 @@ function checkNodeModulesStaleness(worktreePath) {
915
924
  * @param {boolean} options.isDocsOnly - Auto-detected docs-only from code_paths
916
925
  * @param {boolean} options.docsOnly - Explicit --docs-only flag from CLI
917
926
  */
918
- function runGatesInWorktree(worktreePath, id, options = {}) {
927
+ async function runGatesInWorktree(worktreePath, id, options = {}) {
919
928
  const { isDocsOnly = false, docsOnly = false } = options;
920
929
  console.log(`\n${LOG_PREFIX.DONE} Running gates in worktree: ${worktreePath}`);
921
930
  // Check for stale node_modules before running gates (prevents confusing failures)
922
931
  checkNodeModulesStaleness(worktreePath);
923
932
  // WU-1012: Use docs-only gates if explicit --docs-only flag OR auto-detected
924
933
  const useDocsOnlyGates = docsOnly || isDocsOnly;
925
- const gatesCmd = buildGatesCommand({ docsOnly, isDocsOnly });
926
934
  if (useDocsOnlyGates) {
927
935
  console.log(`${LOG_PREFIX.DONE} Using docs-only gates (skipping lint/typecheck/tests)`);
928
936
  if (docsOnly) {
@@ -931,12 +939,14 @@ function runGatesInWorktree(worktreePath, id, options = {}) {
931
939
  }
932
940
  const startTime = Date.now();
933
941
  try {
934
- // WU-1230: Pass WU_ID to validator for context-aware validation
935
- execSync(gatesCmd, {
942
+ const ok = Boolean(await runGates({
936
943
  cwd: worktreePath,
937
- stdio: 'inherit',
938
- env: { ...process.env, WU_ID: id },
939
- });
944
+ docsOnly: useDocsOnlyGates,
945
+ coverageMode: undefined,
946
+ }));
947
+ if (!ok) {
948
+ throw new Error('Gates failed');
949
+ }
940
950
  const duration = Date.now() - startTime;
941
951
  console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Gates passed in ${prettyMs(duration)}`);
942
952
  emitTelemetry({ script: 'wu-done', wu_id: id, step: 'gates', ok: true, duration_ms: duration });
@@ -998,13 +1008,13 @@ async function validateStagedFiles(id, isDocsOnly = false) {
998
1008
  }
999
1009
  }
1000
1010
  // Note: updateStatusRemoveInProgress, addToStatusCompleted, and moveWUToDoneBacklog
1001
- // have been extracted to tools/lib/wu-status-updater.mjs and imported above (WU-1163)
1011
+ // have been extracted to tools/lib/wu-status-updater.ts and imported above (WU-1163)
1002
1012
  //
1003
- // Note: ensureStamp has been replaced with createStamp from tools/lib/stamp-utils.mjs (WU-1163)
1013
+ // Note: ensureStamp has been replaced with createStamp from tools/lib/stamp-utils.ts (WU-1163)
1004
1014
  //
1005
1015
  // Note: readWUPreferWorktree, detectCurrentWorktree, defaultWorktreeFrom, detectWorkspaceMode,
1006
1016
  // defaultBranchFrom, branchExists, runCleanup have been extracted to
1007
- // tools/lib/wu-done-validators.mjs and imported above (WU-1215)
1017
+ // tools/lib/wu-done-validators.ts and imported above (WU-1215)
1008
1018
  /**
1009
1019
  * Validate Branch-Only mode requirements before proceeding
1010
1020
  * @param {string} laneBranch - Expected lane branch name
@@ -1774,8 +1784,7 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
1774
1784
  // Branch-Only mode: run gates in-place (current directory on lane branch)
1775
1785
  console.log(`\n${LOG_PREFIX.DONE} Running gates in Branch-Only mode (in-place on lane branch)`);
1776
1786
  // WU-1012: Use docs-only gates if explicit --docs-only flag OR auto-detected
1777
- const useDocsOnlyGates = args.docsOnly || isDocsOnly;
1778
- const gatesCmd = buildGatesCommand({ docsOnly: Boolean(args.docsOnly), isDocsOnly });
1787
+ const useDocsOnlyGates = Boolean(args.docsOnly) || Boolean(isDocsOnly);
1779
1788
  if (useDocsOnlyGates) {
1780
1789
  console.log(`${LOG_PREFIX.DONE} Using docs-only gates (skipping lint/typecheck/tests)`);
1781
1790
  if (args.docsOnly) {
@@ -1784,7 +1793,10 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
1784
1793
  }
1785
1794
  const startTime = Date.now();
1786
1795
  try {
1787
- execSync(gatesCmd, { stdio: 'inherit' });
1796
+ const ok = Boolean(await runGates({ docsOnly: useDocsOnlyGates }));
1797
+ if (!ok) {
1798
+ throw new Error('Gates failed');
1799
+ }
1788
1800
  const duration = Date.now() - startTime;
1789
1801
  console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Gates passed in ${prettyMs(duration)}`);
1790
1802
  emitTelemetry({
@@ -1818,7 +1830,10 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
1818
1830
  else if (worktreePath && existsSync(worktreePath)) {
1819
1831
  // Worktree mode: run gates in the dedicated worktree
1820
1832
  // WU-1012: Pass both auto-detected and explicit docs-only flags
1821
- runGatesInWorktree(worktreePath, id, { isDocsOnly, docsOnly: Boolean(args.docsOnly) });
1833
+ await runGatesInWorktree(worktreePath, id, {
1834
+ isDocsOnly,
1835
+ docsOnly: Boolean(args.docsOnly),
1836
+ });
1822
1837
  }
1823
1838
  else {
1824
1839
  die(`Worktree not found (${worktreePath || 'unknown'}). Gates must run in the lane worktree.\n` +
@@ -1855,7 +1870,7 @@ async function executeGates({ id, args, isBranchOnly, isDocsOnly, worktreePath,
1855
1870
  console.error(`\n${LOG_PREFIX.DONE} ${EMOJI.FAILURE} COS governance gates failed`);
1856
1871
  console.error('\nTo fix:');
1857
1872
  console.error(' 1. Add required evidence to governance.evidence field in WU YAML');
1858
- console.error(' 2. See: docs/04-operations/_frameworks/cos/evidence-format.md');
1873
+ console.error(' 2. See: https://lumenflow.dev/reference/evidence-format/');
1859
1874
  console.error('\nEmergency bypass (creates audit trail):');
1860
1875
  console.error(` pnpm wu:done --id ${id} --skip-cos-gates --reason "explanation"`);
1861
1876
  die('Abort: WU not completed. Fix governance evidence and retry pnpm wu:done.');
@@ -1908,8 +1923,22 @@ function printStateHUD({ id, docMain, isBranchOnly, isDocsOnly, derivedWorktree,
1908
1923
  async function main() {
1909
1924
  // Allow pre-push hook to recognize wu:done automation (WU-1030)
1910
1925
  process.env.LUMENFLOW_WU_TOOL = 'wu-done';
1911
- // Validate CLI arguments and WU ID format (extracted to wu-done-validators.mjs)
1926
+ // Validate CLI arguments and WU ID format (extracted to wu-done-validators.ts)
1912
1927
  const { args, id } = validateInputs(process.argv);
1928
+ // WU-1223: Check if running from worktree - wu:done now requires main checkout
1929
+ // Agents should use wu:prep from worktree, then wu:done from main
1930
+ const { LOCATION_TYPES } = CONTEXT_VALIDATION;
1931
+ const currentLocation = await resolveLocation();
1932
+ if (currentLocation.type === LOCATION_TYPES.WORKTREE) {
1933
+ die(`${EMOJI.FAILURE} wu:done must be run from main checkout, not from a worktree.\n\n` +
1934
+ `Current location: ${currentLocation.cwd}\n\n` +
1935
+ `WU-1223 NEW WORKFLOW:\n` +
1936
+ ` 1. From worktree, run: pnpm wu:prep --id ${id}\n` +
1937
+ ` (This runs gates and prepares for completion)\n\n` +
1938
+ ` 2. From main, run: cd ${currentLocation.mainCheckout} && pnpm wu:done --id ${id}\n` +
1939
+ ` (This does merge + cleanup only)\n\n` +
1940
+ `Use wu:prep to run gates in the worktree, then wu:done from main for merge/cleanup.`);
1941
+ }
1913
1942
  // Detect workspace mode and calculate paths (WU-1215: extracted to validators module)
1914
1943
  const pathInfo = await detectModeAndPaths(id, args);
1915
1944
  const { WU_PATH, STATUS_PATH, BACKLOG_PATH, STAMPS_DIR, docMain, isBranchOnly, derivedWorktree, docForValidation: initialDocForValidation, isDocsOnly, } = pathInfo;
@@ -1933,6 +1962,11 @@ async function main() {
1933
1962
  }
1934
1963
  const effectiveDerivedWorktree = effectiveBranchOnly ? null : derivedWorktree;
1935
1964
  const effectiveWorktreePath = effectiveBranchOnly ? null : resolvedWorktreePath;
1965
+ // WU-1169: Ensure worktree is clean before proceeding
1966
+ // This prevents WU-1943 rollback loops if rebase fails due to dirty state
1967
+ if (effectiveWorktreePath && existsSync(effectiveWorktreePath)) {
1968
+ await ensureCleanWorktree(effectiveWorktreePath);
1969
+ }
1936
1970
  // Pre-flight checks (WU-1215: extracted to executePreFlightChecks function)
1937
1971
  const preFlightResult = await executePreFlightChecks({
1938
1972
  id,
@@ -1977,7 +2011,9 @@ async function main() {
1977
2011
  // WU-2308: Pass worktreePath to run audit from worktree (checks fixed deps, not stale main deps)
1978
2012
  // WU-1145: Skip pre-flight when skipGates is true (pre-flight runs gates which was already skipped)
1979
2013
  if (!args.skipGates) {
1980
- const hookResult = validateAllPreCommitHooks(id, worktreePath);
2014
+ const hookResult = await validateAllPreCommitHooks(id, worktreePath, {
2015
+ runGates: ({ cwd }) => runGates({ cwd, docsOnly: false }),
2016
+ });
1981
2017
  if (!hookResult.valid) {
1982
2018
  die('Pre-flight validation failed. Fix hook issues and try again.');
1983
2019
  }
package/dist/wu-edit.js CHANGED
@@ -24,9 +24,8 @@
24
24
  * pnpm wu:edit --id WU-123 --acceptance "Criterion 1" --acceptance "Criterion 2"
25
25
  *
26
26
  * Part of WU-1274: Add wu:edit command for spec-only changes
27
- * @see {@link tools/lib/micro-worktree.mjs} - Shared micro-worktree logic
27
+ * @see {@link packages/@lumenflow/cli/src/lib/micro-worktree.ts} - Shared micro-worktree logic
28
28
  */
29
- import { fileURLToPath } from 'node:url';
30
29
  import { getGitForCwd, createGitForPath } from '@lumenflow/core/dist/git-adapter.js';
31
30
  import { die } from '@lumenflow/core/dist/error-handler.js';
32
31
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
@@ -182,19 +181,30 @@ const EDIT_OPTIONS = {
182
181
  codePaths: {
183
182
  name: 'codePaths',
184
183
  flags: '--code-paths <path>',
185
- description: 'Code path (repeatable, replaces existing; use --append to add)',
184
+ description: 'Code path (repeatable, appends to existing; use --replace-code-paths to overwrite)',
186
185
  isRepeatable: true,
187
186
  },
187
+ replaceCodePaths: {
188
+ name: 'replaceCodePaths',
189
+ flags: '--replace-code-paths',
190
+ description: 'Replace existing code_paths instead of appending',
191
+ },
188
192
  risks: {
189
193
  name: 'risks',
190
194
  flags: '--risks <risk>',
191
- description: 'Risk entry (repeatable, replaces existing; use --append to add)',
195
+ description: 'Risk entry (repeatable, appends to existing; use --replace-risks to overwrite)',
192
196
  isRepeatable: true,
193
197
  },
198
+ replaceRisks: {
199
+ name: 'replaceRisks',
200
+ flags: '--replace-risks',
201
+ description: 'Replace existing risks instead of appending',
202
+ },
203
+ // WU-1225: Deprecated --append flag (kept for backwards compatibility)
194
204
  append: {
195
205
  name: 'append',
196
206
  flags: '--append',
197
- description: 'Append to existing array values instead of replacing (for --code-paths, --test-paths-*, --blocked-by, --add-dep)',
207
+ description: '[DEPRECATED] Arrays now append by default. Use --replace-* flags to replace.',
198
208
  },
199
209
  // WU-1456: Add lane reassignment support
200
210
  lane: {
@@ -228,12 +238,22 @@ const EDIT_OPTIONS = {
228
238
  blockedBy: {
229
239
  name: 'blockedBy',
230
240
  flags: '--blocked-by <wuIds>',
231
- description: 'Comma-separated WU IDs that block this WU (replaces existing; use --append to add)',
241
+ description: 'Comma-separated WU IDs that block this WU (appends to existing; use --replace-blocked-by to overwrite)',
242
+ },
243
+ replaceBlockedBy: {
244
+ name: 'replaceBlockedBy',
245
+ flags: '--replace-blocked-by',
246
+ description: 'Replace existing blocked_by instead of appending',
232
247
  },
233
248
  addDep: {
234
249
  name: 'addDep',
235
250
  flags: '--add-dep <wuIds>',
236
- description: 'Comma-separated WU IDs to add to dependencies array (replaces existing; use --append to add)',
251
+ description: 'Comma-separated WU IDs to add to dependencies array (appends to existing; use --replace-dependencies to overwrite)',
252
+ },
253
+ replaceDependencies: {
254
+ name: 'replaceDependencies',
255
+ flags: '--replace-dependencies',
256
+ description: 'Replace existing dependencies instead of appending',
237
257
  },
238
258
  };
239
259
  /**
@@ -334,7 +354,9 @@ function parseArgs() {
334
354
  EDIT_OPTIONS.replaceNotes,
335
355
  EDIT_OPTIONS.replaceAcceptance,
336
356
  EDIT_OPTIONS.codePaths,
357
+ EDIT_OPTIONS.replaceCodePaths,
337
358
  EDIT_OPTIONS.risks,
359
+ EDIT_OPTIONS.replaceRisks,
338
360
  EDIT_OPTIONS.append,
339
361
  // WU-1390: Add test path flags
340
362
  WU_OPTIONS.testPathsManual,
@@ -350,7 +372,9 @@ function parseArgs() {
350
372
  EDIT_OPTIONS.phase,
351
373
  // WU-2564: Add blocked_by and dependencies
352
374
  EDIT_OPTIONS.blockedBy,
375
+ EDIT_OPTIONS.replaceBlockedBy,
353
376
  EDIT_OPTIONS.addDep,
377
+ EDIT_OPTIONS.replaceDependencies,
354
378
  // WU-1039: Add exposure for done WU metadata updates
355
379
  WU_OPTIONS.exposure,
356
380
  ],
@@ -702,7 +726,7 @@ export function applyEdits(wu, opts) {
702
726
  }
703
727
  updated.phase = phaseNum;
704
728
  }
705
- // Handle repeatable --code-paths flags (WU-1388: replace by default, append with --append)
729
+ // Handle repeatable --code-paths flags (WU-1225: append by default, replace with --replace-code-paths)
706
730
  // WU-1816: Split comma-separated string into array (same pattern as test paths)
707
731
  // WU-1870: Fix to split comma-separated values WITHIN array elements (Commander passes ['a,b'] not 'a,b')
708
732
  if (opts.codePaths && opts.codePaths.length > 0) {
@@ -716,9 +740,12 @@ export function applyEdits(wu, opts) {
716
740
  .split(',')
717
741
  .map((p) => p.trim())
718
742
  .filter(Boolean);
719
- updated.code_paths = mergeArrayField(wu.code_paths, codePaths, opts.append);
743
+ // WU-1225: Invert logic - append by default, replace with --replace-code-paths
744
+ // Also support legacy --append flag for backwards compatibility
745
+ const shouldAppend = !opts.replaceCodePaths || opts.append;
746
+ updated.code_paths = mergeArrayField(wu.code_paths, codePaths, shouldAppend);
720
747
  }
721
- // WU-1073: Handle repeatable --risks flags (replace by default, append with --append)
748
+ // WU-1225: Handle repeatable --risks flags (append by default, replace with --replace-risks)
722
749
  // Split comma-separated values within each entry for consistency with other list fields
723
750
  if (opts.risks && opts.risks.length > 0) {
724
751
  const rawRisks = opts.risks;
@@ -731,9 +758,12 @@ export function applyEdits(wu, opts) {
731
758
  .split(',')
732
759
  .map((risk) => risk.trim())
733
760
  .filter(Boolean);
734
- updated.risks = mergeArrayField(wu.risks, risks, opts.append);
761
+ // WU-1225: Invert logic - append by default
762
+ const shouldAppend = !opts.replaceRisks || opts.append;
763
+ updated.risks = mergeArrayField(wu.risks, risks, shouldAppend);
735
764
  }
736
765
  // WU-1390: Handle test path flags (DRY refactor)
766
+ // WU-1225: Test paths now append by default (consistent with --acceptance and --code-paths)
737
767
  const testPathMappings = [
738
768
  { optKey: 'testPathsManual', field: 'manual' },
739
769
  { optKey: 'testPathsUnit', field: 'unit' },
@@ -754,28 +784,32 @@ export function applyEdits(wu, opts) {
754
784
  .map((p) => p.trim())
755
785
  .filter(Boolean);
756
786
  updated.tests = updated.tests || {};
757
- updated.tests[field] = mergeArrayField(wu.tests?.[field], paths, opts.append);
787
+ // WU-1225: Append by default (no individual replace flags for test paths yet)
788
+ const shouldAppend = true;
789
+ updated.tests[field] = mergeArrayField(wu.tests?.[field], paths, shouldAppend);
758
790
  }
759
791
  }
760
792
  // WU-2564: Handle --blocked-by flag
761
- // Comma-separated WU IDs that block this WU
793
+ // WU-1225: Append by default, replace with --replace-blocked-by
762
794
  if (opts.blockedBy) {
763
795
  const rawBlockedBy = opts.blockedBy;
764
796
  const blockedByIds = rawBlockedBy
765
797
  .split(',')
766
798
  .map((id) => id.trim())
767
799
  .filter(Boolean);
768
- updated.blocked_by = mergeArrayField(wu.blocked_by, blockedByIds, opts.append);
800
+ const shouldAppend = !opts.replaceBlockedBy || opts.append;
801
+ updated.blocked_by = mergeArrayField(wu.blocked_by, blockedByIds, shouldAppend);
769
802
  }
770
803
  // WU-2564: Handle --add-dep flag
771
- // Comma-separated WU IDs to add to dependencies array
804
+ // WU-1225: Append by default, replace with --replace-dependencies
772
805
  if (opts.addDep) {
773
806
  const rawAddDep = opts.addDep;
774
807
  const depIds = rawAddDep
775
808
  .split(',')
776
809
  .map((id) => id.trim())
777
810
  .filter(Boolean);
778
- updated.dependencies = mergeArrayField(wu.dependencies, depIds, opts.append);
811
+ const shouldAppend = !opts.replaceDependencies || opts.append;
812
+ updated.dependencies = mergeArrayField(wu.dependencies, depIds, shouldAppend);
779
813
  }
780
814
  // WU-1039: Handle --exposure flag with validation
781
815
  if (opts.exposure) {
@@ -845,21 +879,20 @@ async function main() {
845
879
  ' --description <text> Update description field\n' +
846
880
  ' --acceptance <text> Append acceptance criteria (repeatable; use --replace-acceptance to overwrite)\n' +
847
881
  ' --notes <text> Append to notes (use --replace-notes to overwrite)\n' +
848
- ' --replace-notes Replace existing notes instead of appending\n' +
849
- ' --replace-acceptance Replace existing acceptance instead of appending\n' +
850
- ' --code-paths <paths> Replace code paths (repeatable; use --append to add)\n' +
851
- ' --risks <risk> Replace risks (repeatable; use --append to add)\n' +
882
+ ' --code-paths <paths> Append code paths (repeatable; use --replace-code-paths to overwrite)\n' +
883
+ ' --risks <risk> Append risks (repeatable; use --replace-risks to overwrite)\n' +
852
884
  ' --lane <lane> Update lane assignment (e.g., "Operations: Tooling")\n' +
853
885
  ' --type <type> Update WU type (feature, bug, refactor, documentation)\n' +
854
886
  ' --priority <priority> Update priority (P0, P1, P2, P3)\n' +
855
887
  ' --initiative <initId> Update initiative (bidirectional update)\n' +
856
888
  ' --phase <number> Update phase within initiative\n' +
857
- ' --test-paths-manual <t> Add manual test descriptions (repeatable; use --append to add)\n' +
858
- ' --test-paths-unit <path> Add unit test paths (repeatable; use --append to add)\n' +
859
- ' --test-paths-e2e <path> Add e2e test paths (repeatable; use --append to add)\n' +
860
- ' --blocked-by <wuIds> WU IDs that block this WU (comma-separated; use --append to add)\n' +
861
- ' --add-dep <wuIds> Add WU IDs to dependencies (comma-separated; use --append to add)\n' +
862
- ' --exposure <type> Update exposure level (ui, api, backend-only, documentation)');
889
+ ' --test-paths-manual <t> Append manual test descriptions (repeatable)\n' +
890
+ ' --test-paths-unit <path> Append unit test paths (repeatable)\n' +
891
+ ' --test-paths-e2e <path> Append e2e test paths (repeatable)\n' +
892
+ ' --blocked-by <wuIds> Append WU IDs that block this WU (use --replace-blocked-by to overwrite)\n' +
893
+ ' --add-dep <wuIds> Append WU IDs to dependencies (use --replace-dependencies to overwrite)\n' +
894
+ ' --exposure <type> Update exposure level (ui, api, backend-only, documentation)\n\n' +
895
+ 'Note: All array fields now append by default (WU-1225). Use --replace-* flags to overwrite.');
863
896
  }
864
897
  // Apply edits to get updated WU
865
898
  const updatedWU = applyEdits(originalWU, opts);
@@ -1001,8 +1034,10 @@ async function main() {
1001
1034
  displayReadinessSummary(id);
1002
1035
  }
1003
1036
  }
1004
- // Guard main() execution for testability (WU-1366)
1005
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
1037
+ // WU-1181: Use import.meta.main instead of process.argv[1] comparison
1038
+ // The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
1039
+ // path but import.meta.url resolves to the real path - they never match
1040
+ if (import.meta.main) {
1006
1041
  main().catch((err) => {
1007
1042
  console.error(`${PREFIX} ❌ ${err.message}`);
1008
1043
  process.exit(EXIT_CODES.ERROR);
@@ -3,14 +3,14 @@
3
3
  * WU Lane Inference CLI (WU-908)
4
4
  *
5
5
  * Suggests sub-lane for a WU based on code paths and description.
6
- * Wrapper around lib/lane-inference.mjs for standalone CLI usage.
6
+ * Wrapper around lib/lane-inference.ts for standalone CLI usage.
7
7
  *
8
8
  * Usage:
9
9
  * # Infer from existing WU
10
- * node tools/wu-infer-lane.mjs --id WU-123
10
+ * node tools/wu-infer-lane.ts --id WU-123
11
11
  *
12
12
  * # Infer from manual inputs
13
- * node tools/wu-infer-lane.mjs --paths "tools/**" "docs/**" --desc "Tooling improvements"
13
+ * node tools/wu-infer-lane.ts --paths "tools/**" "docs/**" --desc "Tooling improvements"
14
14
  *
15
15
  * Returns suggested lane and confidence score (0-100).
16
16
  */
@@ -128,9 +128,10 @@ async function main() {
128
128
  die(`Lane inference failed: ${err.message}`);
129
129
  }
130
130
  }
131
- // Guard main() for testability (WU-1366)
132
- import { fileURLToPath } from 'node:url';
131
+ // WU-1181: Use import.meta.main instead of process.argv[1] comparison
132
+ // The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
133
+ // path but import.meta.url resolves to the real path - they never match
133
134
  import { runCLI } from './cli-entry-point.js';
134
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
135
+ if (import.meta.main) {
135
136
  runCLI(main);
136
137
  }
@@ -5,6 +5,9 @@
5
5
  * WU-1803: Fast validation of code_paths and test paths before gates run.
6
6
  * Completes in under 5 seconds vs 2+ minutes for full gates.
7
7
  *
8
+ * WU-1180: Migrated from deprecated parseWUArgs to createWUParser for
9
+ * proper Commander --help output and consistency with other WU commands.
10
+ *
8
11
  * This catches YAML mismatches early, preventing wasted time running full
9
12
  * gates only to fail on code_paths validation at the end of wu:done.
10
13
  *
@@ -17,78 +20,14 @@
17
20
  * - test file paths exist (unit, e2e, integration)
18
21
  * - WU YAML schema is valid
19
22
  */
20
- import { fileURLToPath } from 'node:url';
21
23
  import { existsSync } from 'node:fs';
22
- import { parseWUArgs } from '@lumenflow/core/dist/arg-parser.js';
24
+ import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
23
25
  import { validatePreflight, formatPreflightResult, } from '@lumenflow/core/dist/wu-preflight-validators.js';
24
26
  import { PATTERNS, EXIT_CODES, LOG_PREFIX, EMOJI } from '@lumenflow/core/dist/wu-constants.js';
25
27
  import { defaultWorktreeFrom, WU_PATHS } from '@lumenflow/core/dist/wu-paths.js';
26
28
  import { readWURaw } from '@lumenflow/core/dist/wu-yaml.js';
29
+ import { die } from '@lumenflow/core/dist/error-handler.js';
27
30
  /* eslint-disable security/detect-non-literal-fs-filename */
28
- /**
29
- * Parse command-line arguments
30
- * @param {string[]} argv - Process arguments
31
- * @returns {object} Parsed arguments
32
- */
33
- function parseArgs(argv) {
34
- const args = parseWUArgs(argv);
35
- // Handle help
36
- if (args.help) {
37
- return { help: true };
38
- }
39
- // Validate WU ID
40
- if (!args.id) {
41
- return { error: 'Missing required argument: --id WU-XXX' };
42
- }
43
- const id = args.id.toUpperCase();
44
- if (!PATTERNS.WU_ID.test(id)) {
45
- return { error: `Invalid WU ID format: ${args.id}. Expected WU-NNN` };
46
- }
47
- return {
48
- id,
49
- worktree: args.worktree || null,
50
- help: false,
51
- };
52
- }
53
- /**
54
- * Display help message
55
- */
56
- function showHelp() {
57
- console.log(`
58
- WU Preflight Validation - Fast code_paths and test paths check
59
-
60
- Usage:
61
- pnpm wu:preflight --id WU-XXX [OPTIONS]
62
-
63
- Options:
64
- --id <WU-ID> WU ID to validate (required)
65
- --worktree <path> Worktree path to validate files in (auto-detected if not provided)
66
- --help, -h Show this help
67
-
68
- Description:
69
- Validates code_paths and test file paths exist BEFORE running full gates.
70
- Completes in under 5 seconds vs 2+ minutes for gates.
71
-
72
- This prevents wasting time running full gates only to fail on
73
- code_paths validation at the end of wu:done.
74
-
75
- Checks performed:
76
- ${EMOJI.SUCCESS} code_paths files exist in worktree/main
77
- ${EMOJI.SUCCESS} Test file paths exist (unit, e2e, integration)
78
- ${EMOJI.SUCCESS} WU YAML schema is valid (required fields present)
79
- ${EMOJI.SUCCESS} Manual tests are skipped (descriptions, not files)
80
-
81
- Recommended workflow:
82
- 1. Implement feature/fix
83
- 2. Run: pnpm wu:preflight --id WU-XXX (fast check)
84
- 3. Run: pnpm gates (full validation)
85
- 4. Run: pnpm wu:done --id WU-XXX (complete WU)
86
-
87
- Example:
88
- pnpm wu:preflight --id WU-1803
89
- pnpm wu:preflight --id WU-1803 --worktree worktrees/operations-gates-wu-1803
90
- `);
91
- }
92
31
  /**
93
32
  * Detect worktree path from WU YAML or calculate from lane
94
33
  * @param {string} id - WU ID
@@ -116,23 +55,23 @@ function detectWorktreePath(id) {
116
55
  */
117
56
  async function main() {
118
57
  const PREFIX = LOG_PREFIX.PREFLIGHT;
119
- const args = parseArgs(process.argv);
120
- // Handle help
121
- if (args.help) {
122
- showHelp();
123
- process.exit(EXIT_CODES.SUCCESS);
124
- }
125
- // Handle parse errors
126
- if (args.error) {
127
- console.error(`${PREFIX} ${EMOJI.FAILURE} ${args.error}`);
128
- console.error(`${PREFIX} Run: pnpm wu:preflight --help for usage`);
129
- process.exit(EXIT_CODES.ERROR);
58
+ // WU-1180: Use createWUParser for proper Commander help output
59
+ const args = createWUParser({
60
+ name: 'wu-preflight',
61
+ description: 'Fast validation of code_paths and test paths before gates run. ' +
62
+ 'Completes in under 5 seconds vs 2+ minutes for full gates.',
63
+ options: [WU_OPTIONS.id, WU_OPTIONS.worktree],
64
+ required: ['id'],
65
+ allowPositionalId: true,
66
+ });
67
+ const id = args.id.toUpperCase();
68
+ if (!PATTERNS.WU_ID.test(id)) {
69
+ die(`Invalid WU ID format: ${args.id}. Expected WU-NNN`);
130
70
  }
131
- const { id, worktree } = args;
132
71
  console.log(`${PREFIX} Preflight Validation for ${id}`);
133
72
  console.log(`${PREFIX} ${'='.repeat(30)}\n`);
134
73
  // Determine worktree path
135
- let worktreePath = worktree;
74
+ let worktreePath = args.worktree || null;
136
75
  if (!worktreePath) {
137
76
  worktreePath = detectWorktreePath(id);
138
77
  if (worktreePath) {
@@ -160,9 +99,12 @@ async function main() {
160
99
  process.exit(EXIT_CODES.SUCCESS);
161
100
  }
162
101
  // Guard main() for testability
102
+ // WU-1071: Use import.meta.main instead of process.argv[1] comparison
103
+ // The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
104
+ // path but import.meta.url resolves to the real path - they never match
163
105
  import { runCLI } from './cli-entry-point.js';
164
- if (process.argv[1] === fileURLToPath(import.meta.url)) {
106
+ if (import.meta.main) {
165
107
  runCLI(main);
166
108
  }
167
109
  // Export for testing
168
- export { parseArgs, detectWorktreePath };
110
+ export { detectWorktreePath };