@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
@@ -42,6 +42,10 @@ export const GIT_REFS = {
42
42
  ORIGIN_MAIN: 'origin/main',
43
43
  /** Current HEAD ref */
44
44
  HEAD: 'HEAD',
45
+ /** Upstream ref */
46
+ UPSTREAM: '@{u}',
47
+ /** Range of upstream..HEAD */
48
+ UPSTREAM_RANGE: '@{u}..HEAD',
45
49
  /** Fetch head ref */
46
50
  FETCH_HEAD: 'FETCH_HEAD',
47
51
  };
@@ -273,6 +277,22 @@ export const CONSISTENCY_TYPES = {
273
277
  ORPHAN_WORKTREE_DONE: 'ORPHAN_WORKTREE_DONE',
274
278
  /** Stamp file exists but WU YAML status is not 'done' (partial wu:done failure) */
275
279
  STAMP_EXISTS_YAML_NOT_DONE: 'STAMP_EXISTS_YAML_NOT_DONE',
280
+ /** WU is claimed but its worktree directory is missing */
281
+ MISSING_WORKTREE_CLAIMED: 'MISSING_WORKTREE_CLAIMED',
282
+ };
283
+ /**
284
+ * Consistency check messages
285
+ */
286
+ export const CONSISTENCY_MESSAGES = {
287
+ MISSING_WORKTREE_CLAIMED: (id, status, worktreePath) => `WU ${id} is '${status}' but worktree path is missing (${worktreePath})`,
288
+ MISSING_WORKTREE_CLAIMED_REPAIR: 'Recover worktree or re-claim WU',
289
+ };
290
+ /**
291
+ * Worktree warning messages
292
+ */
293
+ export const WORKTREE_WARNINGS = {
294
+ MISSING_TRACKED_HEADER: 'Tracked worktrees missing on disk (possible manual deletion):',
295
+ MISSING_TRACKED_LINE: (worktreePath) => `Missing: ${worktreePath}`,
276
296
  };
277
297
  /**
278
298
  * File system constants
@@ -285,6 +305,30 @@ export const FILE_SYSTEM = {
285
305
  /** UTF-8 encoding (alias for compatibility) */
286
306
  UTF8: 'utf8',
287
307
  };
308
+ /**
309
+ * Build artifact cleanup globs
310
+ *
311
+ * Centralized glob patterns for worktree artifact cleanup.
312
+ */
313
+ export const BUILD_ARTIFACT_GLOBS = {
314
+ /** Common dist directories inside worktrees */
315
+ DIST_DIRS: [
316
+ 'packages/*/dist',
317
+ 'packages/**/dist',
318
+ 'apps/*/dist',
319
+ 'apps/**/dist',
320
+ 'tools/*/dist',
321
+ 'tools/**/dist',
322
+ ],
323
+ /** TypeScript build info files */
324
+ TSBUILDINFO_FILES: ['**/*.tsbuildinfo'],
325
+ };
326
+ /**
327
+ * Build artifact cleanup ignore patterns
328
+ *
329
+ * Centralized ignore globs for artifact cleanup.
330
+ */
331
+ export const BUILD_ARTIFACT_IGNORES = ['**/node_modules/**', '**/.git/**', '**/.turbo/**'];
288
332
  /**
289
333
  * Process stdio constants
290
334
  *
@@ -367,12 +411,12 @@ export const DEFAULTS = {
367
411
  * YAML serialization options
368
412
  *
369
413
  * Centralized from duplicated { lineWidth: 100 } across wu-* scripts (WU-1256).
370
- * Use with js-yaml dump() function.
414
+ * Use with yaml stringify() options.
371
415
  */
372
416
  export const YAML_OPTIONS = {
373
417
  /** Standard line width for YAML dump (100 chars) */
374
418
  LINE_WIDTH: 100,
375
- /** No line wrapping (-1 disables wrapping in js-yaml) */
419
+ /** No line wrapping (-1 disables wrapping) */
376
420
  NO_WRAP: -1,
377
421
  };
378
422
  /**
@@ -402,6 +446,62 @@ export const BOX = {
402
446
  /** Side border for content lines */
403
447
  SIDE: '║',
404
448
  };
449
+ /**
450
+ * Cleanup guard constants
451
+ */
452
+ export const CLEANUP_GUARD = {
453
+ REASONS: {
454
+ UNCOMMITTED_CHANGES: 'UNCOMMITTED_CHANGES',
455
+ UNPUSHED_COMMITS: 'UNPUSHED_COMMITS',
456
+ STATUS_NOT_DONE: 'STATUS_NOT_DONE',
457
+ MISSING_STAMP: 'MISSING_STAMP',
458
+ PR_NOT_MERGED: 'PR_NOT_MERGED',
459
+ },
460
+ TITLES: {
461
+ BLOCKED: 'CLEANUP BLOCKED',
462
+ NEXT_STEPS: 'Next steps:',
463
+ },
464
+ MESSAGES: {
465
+ UNCOMMITTED_CHANGES: 'Worktree has uncommitted changes. Refusing to delete.',
466
+ UNPUSHED_COMMITS: 'Worktree has unpushed commits. Refusing to delete.',
467
+ STATUS_NOT_DONE: 'WU YAML status is not done. Refusing to delete.',
468
+ MISSING_STAMP: 'WU stamp is missing. Refusing to delete.',
469
+ PR_NOT_MERGED: 'PR is not merged (or cannot be verified). Refusing to delete.',
470
+ },
471
+ NEXT_STEPS: {
472
+ DEFAULT: [
473
+ { text: '1. Resolve the issue above', appendId: false },
474
+ { text: '2. Re-run: pnpm wu:cleanup --id', appendId: true },
475
+ ],
476
+ UNCOMMITTED_CHANGES: [
477
+ { text: '1. Commit or stash changes in the worktree', appendId: false },
478
+ { text: '2. Re-run: pnpm wu:cleanup --id', appendId: true },
479
+ ],
480
+ UNPUSHED_COMMITS: [
481
+ { text: '1. Push the lane branch to origin', appendId: false },
482
+ { text: '2. Re-run: pnpm wu:cleanup --id', appendId: true },
483
+ ],
484
+ STATUS_NOT_DONE: [
485
+ {
486
+ text: `1. Complete the WU with ${LOG_PREFIX.DONE} (creates stamp + done status)`,
487
+ appendId: false,
488
+ },
489
+ { text: '2. Re-run: pnpm wu:cleanup --id', appendId: true },
490
+ ],
491
+ MISSING_STAMP: [
492
+ { text: '1. Run wu:done to create the stamp file', appendId: false },
493
+ { text: '2. Re-run: pnpm wu:cleanup --id', appendId: true },
494
+ ],
495
+ PR_NOT_MERGED: [
496
+ { text: '1. Merge the PR in GitHub', appendId: false },
497
+ { text: '2. Re-run: pnpm wu:cleanup --id', appendId: true },
498
+ ],
499
+ },
500
+ PR_CHECK: {
501
+ START: 'Verifying PR merge status...',
502
+ RESULT: 'PR merge verification via',
503
+ },
504
+ };
405
505
  /**
406
506
  * Git display constants
407
507
  *
@@ -469,6 +569,8 @@ export const GIT_FLAGS = {
469
569
  NO_VERIFY: '--no-verify',
470
570
  /** No GPG sign flag (skip commit signing) */
471
571
  NO_GPG_SIGN: '--no-gpg-sign',
572
+ /** One-line log format */
573
+ ONELINE: '--oneline',
472
574
  };
473
575
  /**
474
576
  * Git commands
@@ -485,6 +587,8 @@ export const GIT_COMMANDS = {
485
587
  LS_TREE: 'ls-tree',
486
588
  /** Git diff command */
487
589
  DIFF: 'diff',
590
+ /** Git log command */
591
+ LOG: 'log',
488
592
  /** Git merge-base command */
489
593
  MERGE_BASE: 'merge-base',
490
594
  /** Git rev-parse command */
@@ -951,7 +1055,6 @@ export const ESLINT_DEFAULTS = {
951
1055
  *
952
1056
  * WU-1866: Temporarily increased from 0 to 100 to unblock gates.
953
1057
  * There are ~82 pre-existing warnings that need proper fixes.
954
- * TODO: Create follow-up WU to fix warnings and restore zero-warnings policy.
955
1058
  */
956
1059
  MAX_WARNINGS: '100',
957
1060
  };
@@ -1030,6 +1133,8 @@ export const GITLEAKS_ARGS = {
1030
1133
  export const PRETTIER_ARGS = {
1031
1134
  /** Check formatting without writing */
1032
1135
  CHECK: '--check',
1136
+ /** List files with formatting differences */
1137
+ LIST_DIFFERENT: '--list-different',
1033
1138
  };
1034
1139
  /**
1035
1140
  * Audit command arguments
@@ -1,11 +1,16 @@
1
1
  /**
2
- * WU Create Validators (WU-2107)
2
+ * WU Create Validators (WU-2107, WU-1062)
3
3
  *
4
- * Validation helpers for wu:create, including lane inference surfacing.
4
+ * Validation helpers for wu:create, including:
5
+ * - Lane inference surfacing (WU-2107)
6
+ * - External spec_refs validation (WU-1062)
5
7
  *
6
8
  * When agents create WUs, this module helps surface lane inference suggestions
7
9
  * to guide better lane selection and improve parallelization.
8
10
  *
11
+ * WU-1062: Validates spec_refs paths, accepting both repo-relative paths
12
+ * and external paths (lumenflow://, ~/.lumenflow/, $LUMENFLOW_HOME/).
13
+ *
9
14
  * NOTE: This is domain-specific WU workflow code, not a general utility.
10
15
  * No external library exists for LumenFlow lane inference validation.
11
16
  */
@@ -40,3 +45,36 @@ export declare function validateLaneWithInference(providedLane: any, codePaths:
40
45
  inferredLane: any;
41
46
  confidence: any;
42
47
  };
48
+ /**
49
+ * WU-1062: Validate spec_refs paths
50
+ *
51
+ * Accepts:
52
+ * - Repo-relative paths: docs/04-operations/plans/WU-XXX-plan.md
53
+ * - External paths: lumenflow://plans/WU-XXX-plan.md
54
+ * - Tilde paths: ~/.lumenflow/plans/WU-XXX-plan.md
55
+ * - Env var paths: $LUMENFLOW_HOME/plans/WU-XXX-plan.md
56
+ *
57
+ * @param {string[]} specRefs - Array of spec reference paths
58
+ * @returns {{ valid: boolean, errors: string[], warnings: string[] }} Validation result
59
+ */
60
+ export declare function validateSpecRefs(specRefs: string[]): {
61
+ valid: boolean;
62
+ errors: string[];
63
+ warnings: string[];
64
+ };
65
+ /**
66
+ * WU-1062: Check if spec_refs contains external paths
67
+ *
68
+ * @param {string[]} specRefs - Array of spec reference paths
69
+ * @returns {boolean} True if any spec_ref is an external path
70
+ */
71
+ export declare function hasExternalSpecRefs(specRefs: string[]): boolean;
72
+ /**
73
+ * WU-1062: Normalize all spec_refs paths
74
+ *
75
+ * Expands external paths to absolute paths while keeping repo-relative paths unchanged.
76
+ *
77
+ * @param {string[]} specRefs - Array of spec reference paths
78
+ * @returns {string[]} Normalized paths
79
+ */
80
+ export declare function normalizeSpecRefs(specRefs: string[]): string[];
@@ -1,14 +1,20 @@
1
1
  /**
2
- * WU Create Validators (WU-2107)
2
+ * WU Create Validators (WU-2107, WU-1062)
3
3
  *
4
- * Validation helpers for wu:create, including lane inference surfacing.
4
+ * Validation helpers for wu:create, including:
5
+ * - Lane inference surfacing (WU-2107)
6
+ * - External spec_refs validation (WU-1062)
5
7
  *
6
8
  * When agents create WUs, this module helps surface lane inference suggestions
7
9
  * to guide better lane selection and improve parallelization.
8
10
  *
11
+ * WU-1062: Validates spec_refs paths, accepting both repo-relative paths
12
+ * and external paths (lumenflow://, ~/.lumenflow/, $LUMENFLOW_HOME/).
13
+ *
9
14
  * NOTE: This is domain-specific WU workflow code, not a general utility.
10
15
  * No external library exists for LumenFlow lane inference validation.
11
16
  */
17
+ import { isExternalPath, normalizeSpecRef } from './lumenflow-home.js';
12
18
  /** Confidence threshold for showing suggestion (percentage) */
13
19
  const CONFIDENCE_THRESHOLD_LOW = 30;
14
20
  /**
@@ -91,3 +97,71 @@ export function validateLaneWithInference(providedLane, codePaths, description,
91
97
  return { shouldWarn: false, warning: '' };
92
98
  }
93
99
  }
100
+ /**
101
+ * WU-1062: Validate spec_refs paths
102
+ *
103
+ * Accepts:
104
+ * - Repo-relative paths: docs/04-operations/plans/WU-XXX-plan.md
105
+ * - External paths: lumenflow://plans/WU-XXX-plan.md
106
+ * - Tilde paths: ~/.lumenflow/plans/WU-XXX-plan.md
107
+ * - Env var paths: $LUMENFLOW_HOME/plans/WU-XXX-plan.md
108
+ *
109
+ * @param {string[]} specRefs - Array of spec reference paths
110
+ * @returns {{ valid: boolean, errors: string[], warnings: string[] }} Validation result
111
+ */
112
+ export function validateSpecRefs(specRefs) {
113
+ const errors = [];
114
+ const warnings = [];
115
+ if (!specRefs || specRefs.length === 0) {
116
+ return { valid: true, errors, warnings };
117
+ }
118
+ for (const ref of specRefs) {
119
+ // Check for empty refs
120
+ if (!ref || ref.trim().length === 0) {
121
+ errors.push('Empty spec_ref detected');
122
+ continue;
123
+ }
124
+ // External paths are valid (will be resolved at runtime)
125
+ if (isExternalPath(ref)) {
126
+ // Add informational warning about external paths
127
+ warnings.push(`External spec_ref: "${ref}" - ensure plan exists at ${normalizeSpecRef(ref)}`);
128
+ continue;
129
+ }
130
+ // Repo-relative paths should follow conventions
131
+ const isValidRepoPath = ref.startsWith('docs/') || ref.startsWith('./docs/') || ref.endsWith('.md');
132
+ if (!isValidRepoPath) {
133
+ warnings.push(`Unconventional spec_ref path: "${ref}" - consider using docs/04-operations/plans/ or lumenflow://plans/`);
134
+ }
135
+ }
136
+ return {
137
+ valid: errors.length === 0,
138
+ errors,
139
+ warnings,
140
+ };
141
+ }
142
+ /**
143
+ * WU-1062: Check if spec_refs contains external paths
144
+ *
145
+ * @param {string[]} specRefs - Array of spec reference paths
146
+ * @returns {boolean} True if any spec_ref is an external path
147
+ */
148
+ export function hasExternalSpecRefs(specRefs) {
149
+ if (!specRefs || specRefs.length === 0) {
150
+ return false;
151
+ }
152
+ return specRefs.some((ref) => isExternalPath(ref));
153
+ }
154
+ /**
155
+ * WU-1062: Normalize all spec_refs paths
156
+ *
157
+ * Expands external paths to absolute paths while keeping repo-relative paths unchanged.
158
+ *
159
+ * @param {string[]} specRefs - Array of spec reference paths
160
+ * @returns {string[]} Normalized paths
161
+ */
162
+ export function normalizeSpecRefs(specRefs) {
163
+ if (!specRefs || specRefs.length === 0) {
164
+ return [];
165
+ }
166
+ return specRefs.map((ref) => normalizeSpecRef(ref));
167
+ }
@@ -23,6 +23,8 @@ import { die, createError, ErrorCodes } from './error-handler.js';
23
23
  import { validateWU, validateDoneWU } from './wu-schema.js';
24
24
  import { assertTransition } from './state-machine.js';
25
25
  import { detectZombieState, recoverZombieState } from './wu-recovery.js';
26
+ // WU-1061: Import docs regeneration utilities
27
+ import { maybeRegenerateAndStageDocs } from './wu-done-docs-generate.js';
26
28
  /**
27
29
  * @typedef {Object} BranchOnlyContext
28
30
  * @property {string} id - WU ID (e.g., "WU-1215")
@@ -119,6 +121,13 @@ export async function executeBranchOnlyCompletion(context) {
119
121
  statusPath: metadataStatusPath,
120
122
  backlogPath: metadataBacklogPath,
121
123
  });
124
+ // WU-1061: Regenerate docs if doc-source files changed
125
+ // This runs BEFORE stageAndFormatMetadata to include doc outputs
126
+ // in the single atomic commit
127
+ await maybeRegenerateAndStageDocs({
128
+ baseBranch: BRANCHES.MAIN,
129
+ repoRoot: metadataBasePath,
130
+ });
122
131
  // Step 7: Stage and format files
123
132
  await stageAndFormatMetadata({
124
133
  id,
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Branch utilities for wu:done workflows.
3
+ */
4
+ /**
5
+ * Check if branch is already merged to main
6
+ *
7
+ * @param {string} branch - Lane branch name
8
+ * @returns {Promise<boolean>} Whether branch is already merged
9
+ */
10
+ export declare function isBranchAlreadyMerged(branch: any): Promise<boolean>;
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Branch utilities for wu:done workflows.
3
+ */
4
+ import { getGitForCwd } from './git-adapter.js';
5
+ import { BRANCHES, LOG_PREFIX } from './wu-constants.js';
6
+ import { PREFLIGHT } from './wu-done-messages.js';
7
+ /** @constant {number} SHA_SHORT_LENGTH - Length of shortened git SHA hashes for display */
8
+ const SHA_SHORT_LENGTH = 8;
9
+ /**
10
+ * Check if branch is already merged to main
11
+ *
12
+ * @param {string} branch - Lane branch name
13
+ * @returns {Promise<boolean>} Whether branch is already merged
14
+ */
15
+ export async function isBranchAlreadyMerged(branch) {
16
+ const gitAdapter = getGitForCwd();
17
+ try {
18
+ const branchTip = (await gitAdapter.getCommitHash(branch)).trim();
19
+ const mergeBase = (await gitAdapter.mergeBase(BRANCHES.MAIN, branch)).trim();
20
+ const mainHead = (await gitAdapter.getCommitHash(BRANCHES.MAIN)).trim();
21
+ if (branchTip === mergeBase) {
22
+ console.log(PREFLIGHT.BRANCH_INFO(branch, branchTip.substring(0, SHA_SHORT_LENGTH), mergeBase.substring(0, SHA_SHORT_LENGTH), mainHead.substring(0, SHA_SHORT_LENGTH)));
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+ catch (e) {
28
+ console.warn(`${LOG_PREFIX.DONE} Warning: Could not check if branch is merged: ${e.message}`);
29
+ return false;
30
+ }
31
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Cleanup helpers for wu:done.
3
+ */
4
+ /**
5
+ * Run cleanup operations after successful merge
6
+ * Removes worktree and optionally deletes lane branch
7
+ */
8
+ export declare function runCleanup(docMain: any, args: any): Promise<void>;
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Cleanup helpers for wu:done.
3
+ */
4
+ import { existsSync } from 'node:fs';
5
+ import { getGitForCwd } from './git-adapter.js';
6
+ import { withCleanupLock } from './cleanup-lock.js';
7
+ import { validateWorktreeOwnership } from './worktree-ownership.js';
8
+ import { getCleanupInstallConfig, CLEANUP_INSTALL_TIMEOUT_MS } from './cleanup-install-config.js';
9
+ import { createValidationError } from './wu-done-errors.js';
10
+ import { defaultWorktreeFrom, defaultBranchFrom, branchExists } from './wu-done-paths.js';
11
+ import { isBranchAlreadyMerged } from './wu-done-branch-utils.js';
12
+ import { CLAIMED_MODES, EMOJI, LOG_PREFIX, REMOTES } from './wu-constants.js';
13
+ import { exec as execCallback } from 'node:child_process';
14
+ import { promisify } from 'node:util';
15
+ const execAsync = promisify(execCallback);
16
+ /**
17
+ * Run cleanup operations after successful merge
18
+ * Removes worktree and optionally deletes lane branch
19
+ */
20
+ export async function runCleanup(docMain, args) {
21
+ const wuId = docMain.id;
22
+ const worktreePath = args.worktree || (await defaultWorktreeFrom(docMain));
23
+ // WU-2278: Validate worktree ownership before cleanup
24
+ // Prevents cross-agent worktree deletion
25
+ if (!args.overrideOwner) {
26
+ const ownershipResult = validateWorktreeOwnership({ worktreePath, wuId });
27
+ if (!ownershipResult.valid) {
28
+ throw createValidationError(`${ownershipResult.error}\n\nTo override (DANGEROUS): pnpm wu:done --id ${wuId} --override-owner --reason "explanation"`, { wuId, worktreePath, error: ownershipResult.error });
29
+ }
30
+ }
31
+ // WU-2241: Wrap cleanup operations in cleanup lock to prevent concurrent collision
32
+ await withCleanupLock(wuId, async () => {
33
+ await runCleanupInternal(docMain, args, worktreePath);
34
+ }, { worktreePath });
35
+ }
36
+ /**
37
+ * Internal cleanup implementation (runs under cleanup lock)
38
+ */
39
+ async function runCleanupInternal(docMain, args, worktreePath) {
40
+ // Step 6: Remove worktree (runs even if commit/push failed)
41
+ // Skip removal in PR mode (worktree needed for cleanup after PR merge)
42
+ const claimedMode = docMain.claimed_mode || CLAIMED_MODES.WORKTREE;
43
+ const requiresReview = docMain.requires_review === true;
44
+ const prModeEnabled = claimedMode === CLAIMED_MODES.WORKTREE_PR || args.createPR || requiresReview;
45
+ // WU-2241: Track branch for cleanup after worktree removal
46
+ const laneBranch = await defaultBranchFrom(docMain);
47
+ if (!args.noRemove && !prModeEnabled) {
48
+ if (worktreePath && existsSync(worktreePath)) {
49
+ try {
50
+ await getGitForCwd().worktreeRemove(worktreePath, { force: true });
51
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Removed worktree ${worktreePath}`);
52
+ // WU-2241: Delete branch AFTER worktree removal (correct ordering)
53
+ // This ensures we don't leave orphan branches when worktree is removed
54
+ if (laneBranch && (await branchExists(laneBranch))) {
55
+ await deleteBranchWithCleanup(laneBranch);
56
+ }
57
+ // WU-1743: Re-run pnpm install to fix broken symlinks
58
+ // When pnpm install runs in a worktree, it may create symlinks with absolute paths
59
+ // to the worktree. After worktree removal, these symlinks break.
60
+ // Re-running pnpm install regenerates them with correct paths.
61
+ // WU-2278: Use timeout and CI=true to prevent hangs
62
+ console.log(`${LOG_PREFIX.DONE} Reinstalling dependencies to fix symlinks...`);
63
+ try {
64
+ const installConfig = getCleanupInstallConfig();
65
+ await execAsync(installConfig.command, {
66
+ timeout: installConfig.timeout,
67
+ env: installConfig.env,
68
+ });
69
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Dependencies reinstalled`);
70
+ }
71
+ catch (installErr) {
72
+ // Non-fatal: warn but don't fail wu:done
73
+ // WU-2278: Include timeout info in error message
74
+ const isTimeout = installErr.killed || installErr.signal === 'SIGTERM';
75
+ const errorMsg = isTimeout
76
+ ? `pnpm install timed out after ${CLEANUP_INSTALL_TIMEOUT_MS / 1000}s`
77
+ : `pnpm install failed: ${installErr.message}`;
78
+ console.warn(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} ${errorMsg}`);
79
+ }
80
+ }
81
+ catch (e) {
82
+ console.warn(`${LOG_PREFIX.DONE} Could not remove worktree ${worktreePath}: ${e.message}`);
83
+ }
84
+ }
85
+ else {
86
+ console.log(`${LOG_PREFIX.DONE} Worktree not found; skipping removal`);
87
+ // WU-2241: Still cleanup branch if worktree doesn't exist (orphan branch scenario)
88
+ if (!prModeEnabled && laneBranch && (await branchExists(laneBranch))) {
89
+ await deleteBranchWithCleanup(laneBranch);
90
+ }
91
+ }
92
+ }
93
+ else if (prModeEnabled) {
94
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.WARNING} Worktree preserved (PR mode - run wu:cleanup after PR merge)`);
95
+ }
96
+ }
97
+ /**
98
+ * WU-2241: Delete both local and remote branch with proper error handling
99
+ */
100
+ async function deleteBranchWithCleanup(laneBranch) {
101
+ const gitAdapter = getGitForCwd();
102
+ // WU-1440: Check if branch is merged before deletion
103
+ // Use -D (force) when confirmed merged to handle rebased branches
104
+ const isMerged = await isBranchAlreadyMerged(laneBranch);
105
+ try {
106
+ await gitAdapter.deleteBranch(laneBranch, { force: isMerged });
107
+ const modeIndicator = isMerged ? ' (force: merged)' : '';
108
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Deleted local branch ${laneBranch}${modeIndicator}`);
109
+ // Also delete remote if it exists
110
+ try {
111
+ await gitAdapter.raw(['push', REMOTES.ORIGIN, '--delete', laneBranch]);
112
+ console.log(`${LOG_PREFIX.DONE} ${EMOJI.SUCCESS} Deleted remote branch ${laneBranch}`);
113
+ }
114
+ catch (e) {
115
+ // WU-2241: Non-fatal - remote branch may already be deleted or never existed
116
+ console.warn(`${LOG_PREFIX.DONE} Could not delete remote branch: ${e.message}`);
117
+ }
118
+ }
119
+ catch (e) {
120
+ console.warn(`${LOG_PREFIX.DONE} Could not delete branch ${laneBranch}: ${e.message}`);
121
+ }
122
+ }
@@ -0,0 +1,73 @@
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
+ /**
11
+ * Pathspecs for files that affect generated documentation.
12
+ * When any of these files change, docs:generate should be run.
13
+ *
14
+ * Based on .husky/hooks/docs-sync.mjs patterns, expanded to match
15
+ * all files that can affect CLI/config documentation.
16
+ */
17
+ export declare const DOC_SOURCE_PATHSPECS: readonly ["tools/generate-cli-docs.ts", "packages/@lumenflow/core/src/arg-parser.ts", "packages/@lumenflow/core/src/lumenflow-config-schema.ts", "packages/@lumenflow/core/src/index.ts", "packages/@lumenflow/cli/package.json", "packages/@lumenflow/cli/src/"];
18
+ /**
19
+ * Output files generated by docs:generate.
20
+ * These files are staged before the metadata commit.
21
+ */
22
+ export declare const DOC_OUTPUT_FILES: readonly ["apps/docs/src/content/docs/reference/cli.mdx", "apps/docs/src/content/docs/reference/config.mdx"];
23
+ /**
24
+ * Check if any doc-source files changed compared to the base branch.
25
+ * Uses git diff with pathspecs for efficient detection.
26
+ *
27
+ * @param baseBranch - Base branch for comparison (e.g., 'main', 'origin/main')
28
+ * @returns Promise<boolean> - True if doc-source files changed
29
+ */
30
+ export declare function hasDocSourceChanges(baseBranch: string): Promise<boolean>;
31
+ /**
32
+ * Stage doc output files for the metadata commit.
33
+ *
34
+ * @returns Promise<void>
35
+ */
36
+ export declare function stageDocOutputs(): Promise<void>;
37
+ /**
38
+ * Run turbo docs:generate to regenerate documentation.
39
+ * Turbo handles build dependencies and caching.
40
+ *
41
+ * @param repoRoot - Repository root directory
42
+ * @returns void
43
+ */
44
+ export declare function runDocsGenerate(repoRoot: string): void;
45
+ /**
46
+ * Result of the docs regeneration check.
47
+ */
48
+ export interface DocsRegenerationResult {
49
+ /** Whether doc-source files changed */
50
+ docsChanged: boolean;
51
+ /** Whether docs were regenerated */
52
+ regenerated: boolean;
53
+ }
54
+ /**
55
+ * Options for maybeRegenerateAndStageDocs.
56
+ */
57
+ export interface MaybeRegenerateDocsOptions {
58
+ /** Base branch for comparison (e.g., 'main', calculated from defaultBranchFrom) */
59
+ baseBranch: string;
60
+ /** Repository root directory for running turbo */
61
+ repoRoot: string;
62
+ }
63
+ /**
64
+ * Detect doc-source changes and regenerate docs if needed.
65
+ * This is the main integration point for wu:done.
66
+ *
67
+ * Call this BEFORE stageAndFormatMetadata() to include doc outputs
68
+ * in the single atomic commit.
69
+ *
70
+ * @param options - Detection and regeneration options
71
+ * @returns Promise<DocsRegenerationResult> - Whether docs changed and were regenerated
72
+ */
73
+ export declare function maybeRegenerateAndStageDocs(options: MaybeRegenerateDocsOptions): Promise<DocsRegenerationResult>;