@lumenflow/core 1.0.0 → 1.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/arg-parser.d.ts +6 -0
  2. package/dist/arg-parser.js +57 -1
  3. package/dist/backlog-generator.js +1 -1
  4. package/dist/backlog-sync-validator.js +3 -3
  5. package/dist/branch-check.d.ts +21 -0
  6. package/dist/branch-check.js +77 -0
  7. package/dist/cli/is-agent-branch.d.ts +11 -0
  8. package/dist/cli/is-agent-branch.js +15 -0
  9. package/dist/code-paths-overlap.js +2 -2
  10. package/dist/error-handler.d.ts +1 -0
  11. package/dist/error-handler.js +4 -1
  12. package/dist/git-adapter.d.ts +23 -0
  13. package/dist/git-adapter.js +38 -2
  14. package/dist/index.d.ts +3 -0
  15. package/dist/index.js +5 -0
  16. package/dist/lane-checker.d.ts +36 -3
  17. package/dist/lane-checker.js +128 -17
  18. package/dist/lane-inference.js +3 -4
  19. package/dist/lumenflow-config-schema.d.ts +125 -0
  20. package/dist/lumenflow-config-schema.js +76 -0
  21. package/dist/lumenflow-home.d.ts +130 -0
  22. package/dist/lumenflow-home.js +208 -0
  23. package/dist/manual-test-validator.js +1 -1
  24. package/dist/orchestration-rules.d.ts +1 -1
  25. package/dist/orchestration-rules.js +2 -2
  26. package/dist/orphan-detector.d.ts +16 -0
  27. package/dist/orphan-detector.js +24 -0
  28. package/dist/path-classifiers.d.ts +1 -1
  29. package/dist/path-classifiers.js +1 -1
  30. package/dist/rebase-artifact-cleanup.d.ts +17 -0
  31. package/dist/rebase-artifact-cleanup.js +49 -8
  32. package/dist/spawn-strategy.d.ts +53 -0
  33. package/dist/spawn-strategy.js +106 -0
  34. package/dist/spec-branch-helpers.d.ts +118 -0
  35. package/dist/spec-branch-helpers.js +192 -0
  36. package/dist/stamp-utils.d.ts +10 -0
  37. package/dist/stamp-utils.js +17 -19
  38. package/dist/token-counter.js +2 -2
  39. package/dist/wu-consistency-checker.d.ts +2 -0
  40. package/dist/wu-consistency-checker.js +40 -6
  41. package/dist/wu-constants.d.ts +98 -3
  42. package/dist/wu-constants.js +108 -3
  43. package/dist/wu-create-validators.d.ts +40 -2
  44. package/dist/wu-create-validators.js +76 -2
  45. package/dist/wu-done-branch-only.js +9 -0
  46. package/dist/wu-done-branch-utils.d.ts +10 -0
  47. package/dist/wu-done-branch-utils.js +31 -0
  48. package/dist/wu-done-cleanup.d.ts +8 -0
  49. package/dist/wu-done-cleanup.js +122 -0
  50. package/dist/wu-done-docs-generate.d.ts +73 -0
  51. package/dist/wu-done-docs-generate.js +108 -0
  52. package/dist/wu-done-docs-only.d.ts +20 -0
  53. package/dist/wu-done-docs-only.js +65 -0
  54. package/dist/wu-done-errors.d.ts +17 -0
  55. package/dist/wu-done-errors.js +24 -0
  56. package/dist/wu-done-inputs.d.ts +12 -0
  57. package/dist/wu-done-inputs.js +51 -0
  58. package/dist/wu-done-metadata.d.ts +100 -0
  59. package/dist/wu-done-metadata.js +193 -0
  60. package/dist/wu-done-paths.d.ts +69 -0
  61. package/dist/wu-done-paths.js +237 -0
  62. package/dist/wu-done-preflight.d.ts +48 -0
  63. package/dist/wu-done-preflight.js +185 -0
  64. package/dist/wu-done-validation.d.ts +82 -0
  65. package/dist/wu-done-validation.js +340 -0
  66. package/dist/wu-done-validators.d.ts +13 -409
  67. package/dist/wu-done-validators.js +9 -1225
  68. package/dist/wu-done-worktree.d.ts +0 -1
  69. package/dist/wu-done-worktree.js +24 -30
  70. package/dist/wu-schema.js +4 -4
  71. package/dist/wu-spawn-skills.d.ts +19 -0
  72. package/dist/wu-spawn-skills.js +148 -0
  73. package/dist/wu-spawn.d.ts +17 -4
  74. package/dist/wu-spawn.js +113 -177
  75. package/dist/wu-validation.d.ts +1 -0
  76. package/dist/wu-validation.js +21 -1
  77. package/dist/wu-validator.d.ts +51 -0
  78. package/dist/wu-validator.js +108 -0
  79. package/package.json +12 -8
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @file wu-done-docs-generate.ts
3
+ * @description Documentation regeneration utilities for wu:done
4
+ *
5
+ * WU-1061: Integrate docs:generate into wu:done for @lumenflow/* changes
6
+ *
7
+ * Detects changes to files that affect generated documentation and triggers
8
+ * regeneration during wu:done (after gates pass, before metadata commit).
9
+ */
10
+ import { execSync } from 'node:child_process';
11
+ import { getGitForCwd } from './git-adapter.js';
12
+ import { STDIO } from './wu-constants.js';
13
+ /**
14
+ * Pathspecs for files that affect generated documentation.
15
+ * When any of these files change, docs:generate should be run.
16
+ *
17
+ * Based on .husky/hooks/docs-sync.mjs patterns, expanded to match
18
+ * all files that can affect CLI/config documentation.
19
+ */
20
+ export const DOC_SOURCE_PATHSPECS = [
21
+ 'tools/generate-cli-docs.ts',
22
+ 'packages/@lumenflow/core/src/arg-parser.ts',
23
+ 'packages/@lumenflow/core/src/lumenflow-config-schema.ts',
24
+ 'packages/@lumenflow/core/src/index.ts',
25
+ 'packages/@lumenflow/cli/package.json',
26
+ 'packages/@lumenflow/cli/src/',
27
+ ];
28
+ /**
29
+ * Output files generated by docs:generate.
30
+ * These files are staged before the metadata commit.
31
+ */
32
+ export const DOC_OUTPUT_FILES = [
33
+ 'apps/docs/src/content/docs/reference/cli.mdx',
34
+ 'apps/docs/src/content/docs/reference/config.mdx',
35
+ ];
36
+ /**
37
+ * Check if any doc-source files changed compared to the base branch.
38
+ * Uses git diff with pathspecs for efficient detection.
39
+ *
40
+ * @param baseBranch - Base branch for comparison (e.g., 'main', 'origin/main')
41
+ * @returns Promise<boolean> - True if doc-source files changed
42
+ */
43
+ export async function hasDocSourceChanges(baseBranch) {
44
+ try {
45
+ const gitAdapter = getGitForCwd();
46
+ const diff = await gitAdapter.raw([
47
+ 'diff',
48
+ `${baseBranch}...HEAD`,
49
+ '--name-only',
50
+ '--',
51
+ ...DOC_SOURCE_PATHSPECS,
52
+ ]);
53
+ return diff.trim().length > 0;
54
+ }
55
+ catch {
56
+ // Fail-safe: don't regenerate on git errors
57
+ return false;
58
+ }
59
+ }
60
+ /**
61
+ * Stage doc output files for the metadata commit.
62
+ *
63
+ * @returns Promise<void>
64
+ */
65
+ export async function stageDocOutputs() {
66
+ const gitAdapter = getGitForCwd();
67
+ await gitAdapter.add([...DOC_OUTPUT_FILES]);
68
+ }
69
+ /**
70
+ * Run turbo docs:generate to regenerate documentation.
71
+ * Turbo handles build dependencies and caching.
72
+ *
73
+ * @param repoRoot - Repository root directory
74
+ * @returns void
75
+ */
76
+ export function runDocsGenerate(repoRoot) {
77
+ execSync('pnpm turbo docs:generate', {
78
+ cwd: repoRoot,
79
+ stdio: STDIO.INHERIT,
80
+ encoding: 'utf-8',
81
+ });
82
+ }
83
+ /**
84
+ * Detect doc-source changes and regenerate docs if needed.
85
+ * This is the main integration point for wu:done.
86
+ *
87
+ * Call this BEFORE stageAndFormatMetadata() to include doc outputs
88
+ * in the single atomic commit.
89
+ *
90
+ * @param options - Detection and regeneration options
91
+ * @returns Promise<DocsRegenerationResult> - Whether docs changed and were regenerated
92
+ */
93
+ export async function maybeRegenerateAndStageDocs(options) {
94
+ const { baseBranch, repoRoot } = options;
95
+ // Check if doc-source files changed
96
+ const docsChanged = await hasDocSourceChanges(baseBranch);
97
+ if (!docsChanged) {
98
+ console.log('[wu:done] No doc-source changes detected, skipping docs:generate');
99
+ return { docsChanged: false, regenerated: false };
100
+ }
101
+ console.log('[wu:done] Doc-source changes detected, running turbo docs:generate...');
102
+ // Run turbo docs:generate (Turbo handles caching and dependencies)
103
+ runDocsGenerate(repoRoot);
104
+ // Stage the doc output files
105
+ await stageDocOutputs();
106
+ console.log('[wu:done] Documentation regenerated and staged');
107
+ return { docsChanged: true, regenerated: true };
108
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Docs-only detection utilities for wu:done
3
+ *
4
+ * WU-1234 + WU-1255 + WU-1539: Detect docs-only WUs from code_paths.
5
+ */
6
+ /**
7
+ * Detect docs-only WU from code_paths.
8
+ * Returns true if all code_paths are documentation paths only.
9
+ *
10
+ * Docs-only paths: docs/, ai/, .claude/, memory-bank/, README*, CLAUDE*.md
11
+ * NOT docs-only: tools/, scripts/ (these are code, not documentation)
12
+ *
13
+ * WU-1539: Fixed misclassification where tools/ was treated as docs-only
14
+ * but then rejected by validateDocsOnly(). tools/ should skip web tests
15
+ * but NOT be classified as docs-only.
16
+ *
17
+ * @param {string[]|null|undefined} codePaths - Array of file paths from WU YAML
18
+ * @returns {boolean} True if WU is docs-only (all paths are documentation)
19
+ */
20
+ export declare function detectDocsOnlyByPaths(codePaths: any): boolean;
@@ -0,0 +1,65 @@
1
+ /**
2
+ * Docs-only detection utilities for wu:done
3
+ *
4
+ * WU-1234 + WU-1255 + WU-1539: Detect docs-only WUs from code_paths.
5
+ */
6
+ /**
7
+ * Prefixes for paths that qualify as "docs-only" (no code changes).
8
+ * Unlike SKIP_TESTS_PREFIXES, this excludes tools/ and scripts/ because
9
+ * those contain code files that require full gate validation.
10
+ *
11
+ * WU-1539: Split from shouldSkipWebTests to fix docs-only misclassification.
12
+ * @constant {string[]}
13
+ */
14
+ const DOCS_ONLY_PREFIXES = Object.freeze(['docs/', 'ai/', '.claude/', 'memory-bank/']);
15
+ /**
16
+ * Root file patterns that qualify as docs-only.
17
+ * @constant {string[]}
18
+ */
19
+ const DOCS_ONLY_ROOT_FILES = Object.freeze(['readme', 'claude']);
20
+ /**
21
+ * Detect docs-only WU from code_paths.
22
+ * Returns true if all code_paths are documentation paths only.
23
+ *
24
+ * Docs-only paths: docs/, ai/, .claude/, memory-bank/, README*, CLAUDE*.md
25
+ * NOT docs-only: tools/, scripts/ (these are code, not documentation)
26
+ *
27
+ * WU-1539: Fixed misclassification where tools/ was treated as docs-only
28
+ * but then rejected by validateDocsOnly(). tools/ should skip web tests
29
+ * but NOT be classified as docs-only.
30
+ *
31
+ * @param {string[]|null|undefined} codePaths - Array of file paths from WU YAML
32
+ * @returns {boolean} True if WU is docs-only (all paths are documentation)
33
+ */
34
+ export function detectDocsOnlyByPaths(codePaths) {
35
+ if (!codePaths || !Array.isArray(codePaths) || codePaths.length === 0) {
36
+ return false;
37
+ }
38
+ return codePaths.every((filePath) => {
39
+ if (!filePath || typeof filePath !== 'string') {
40
+ return false;
41
+ }
42
+ const path = filePath.trim();
43
+ if (path.length === 0) {
44
+ return false;
45
+ }
46
+ // Check docs-only prefixes (docs/, ai/, .claude/, memory-bank/)
47
+ for (const prefix of DOCS_ONLY_PREFIXES) {
48
+ if (path.startsWith(prefix)) {
49
+ return true;
50
+ }
51
+ }
52
+ // Check if it's a markdown file (*.md)
53
+ if (path.endsWith('.md')) {
54
+ return true;
55
+ }
56
+ // Check root file patterns (README*, CLAUDE*.md)
57
+ const lowerPath = path.toLowerCase();
58
+ for (const pattern of DOCS_ONLY_ROOT_FILES) {
59
+ if (lowerPath.startsWith(pattern)) {
60
+ return true;
61
+ }
62
+ }
63
+ return false;
64
+ });
65
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Error helpers for wu:done validators
3
+ *
4
+ * WU-1049: Standardize error handling patterns across wu-done validator paths.
5
+ */
6
+ /**
7
+ * Create a validation error for wu:done validators.
8
+ */
9
+ export declare function createValidationError(message: any, details?: {}): import("./error-handler.js").WUError;
10
+ /**
11
+ * Create a file-not-found error for wu:done validators.
12
+ */
13
+ export declare function createFileNotFoundError(message: any, details?: {}): import("./error-handler.js").WUError;
14
+ /**
15
+ * Create a recovery error for wu:done recovery flows.
16
+ */
17
+ export declare function createRecoveryError(message: any, details?: {}): import("./error-handler.js").WUError;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Error helpers for wu:done validators
3
+ *
4
+ * WU-1049: Standardize error handling patterns across wu-done validator paths.
5
+ */
6
+ import { createError, ErrorCodes } from './error-handler.js';
7
+ /**
8
+ * Create a validation error for wu:done validators.
9
+ */
10
+ export function createValidationError(message, details = {}) {
11
+ return createError(ErrorCodes.VALIDATION_ERROR, message, details);
12
+ }
13
+ /**
14
+ * Create a file-not-found error for wu:done validators.
15
+ */
16
+ export function createFileNotFoundError(message, details = {}) {
17
+ return createError(ErrorCodes.FILE_NOT_FOUND, message, details);
18
+ }
19
+ /**
20
+ * Create a recovery error for wu:done recovery flows.
21
+ */
22
+ export function createRecoveryError(message, details = {}) {
23
+ return createError(ErrorCodes.RECOVERY_ERROR, message, details);
24
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Input parsing for wu:done validators.
3
+ */
4
+ /**
5
+ * Validates command-line inputs and WU ID format
6
+ * @param {string[]} argv - Process arguments
7
+ * @returns {{ args: object, id: string }} Parsed args and validated WU ID
8
+ */
9
+ export declare function validateInputs(argv: any): {
10
+ args: import("commander").OptionValues;
11
+ id: any;
12
+ };
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Input parsing for wu:done validators.
3
+ */
4
+ import { parseWUArgs } from './arg-parser.js';
5
+ import { die } from './error-handler.js';
6
+ import { EXIT_CODES, PATTERNS } from './wu-constants.js';
7
+ /**
8
+ * Validates command-line inputs and WU ID format
9
+ * @param {string[]} argv - Process arguments
10
+ * @returns {{ args: object, id: string }} Parsed args and validated WU ID
11
+ */
12
+ export function validateInputs(argv) {
13
+ const args = parseWUArgs(argv);
14
+ if (args.help || !args.id) {
15
+ console.log('Usage: pnpm wu:done --id WU-334 [OPTIONS]\n\n' +
16
+ 'Options:\n' +
17
+ ' --worktree <path> Override worktree path (default: worktrees/<lane>-<wu-id>)\n' +
18
+ ' --no-auto Skip auto-updating YAML/backlog/status (you staged manually)\n' +
19
+ ' --no-remove Skip worktree removal\n' +
20
+ ' --no-merge Skip auto-merging lane branch to main\n' +
21
+ ' --delete-branch Delete lane branch after merge (both local and remote)\n' +
22
+ ' --create-pr Create PR instead of auto-merge (requires gh CLI)\n' +
23
+ ' --pr-draft Create PR as draft (use with --create-pr)\n' +
24
+ ' --skip-gates Skip gates check (USE WITH EXTREME CAUTION)\n' +
25
+ ' --docs-only Run docs-only gates (requires exposure: documentation)\n' +
26
+ ' --reason "<text>" Required with --skip-gates or --override-owner\n' +
27
+ ' --fix-wu WU-{id} Required with --skip-gates: WU ID that will fix the failures\n' +
28
+ ' --allow-todo Allow TODO comments in code (requires justification in WU notes)\n' +
29
+ ' --override-owner Override ownership check (requires --reason, audited)\n' +
30
+ ' --no-auto-rebase Disable auto-rebase on branch divergence (WU-1303)\n' +
31
+ ' --require-agents Block completion if mandatory agents not invoked (WU-1542)\n' +
32
+ ' --help, -h Show this help\n\n' +
33
+ '⚠️ SKIP-GATES WARNING:\n' +
34
+ ' Only use --skip-gates when:\n' +
35
+ ' • Test failures are confirmed pre-existing (not introduced by your WU)\n' +
36
+ ' • A separate WU exists to fix those failures (specify with --fix-wu)\n' +
37
+ ' • Your WU work is genuinely complete\n\n' +
38
+ ' NEVER use --skip-gates for failures introduced by your WU!\n' +
39
+ ' All skip-gates events are logged to .beacon/skip-gates-audit.log\n\n' +
40
+ '📝 WU VALIDATOR:\n' +
41
+ ' Automatically scans code_paths for:\n' +
42
+ ' • TODO/FIXME/HACK/XXX comments (fails validation unless --allow-todo)\n' +
43
+ ' • Mock/Stub/Fake classes in production code (warning only)\n' +
44
+ ' Use --allow-todo only for legitimate cases with justification in WU notes.\n');
45
+ process.exit(args.help ? EXIT_CODES.SUCCESS : EXIT_CODES.ERROR);
46
+ }
47
+ const id = args.id.toUpperCase();
48
+ if (!PATTERNS.WU_ID.test(id))
49
+ die(`Invalid WU id '${args.id}'. Expected format WU-123`);
50
+ return { args, id };
51
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Metadata update helpers for wu:done.
3
+ */
4
+ /**
5
+ * Generate commit message for WU completion
6
+ * Extracted from wu-done.mjs (WU-1215 Phase 2 Extraction #1 Helper)
7
+ * @param {string} id - WU ID (e.g., "WU-1215")
8
+ * @param {string} title - WU title
9
+ * @param {number} maxLength - Maximum commit header length from commitlint config
10
+ * @returns {string} Formatted commit message
11
+ * @throws {Error} If generated message exceeds maxLength
12
+ */
13
+ export declare function generateCommitMessage(id: any, title: any, maxLength?: number): string;
14
+ /**
15
+ * Validate that required metadata files exist before updating
16
+ * WU-1275: Fail fast before mutations to prevent partial state
17
+ *
18
+ * @param {object} params - Parameters object
19
+ * @param {string} params.statusPath - Path to status.md file
20
+ * @param {string} params.backlogPath - Path to backlog.md file
21
+ * @throws {WUError} If any required file is missing
22
+ */
23
+ export declare function validateMetadataFilesExist({ statusPath, backlogPath }: {
24
+ statusPath: any;
25
+ backlogPath: any;
26
+ }): void;
27
+ /**
28
+ * Update all metadata files for WU completion
29
+ * Extracted from wu-done.mjs (WU-1215 Phase 2 Extraction #1 Helper)
30
+ * WU-1572: Made async for WUStateStore integration
31
+ * @param {object} params - Parameters object
32
+ * @param {string} params.id - WU ID
33
+ * @param {string} params.title - WU title
34
+ * @param {object} params.doc - WU YAML document to update
35
+ * @param {string} params.wuPath - Path to WU YAML file
36
+ * @param {string} params.statusPath - Path to status.md file
37
+ * @param {string} params.backlogPath - Path to backlog.md file
38
+ */
39
+ export declare function updateMetadataFiles({ id, title, doc, wuPath, statusPath, backlogPath }: {
40
+ id: any;
41
+ title: any;
42
+ doc: any;
43
+ wuPath: any;
44
+ statusPath: any;
45
+ backlogPath: any;
46
+ }): Promise<void>;
47
+ /**
48
+ * Collect metadata updates to a transaction (WU-1369: Atomic pattern)
49
+ *
50
+ * This is the atomic version of updateMetadataFiles.
51
+ * Instead of writing files immediately, it collects all changes
52
+ * into a WUTransaction object for atomic commit.
53
+ *
54
+ * Usage:
55
+ * ```js
56
+ * const tx = new WUTransaction(id);
57
+ * collectMetadataToTransaction({ id, title, doc, wuPath, statusPath, backlogPath, stampPath, transaction: tx });
58
+ * // All changes are now in tx.pendingWrites
59
+ * // Validate, then commit or abort
60
+ * tx.commit();
61
+ * ```
62
+ *
63
+ * @param {object} params - Parameters object
64
+ * @param {string} params.id - WU ID
65
+ * @param {string} params.title - WU title
66
+ * @param {object} params.doc - WU YAML document to update (will be mutated)
67
+ * @param {string} params.wuPath - Path to WU YAML file
68
+ * @param {string} params.statusPath - Path to status.md file
69
+ * @param {string} params.backlogPath - Path to backlog.md file
70
+ * @param {string} params.stampPath - Path to stamp file
71
+ * @param {WUTransaction} params.transaction - Transaction to add writes to
72
+ */
73
+ export declare function collectMetadataToTransaction({ id, title, doc, wuPath, statusPath, backlogPath, stampPath, transaction, }: {
74
+ id: any;
75
+ title: any;
76
+ doc: any;
77
+ wuPath: any;
78
+ statusPath: any;
79
+ backlogPath: any;
80
+ stampPath: any;
81
+ transaction: any;
82
+ }): Promise<void>;
83
+ /**
84
+ * Stage and format metadata files
85
+ * Extracted from wu-done.mjs (WU-1215 Phase 2 Extraction #1 Helper)
86
+ * @param {object} params - Parameters object
87
+ * @param {string} params.id - WU ID (for error reporting)
88
+ * @param {string} params.wuPath - Path to WU YAML file
89
+ * @param {string} params.statusPath - Path to status.md file
90
+ * @param {string} params.backlogPath - Path to backlog.md file
91
+ * @param {string} params.stampsDir - Path to stamps directory
92
+ * @throws {Error} If formatting fails
93
+ */
94
+ export declare function stageAndFormatMetadata({ id, wuPath, statusPath, backlogPath, stampsDir }: {
95
+ id: any;
96
+ wuPath: any;
97
+ statusPath: any;
98
+ backlogPath: any;
99
+ stampsDir: any;
100
+ }): Promise<void>;
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Metadata update helpers for wu:done.
3
+ */
4
+ import path from 'node:path';
5
+ import { existsSync } from 'node:fs';
6
+ import { exec as execCallback } from 'node:child_process';
7
+ import { promisify } from 'node:util';
8
+ import { getGitForCwd } from './git-adapter.js';
9
+ import { updateStatusRemoveInProgress, addToStatusCompleted } from './wu-status-updater.js';
10
+ import { moveWUToDoneBacklog } from './wu-backlog-updater.js';
11
+ import { createStamp } from './stamp-utils.js';
12
+ import { WU_EVENTS_FILE_NAME } from './wu-state-store.js';
13
+ import { computeWUYAMLContent, computeStatusContent, computeBacklogContent, computeWUEventsContentAfterComplete, computeStampContent, } from './wu-transaction-collectors.js';
14
+ import { DEFAULTS, LOG_PREFIX, EMOJI, PKG_MANAGER, SCRIPTS, PRETTIER_FLAGS, BEACON_PATHS, } from './wu-constants.js';
15
+ import { applyExposureDefaults } from './wu-done-validation.js';
16
+ import { createFileNotFoundError, createValidationError } from './wu-done-errors.js';
17
+ import { writeWU } from './wu-yaml.js';
18
+ const execAsync = promisify(execCallback);
19
+ /**
20
+ * Generate commit message for WU completion
21
+ * Extracted from wu-done.mjs (WU-1215 Phase 2 Extraction #1 Helper)
22
+ * @param {string} id - WU ID (e.g., "WU-1215")
23
+ * @param {string} title - WU title
24
+ * @param {number} maxLength - Maximum commit header length from commitlint config
25
+ * @returns {string} Formatted commit message
26
+ * @throws {Error} If generated message exceeds maxLength
27
+ */
28
+ export function generateCommitMessage(id, title, maxLength = DEFAULTS.MAX_COMMIT_SUBJECT) {
29
+ const prefix = `wu(${id.toLowerCase()}): done - `;
30
+ const safe = String(title).trim().toLowerCase().replace(/\s+/g, ' ');
31
+ const room = Math.max(0, maxLength - prefix.length);
32
+ const short = safe.length > room ? `${safe.slice(0, room - 1)}…` : safe;
33
+ const msg = `${prefix}${short}`;
34
+ if (msg.length > maxLength) {
35
+ const error = new Error(`Commit message too long (${msg.length}/${maxLength}).\n` +
36
+ `Fix: Shorten WU title\n` +
37
+ `Current title: "${title}" (${title.length} chars)\n` +
38
+ `Suggested max: ~${maxLength - prefix.length} chars`);
39
+ error.code = 'COMMIT_MESSAGE_TOO_LONG';
40
+ error.data = {
41
+ title,
42
+ titleLength: title.length,
43
+ messageLength: msg.length,
44
+ maxLength,
45
+ suggestedMax: maxLength - prefix.length,
46
+ };
47
+ throw error;
48
+ }
49
+ return msg;
50
+ }
51
+ /**
52
+ * Validate that required metadata files exist before updating
53
+ * WU-1275: Fail fast before mutations to prevent partial state
54
+ *
55
+ * @param {object} params - Parameters object
56
+ * @param {string} params.statusPath - Path to status.md file
57
+ * @param {string} params.backlogPath - Path to backlog.md file
58
+ * @throws {WUError} If any required file is missing
59
+ */
60
+ export function validateMetadataFilesExist({ statusPath, backlogPath }) {
61
+ const missing = [];
62
+ if (!existsSync(statusPath)) {
63
+ missing.push(`Status: ${statusPath}`);
64
+ }
65
+ if (!existsSync(backlogPath)) {
66
+ missing.push(`Backlog: ${backlogPath}`);
67
+ }
68
+ if (missing.length > 0) {
69
+ throw createFileNotFoundError(`Required metadata files missing:\n ${missing.join('\n ')}\n\nCannot complete WU - verify worktree has latest metadata files.`, { missingFiles: missing });
70
+ }
71
+ }
72
+ /**
73
+ * Update all metadata files for WU completion
74
+ * Extracted from wu-done.mjs (WU-1215 Phase 2 Extraction #1 Helper)
75
+ * WU-1572: Made async for WUStateStore integration
76
+ * @param {object} params - Parameters object
77
+ * @param {string} params.id - WU ID
78
+ * @param {string} params.title - WU title
79
+ * @param {object} params.doc - WU YAML document to update
80
+ * @param {string} params.wuPath - Path to WU YAML file
81
+ * @param {string} params.statusPath - Path to status.md file
82
+ * @param {string} params.backlogPath - Path to backlog.md file
83
+ */
84
+ export async function updateMetadataFiles({ id, title, doc, wuPath, statusPath, backlogPath }) {
85
+ // WU-1275: Fail fast before any mutations
86
+ validateMetadataFilesExist({ statusPath, backlogPath });
87
+ const exposureUpdate = applyExposureDefaults(doc);
88
+ if (exposureUpdate.applied) {
89
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Auto-set exposure to ${exposureUpdate.exposure} for ${id}`);
90
+ }
91
+ // Update WU YAML (mark as done, lock, set completion timestamp)
92
+ doc.status = 'done';
93
+ doc.locked = true;
94
+ doc.completed_at = new Date().toISOString();
95
+ writeWU(wuPath, doc);
96
+ // Update status.md (remove from In Progress, add to Completed)
97
+ updateStatusRemoveInProgress(statusPath, id);
98
+ addToStatusCompleted(statusPath, id, title);
99
+ // Update backlog.md (move to Done section)
100
+ // WU-1572: Now async for state store integration
101
+ await moveWUToDoneBacklog(backlogPath, id, title);
102
+ // Create completion stamp
103
+ createStamp({ id, title });
104
+ }
105
+ /**
106
+ * Collect metadata updates to a transaction (WU-1369: Atomic pattern)
107
+ *
108
+ * This is the atomic version of updateMetadataFiles.
109
+ * Instead of writing files immediately, it collects all changes
110
+ * into a WUTransaction object for atomic commit.
111
+ *
112
+ * Usage:
113
+ * ```js
114
+ * const tx = new WUTransaction(id);
115
+ * collectMetadataToTransaction({ id, title, doc, wuPath, statusPath, backlogPath, stampPath, transaction: tx });
116
+ * // All changes are now in tx.pendingWrites
117
+ * // Validate, then commit or abort
118
+ * tx.commit();
119
+ * ```
120
+ *
121
+ * @param {object} params - Parameters object
122
+ * @param {string} params.id - WU ID
123
+ * @param {string} params.title - WU title
124
+ * @param {object} params.doc - WU YAML document to update (will be mutated)
125
+ * @param {string} params.wuPath - Path to WU YAML file
126
+ * @param {string} params.statusPath - Path to status.md file
127
+ * @param {string} params.backlogPath - Path to backlog.md file
128
+ * @param {string} params.stampPath - Path to stamp file
129
+ * @param {WUTransaction} params.transaction - Transaction to add writes to
130
+ */
131
+ // WU-1574: Made async for computeBacklogContent
132
+ export async function collectMetadataToTransaction({ id, title, doc, wuPath, statusPath, backlogPath, stampPath, transaction, }) {
133
+ // WU-1369: Fail fast before any computations
134
+ validateMetadataFilesExist({ statusPath, backlogPath });
135
+ const exposureUpdate = applyExposureDefaults(doc);
136
+ if (exposureUpdate.applied) {
137
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.INFO} Auto-set exposure to ${exposureUpdate.exposure} for ${id}`);
138
+ }
139
+ // Compute WU YAML content (mutates doc, returns YAML string)
140
+ const wuYAMLContent = computeWUYAMLContent(doc);
141
+ transaction.addWrite(wuPath, wuYAMLContent, 'WU YAML');
142
+ // Compute status.md content
143
+ const statusContent = computeStatusContent(statusPath, id, title);
144
+ transaction.addWrite(statusPath, statusContent, 'status.md');
145
+ // Compute backlog.md content (WU-1574: now async)
146
+ const backlogContent = await computeBacklogContent(backlogPath, id, title);
147
+ transaction.addWrite(backlogPath, backlogContent, 'backlog.md');
148
+ const wuEventsUpdate = await computeWUEventsContentAfterComplete(backlogPath, id);
149
+ if (wuEventsUpdate) {
150
+ transaction.addWrite(wuEventsUpdate.eventsPath, wuEventsUpdate.content, 'wu-events.jsonl');
151
+ }
152
+ // Compute stamp content
153
+ const stampContent = computeStampContent(id, title);
154
+ transaction.addWrite(stampPath, stampContent, 'completion stamp');
155
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Collected ${transaction.size} metadata updates for atomic commit`);
156
+ }
157
+ /**
158
+ * Stage and format metadata files
159
+ * Extracted from wu-done.mjs (WU-1215 Phase 2 Extraction #1 Helper)
160
+ * @param {object} params - Parameters object
161
+ * @param {string} params.id - WU ID (for error reporting)
162
+ * @param {string} params.wuPath - Path to WU YAML file
163
+ * @param {string} params.statusPath - Path to status.md file
164
+ * @param {string} params.backlogPath - Path to backlog.md file
165
+ * @param {string} params.stampsDir - Path to stamps directory
166
+ * @throws {Error} If formatting fails
167
+ */
168
+ export async function stageAndFormatMetadata({ id, wuPath, statusPath, backlogPath, stampsDir }) {
169
+ // WU-1235: Use getGitForCwd() to capture current directory (worktree after chdir)
170
+ // The singleton git adapter captures cwd at import time, which is wrong after process.chdir()
171
+ const gitCwd = getGitForCwd();
172
+ // Stage files
173
+ const wuEventsPath = path.join(BEACON_PATHS.STATE_DIR, WU_EVENTS_FILE_NAME);
174
+ const filesToStage = [wuPath, statusPath, backlogPath, stampsDir];
175
+ if (existsSync(wuEventsPath)) {
176
+ filesToStage.push(wuEventsPath);
177
+ }
178
+ await gitCwd.add(filesToStage);
179
+ // Format documentation
180
+ console.log(`${LOG_PREFIX.DONE} Formatting auto-generated documentation...`);
181
+ try {
182
+ const prettierCmd = `${PKG_MANAGER} ${SCRIPTS.PRETTIER} ${PRETTIER_FLAGS.WRITE} "${wuPath}" "${statusPath}" "${backlogPath}"`;
183
+ await execAsync(prettierCmd);
184
+ await gitCwd.add([wuPath, statusPath, backlogPath]);
185
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Documentation formatted`);
186
+ }
187
+ catch (err) {
188
+ throw createValidationError(`Failed to format documentation: ${err.message}`, {
189
+ wuId: id,
190
+ error: err.message,
191
+ });
192
+ }
193
+ }
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Path and workspace detection helpers for wu:done.
3
+ */
4
+ /**
5
+ * Read WU YAML preferring worktree version over main version
6
+ *
7
+ * WU-1584 Fix #4: Added diagnostic logging to confirm which YAML file is being
8
+ * read for code_paths validation. This helps debug issues where worktree YAML
9
+ * differs from main checkout YAML.
10
+ *
11
+ * @param {string} id - WU ID
12
+ * @param {string|null} worktreePath - Worktree path (null if branch-only mode)
13
+ * @param {string} mainWUPath - Path to WU YAML in main checkout
14
+ * @returns {object} Parsed WU document
15
+ */
16
+ export declare function readWUPreferWorktree(id: any, worktreePath: any, mainWUPath: any): any;
17
+ /**
18
+ * Detect if currently running inside a worktree
19
+ * Checks for .git file (not directory) which indicates a worktree
20
+ * @returns {string|null} Current directory path if inside worktree, null otherwise
21
+ */
22
+ export declare function detectCurrentWorktree(): string;
23
+ /**
24
+ * Resolve worktree path from WU YAML
25
+ * Originally implemented in WU-1226, extracted to validators module in WU-1215
26
+ * Priority:
27
+ * 1. Read worktree_path field (set at claim time, immune to lane field changes)
28
+ * 2. Fall back to calculating from lane field (for old WUs without worktree_path)
29
+ * 3. Use git worktree list to find actual path (defensive fallback)
30
+ * @param {object} doc - WU YAML document
31
+ * @returns {Promise<string|null>} - Worktree path or null if not found
32
+ */
33
+ export declare function defaultWorktreeFrom(doc: any): Promise<any>;
34
+ /**
35
+ * Detect workspace mode from WU YAML
36
+ * @param {object} doc - WU YAML document
37
+ * @returns {'worktree' | 'branch-only'}
38
+ */
39
+ export declare function detectWorkspaceMode(doc: any): string;
40
+ /**
41
+ * Calculate lane branch name from WU YAML
42
+ * @param {object} doc - WU YAML document
43
+ * @returns {string|null} Lane branch name (e.g., lane/operations-tooling/wu-1215)
44
+ */
45
+ export declare function defaultBranchFrom(doc: any): string;
46
+ /**
47
+ * Check if a branch exists
48
+ * @param {string} branch - Branch name to check
49
+ * @returns {Promise<boolean>} True if branch exists
50
+ */
51
+ export declare function branchExists(branch: any): Promise<boolean>;
52
+ /**
53
+ * Detect workspace mode and calculate all relevant paths
54
+ * @param {string} id - WU ID
55
+ * @param {object} args - Parsed command-line arguments
56
+ * @returns {Promise<object>} Object containing paths, mode info, and WU document
57
+ */
58
+ export declare function detectModeAndPaths(id: any, args: any): Promise<{
59
+ WU_PATH: string;
60
+ STATUS_PATH: string;
61
+ BACKLOG_PATH: string;
62
+ STAMPS_DIR: string;
63
+ docMain: any;
64
+ workspaceMode: string;
65
+ isBranchOnly: boolean;
66
+ derivedWorktree: any;
67
+ docForValidation: any;
68
+ isDocsOnly: boolean;
69
+ }>;