@lumenflow/core 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (263) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +119 -0
  3. package/dist/active-wu-detector.d.ts +33 -0
  4. package/dist/active-wu-detector.js +106 -0
  5. package/dist/adapters/filesystem-metrics.adapter.d.ts +108 -0
  6. package/dist/adapters/filesystem-metrics.adapter.js +519 -0
  7. package/dist/adapters/terminal-renderer.adapter.d.ts +106 -0
  8. package/dist/adapters/terminal-renderer.adapter.js +337 -0
  9. package/dist/arg-parser.d.ts +63 -0
  10. package/dist/arg-parser.js +560 -0
  11. package/dist/backlog-editor.d.ts +98 -0
  12. package/dist/backlog-editor.js +179 -0
  13. package/dist/backlog-generator.d.ts +111 -0
  14. package/dist/backlog-generator.js +381 -0
  15. package/dist/backlog-parser.d.ts +45 -0
  16. package/dist/backlog-parser.js +102 -0
  17. package/dist/backlog-sync-validator.d.ts +78 -0
  18. package/dist/backlog-sync-validator.js +294 -0
  19. package/dist/branch-drift.d.ts +34 -0
  20. package/dist/branch-drift.js +51 -0
  21. package/dist/cleanup-install-config.d.ts +33 -0
  22. package/dist/cleanup-install-config.js +37 -0
  23. package/dist/cleanup-lock.d.ts +139 -0
  24. package/dist/cleanup-lock.js +313 -0
  25. package/dist/code-path-validator.d.ts +146 -0
  26. package/dist/code-path-validator.js +537 -0
  27. package/dist/code-paths-overlap.d.ts +55 -0
  28. package/dist/code-paths-overlap.js +245 -0
  29. package/dist/commands-logger.d.ts +77 -0
  30. package/dist/commands-logger.js +254 -0
  31. package/dist/commit-message-utils.d.ts +25 -0
  32. package/dist/commit-message-utils.js +41 -0
  33. package/dist/compliance-parser.d.ts +150 -0
  34. package/dist/compliance-parser.js +507 -0
  35. package/dist/constants/backlog-patterns.d.ts +20 -0
  36. package/dist/constants/backlog-patterns.js +23 -0
  37. package/dist/constants/dora-constants.d.ts +49 -0
  38. package/dist/constants/dora-constants.js +53 -0
  39. package/dist/constants/gate-constants.d.ts +15 -0
  40. package/dist/constants/gate-constants.js +15 -0
  41. package/dist/constants/linter-constants.d.ts +16 -0
  42. package/dist/constants/linter-constants.js +16 -0
  43. package/dist/constants/tokenizer-constants.d.ts +15 -0
  44. package/dist/constants/tokenizer-constants.js +15 -0
  45. package/dist/core/scope-checker.d.ts +97 -0
  46. package/dist/core/scope-checker.js +163 -0
  47. package/dist/core/tool-runner.d.ts +161 -0
  48. package/dist/core/tool-runner.js +393 -0
  49. package/dist/core/tool.constants.d.ts +105 -0
  50. package/dist/core/tool.constants.js +101 -0
  51. package/dist/core/tool.schemas.d.ts +226 -0
  52. package/dist/core/tool.schemas.js +226 -0
  53. package/dist/core/worktree-guard.d.ts +130 -0
  54. package/dist/core/worktree-guard.js +242 -0
  55. package/dist/coverage-gate.d.ts +108 -0
  56. package/dist/coverage-gate.js +196 -0
  57. package/dist/date-utils.d.ts +75 -0
  58. package/dist/date-utils.js +140 -0
  59. package/dist/dependency-graph.d.ts +142 -0
  60. package/dist/dependency-graph.js +550 -0
  61. package/dist/dependency-guard.d.ts +54 -0
  62. package/dist/dependency-guard.js +142 -0
  63. package/dist/dependency-validator.d.ts +105 -0
  64. package/dist/dependency-validator.js +154 -0
  65. package/dist/docs-path-validator.d.ts +36 -0
  66. package/dist/docs-path-validator.js +95 -0
  67. package/dist/domain/orchestration.constants.d.ts +99 -0
  68. package/dist/domain/orchestration.constants.js +97 -0
  69. package/dist/domain/orchestration.schemas.d.ts +280 -0
  70. package/dist/domain/orchestration.schemas.js +211 -0
  71. package/dist/domain/orchestration.types.d.ts +133 -0
  72. package/dist/domain/orchestration.types.js +12 -0
  73. package/dist/error-handler.d.ts +116 -0
  74. package/dist/error-handler.js +136 -0
  75. package/dist/file-classifiers.d.ts +62 -0
  76. package/dist/file-classifiers.js +108 -0
  77. package/dist/gates-agent-mode.d.ts +81 -0
  78. package/dist/gates-agent-mode.js +94 -0
  79. package/dist/generate-traceability.d.ts +107 -0
  80. package/dist/generate-traceability.js +411 -0
  81. package/dist/git-adapter.d.ts +395 -0
  82. package/dist/git-adapter.js +649 -0
  83. package/dist/git-staged-validator.d.ts +32 -0
  84. package/dist/git-staged-validator.js +48 -0
  85. package/dist/hardcoded-strings.d.ts +61 -0
  86. package/dist/hardcoded-strings.js +270 -0
  87. package/dist/incremental-lint.d.ts +78 -0
  88. package/dist/incremental-lint.js +129 -0
  89. package/dist/incremental-test.d.ts +39 -0
  90. package/dist/incremental-test.js +61 -0
  91. package/dist/index.d.ts +42 -0
  92. package/dist/index.js +61 -0
  93. package/dist/invariants/check-automated-tests.d.ts +50 -0
  94. package/dist/invariants/check-automated-tests.js +166 -0
  95. package/dist/invariants-runner.d.ts +103 -0
  96. package/dist/invariants-runner.js +527 -0
  97. package/dist/lane-checker.d.ts +50 -0
  98. package/dist/lane-checker.js +319 -0
  99. package/dist/lane-inference.d.ts +39 -0
  100. package/dist/lane-inference.js +195 -0
  101. package/dist/lane-lock.d.ts +211 -0
  102. package/dist/lane-lock.js +474 -0
  103. package/dist/lane-validator.d.ts +48 -0
  104. package/dist/lane-validator.js +114 -0
  105. package/dist/logs-lib.d.ts +104 -0
  106. package/dist/logs-lib.js +207 -0
  107. package/dist/lumenflow-config-schema.d.ts +272 -0
  108. package/dist/lumenflow-config-schema.js +207 -0
  109. package/dist/lumenflow-config.d.ts +95 -0
  110. package/dist/lumenflow-config.js +236 -0
  111. package/dist/manual-test-validator.d.ts +80 -0
  112. package/dist/manual-test-validator.js +200 -0
  113. package/dist/merge-lock.d.ts +115 -0
  114. package/dist/merge-lock.js +251 -0
  115. package/dist/micro-worktree.d.ts +159 -0
  116. package/dist/micro-worktree.js +427 -0
  117. package/dist/migration-deployer.d.ts +69 -0
  118. package/dist/migration-deployer.js +151 -0
  119. package/dist/orchestration-advisory-loader.d.ts +28 -0
  120. package/dist/orchestration-advisory-loader.js +87 -0
  121. package/dist/orchestration-advisory.d.ts +58 -0
  122. package/dist/orchestration-advisory.js +94 -0
  123. package/dist/orchestration-di.d.ts +48 -0
  124. package/dist/orchestration-di.js +57 -0
  125. package/dist/orchestration-rules.d.ts +57 -0
  126. package/dist/orchestration-rules.js +201 -0
  127. package/dist/orphan-detector.d.ts +131 -0
  128. package/dist/orphan-detector.js +226 -0
  129. package/dist/path-classifiers.d.ts +57 -0
  130. package/dist/path-classifiers.js +93 -0
  131. package/dist/piped-command-detector.d.ts +34 -0
  132. package/dist/piped-command-detector.js +64 -0
  133. package/dist/ports/dashboard-renderer.port.d.ts +112 -0
  134. package/dist/ports/dashboard-renderer.port.js +25 -0
  135. package/dist/ports/metrics-collector.port.d.ts +132 -0
  136. package/dist/ports/metrics-collector.port.js +26 -0
  137. package/dist/process-detector.d.ts +84 -0
  138. package/dist/process-detector.js +172 -0
  139. package/dist/prompt-linter.d.ts +72 -0
  140. package/dist/prompt-linter.js +312 -0
  141. package/dist/prompt-monitor.d.ts +15 -0
  142. package/dist/prompt-monitor.js +205 -0
  143. package/dist/rebase-artifact-cleanup.d.ts +145 -0
  144. package/dist/rebase-artifact-cleanup.js +433 -0
  145. package/dist/retry-strategy.d.ts +189 -0
  146. package/dist/retry-strategy.js +283 -0
  147. package/dist/risk-detector.d.ts +108 -0
  148. package/dist/risk-detector.js +252 -0
  149. package/dist/rollback-utils.d.ts +76 -0
  150. package/dist/rollback-utils.js +104 -0
  151. package/dist/section-headings.d.ts +43 -0
  152. package/dist/section-headings.js +49 -0
  153. package/dist/spawn-escalation.d.ts +90 -0
  154. package/dist/spawn-escalation.js +253 -0
  155. package/dist/spawn-monitor.d.ts +229 -0
  156. package/dist/spawn-monitor.js +672 -0
  157. package/dist/spawn-recovery.d.ts +82 -0
  158. package/dist/spawn-recovery.js +298 -0
  159. package/dist/spawn-registry-schema.d.ts +98 -0
  160. package/dist/spawn-registry-schema.js +108 -0
  161. package/dist/spawn-registry-store.d.ts +146 -0
  162. package/dist/spawn-registry-store.js +273 -0
  163. package/dist/spawn-tree.d.ts +121 -0
  164. package/dist/spawn-tree.js +285 -0
  165. package/dist/stamp-status-validator.d.ts +84 -0
  166. package/dist/stamp-status-validator.js +134 -0
  167. package/dist/stamp-utils.d.ts +100 -0
  168. package/dist/stamp-utils.js +229 -0
  169. package/dist/state-machine.d.ts +26 -0
  170. package/dist/state-machine.js +83 -0
  171. package/dist/system-map-validator.d.ts +80 -0
  172. package/dist/system-map-validator.js +272 -0
  173. package/dist/telemetry.d.ts +80 -0
  174. package/dist/telemetry.js +213 -0
  175. package/dist/token-counter.d.ts +51 -0
  176. package/dist/token-counter.js +145 -0
  177. package/dist/usecases/get-dashboard-data.usecase.d.ts +52 -0
  178. package/dist/usecases/get-dashboard-data.usecase.js +61 -0
  179. package/dist/usecases/get-suggestions.usecase.d.ts +100 -0
  180. package/dist/usecases/get-suggestions.usecase.js +153 -0
  181. package/dist/user-normalizer.d.ts +41 -0
  182. package/dist/user-normalizer.js +141 -0
  183. package/dist/validators/phi-constants.d.ts +97 -0
  184. package/dist/validators/phi-constants.js +152 -0
  185. package/dist/validators/phi-scanner.d.ts +58 -0
  186. package/dist/validators/phi-scanner.js +215 -0
  187. package/dist/worktree-ownership.d.ts +50 -0
  188. package/dist/worktree-ownership.js +74 -0
  189. package/dist/worktree-scanner.d.ts +103 -0
  190. package/dist/worktree-scanner.js +168 -0
  191. package/dist/worktree-symlink.d.ts +99 -0
  192. package/dist/worktree-symlink.js +359 -0
  193. package/dist/wu-backlog-updater.d.ts +17 -0
  194. package/dist/wu-backlog-updater.js +37 -0
  195. package/dist/wu-checkpoint.d.ts +124 -0
  196. package/dist/wu-checkpoint.js +233 -0
  197. package/dist/wu-claim-helpers.d.ts +26 -0
  198. package/dist/wu-claim-helpers.js +63 -0
  199. package/dist/wu-claim-resume.d.ts +106 -0
  200. package/dist/wu-claim-resume.js +276 -0
  201. package/dist/wu-consistency-checker.d.ts +95 -0
  202. package/dist/wu-consistency-checker.js +567 -0
  203. package/dist/wu-constants.d.ts +1275 -0
  204. package/dist/wu-constants.js +1382 -0
  205. package/dist/wu-create-validators.d.ts +42 -0
  206. package/dist/wu-create-validators.js +93 -0
  207. package/dist/wu-done-branch-only.d.ts +63 -0
  208. package/dist/wu-done-branch-only.js +191 -0
  209. package/dist/wu-done-messages.d.ts +119 -0
  210. package/dist/wu-done-messages.js +185 -0
  211. package/dist/wu-done-pr.d.ts +72 -0
  212. package/dist/wu-done-pr.js +174 -0
  213. package/dist/wu-done-retry-helpers.d.ts +85 -0
  214. package/dist/wu-done-retry-helpers.js +172 -0
  215. package/dist/wu-done-ui.d.ts +37 -0
  216. package/dist/wu-done-ui.js +69 -0
  217. package/dist/wu-done-validators.d.ts +411 -0
  218. package/dist/wu-done-validators.js +1229 -0
  219. package/dist/wu-done-worktree.d.ts +182 -0
  220. package/dist/wu-done-worktree.js +1097 -0
  221. package/dist/wu-helpers.d.ts +128 -0
  222. package/dist/wu-helpers.js +248 -0
  223. package/dist/wu-lint.d.ts +70 -0
  224. package/dist/wu-lint.js +234 -0
  225. package/dist/wu-paths.d.ts +171 -0
  226. package/dist/wu-paths.js +178 -0
  227. package/dist/wu-preflight-validators.d.ts +86 -0
  228. package/dist/wu-preflight-validators.js +251 -0
  229. package/dist/wu-recovery.d.ts +138 -0
  230. package/dist/wu-recovery.js +341 -0
  231. package/dist/wu-repair-core.d.ts +131 -0
  232. package/dist/wu-repair-core.js +669 -0
  233. package/dist/wu-schema-normalization.d.ts +17 -0
  234. package/dist/wu-schema-normalization.js +82 -0
  235. package/dist/wu-schema.d.ts +793 -0
  236. package/dist/wu-schema.js +881 -0
  237. package/dist/wu-spawn-helpers.d.ts +121 -0
  238. package/dist/wu-spawn-helpers.js +271 -0
  239. package/dist/wu-spawn.d.ts +158 -0
  240. package/dist/wu-spawn.js +1306 -0
  241. package/dist/wu-state-schema.d.ts +213 -0
  242. package/dist/wu-state-schema.js +156 -0
  243. package/dist/wu-state-store.d.ts +264 -0
  244. package/dist/wu-state-store.js +691 -0
  245. package/dist/wu-status-transition.d.ts +63 -0
  246. package/dist/wu-status-transition.js +382 -0
  247. package/dist/wu-status-updater.d.ts +25 -0
  248. package/dist/wu-status-updater.js +116 -0
  249. package/dist/wu-transaction-collectors.d.ts +116 -0
  250. package/dist/wu-transaction-collectors.js +272 -0
  251. package/dist/wu-transaction.d.ts +170 -0
  252. package/dist/wu-transaction.js +273 -0
  253. package/dist/wu-validation-constants.d.ts +60 -0
  254. package/dist/wu-validation-constants.js +66 -0
  255. package/dist/wu-validation.d.ts +118 -0
  256. package/dist/wu-validation.js +243 -0
  257. package/dist/wu-validator.d.ts +62 -0
  258. package/dist/wu-validator.js +325 -0
  259. package/dist/wu-yaml-fixer.d.ts +97 -0
  260. package/dist/wu-yaml-fixer.js +264 -0
  261. package/dist/wu-yaml.d.ts +86 -0
  262. package/dist/wu-yaml.js +222 -0
  263. package/package.json +114 -0
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Worktree Symlink Utilities
3
+ *
4
+ * WU-1443: Auto-symlink node_modules in new worktrees
5
+ * WU-1579: Extend to symlink nested package node_modules
6
+ * WU-2238: Detect and refuse symlinks when target contains worktree-path symlinks
7
+ *
8
+ * After wu:claim creates a worktree, running pnpm commands fails with
9
+ * 'node_modules missing'. This module provides the symlink helper to
10
+ * auto-create the node_modules symlink pointing to the main repo's
11
+ * node_modules directory, including nested package node_modules.
12
+ *
13
+ * @module tools/lib/worktree-symlink.mjs
14
+ */
15
+ /**
16
+ * List of nested package/app paths that have their own node_modules
17
+ *
18
+ * pnpm monorepos create node_modules in each package with symlinks
19
+ * to the .pnpm store. turbo typecheck and tests need these to resolve imports.
20
+ *
21
+ * @type {string[]}
22
+ */
23
+ export declare const NESTED_PACKAGE_PATHS: string[];
24
+ /**
25
+ * Check if a node_modules directory contains symlinks pointing into worktrees
26
+ *
27
+ * WU-2238: When pnpm install runs inside a worktree, it can create symlinks
28
+ * that point to the worktree's .pnpm store. When that worktree is removed,
29
+ * these symlinks become broken and cause ERR_MODULE_NOT_FOUND.
30
+ *
31
+ * @param {string} nodeModulesPath - Absolute path to node_modules directory
32
+ * @returns {{hasWorktreeSymlinks: boolean, brokenSymlinks: string[]}}
33
+ */
34
+ export declare function hasWorktreePathSymlinks(nodeModulesPath: any): {
35
+ hasWorktreeSymlinks: boolean;
36
+ brokenSymlinks: any[];
37
+ };
38
+ /**
39
+ * Create symlink to node_modules in a newly created worktree
40
+ *
41
+ * This enables immediate pnpm command execution without manual symlink creation.
42
+ * The symlink is relative for portability across different project locations.
43
+ *
44
+ * WU-2238: When mainRepoPath is provided, checks if the target node_modules
45
+ * contains symlinks pointing into worktrees. If so, refuses to create the
46
+ * symlink to prevent inheriting broken module resolution.
47
+ *
48
+ * @param {string} worktreePath - Absolute path to the worktree directory
49
+ * @param {object} [logger] - Logger object with info/warn methods (defaults to console)
50
+ * @param {string} [mainRepoPath] - Optional: Absolute path to main repo for worktree-path check
51
+ * @returns {{created: boolean, skipped: boolean, refused?: boolean, reason?: string, error?: Error}}
52
+ *
53
+ * @example
54
+ * // In wu-claim.mjs after worktree creation:
55
+ * symlinkNodeModules('/path/to/worktrees/operations-tooling-wu-1443');
56
+ */
57
+ export declare function symlinkNodeModules(worktreePath: any, logger?: Console, mainRepoPath?: any): {
58
+ created: boolean;
59
+ skipped: boolean;
60
+ refused?: undefined;
61
+ reason?: undefined;
62
+ error?: undefined;
63
+ } | {
64
+ created: boolean;
65
+ skipped: boolean;
66
+ refused: boolean;
67
+ reason: string;
68
+ error?: undefined;
69
+ } | {
70
+ created: boolean;
71
+ skipped: boolean;
72
+ error: any;
73
+ refused?: undefined;
74
+ reason?: undefined;
75
+ };
76
+ /**
77
+ * Create symlinks for nested package node_modules in a worktree
78
+ *
79
+ * WU-1579: pnpm monorepos have node_modules in each package containing
80
+ * symlinks to the .pnpm store. turbo typecheck needs these to resolve
81
+ * package imports correctly.
82
+ *
83
+ * @param {string} worktreePath - Absolute path to the worktree directory
84
+ * @param {string} mainRepoPath - Absolute path to the main repository root
85
+ * @param {object} [logger] - Logger object with info/warn methods (defaults to silent)
86
+ * @returns {{created: number, skipped: number, errors: Error[]}}
87
+ *
88
+ * @example
89
+ * // In wu-claim.mjs after worktree creation:
90
+ * symlinkNestedNodeModules(
91
+ * '/path/to/worktrees/operations-tooling-wu-1579',
92
+ * '/path/to/main-repo'
93
+ * );
94
+ */
95
+ export declare function symlinkNestedNodeModules(worktreePath: any, mainRepoPath: any, logger?: any): {
96
+ created: number;
97
+ skipped: number;
98
+ errors: any[];
99
+ };
@@ -0,0 +1,359 @@
1
+ /**
2
+ * Worktree Symlink Utilities
3
+ *
4
+ * WU-1443: Auto-symlink node_modules in new worktrees
5
+ * WU-1579: Extend to symlink nested package node_modules
6
+ * WU-2238: Detect and refuse symlinks when target contains worktree-path symlinks
7
+ *
8
+ * After wu:claim creates a worktree, running pnpm commands fails with
9
+ * 'node_modules missing'. This module provides the symlink helper to
10
+ * auto-create the node_modules symlink pointing to the main repo's
11
+ * node_modules directory, including nested package node_modules.
12
+ *
13
+ * @module tools/lib/worktree-symlink.mjs
14
+ */
15
+ import fs from 'node:fs';
16
+ import path from 'node:path';
17
+ import { LOG_PREFIX } from './wu-constants.js';
18
+ /**
19
+ * Relative path from worktree to main repo's node_modules
20
+ *
21
+ * Worktrees are at: worktrees/<lane>-wu-<id>/
22
+ * node_modules is at: ./node_modules (project root)
23
+ *
24
+ * So from worktree: ../../node_modules
25
+ */
26
+ const RELATIVE_NODE_MODULES_PATH = '../../node_modules';
27
+ /**
28
+ * node_modules directory name
29
+ */
30
+ const NODE_MODULES_DIR = 'node_modules';
31
+ /**
32
+ * Pattern to detect paths that are inside a worktrees directory.
33
+ * Used to identify symlinks that may break when worktrees are removed.
34
+ */
35
+ const WORKTREES_PATH_SEGMENT = '/worktrees/';
36
+ /**
37
+ * pnpm store directory name (contains package symlinks)
38
+ */
39
+ const PNPM_DIR = '.pnpm';
40
+ /**
41
+ * List of nested package/app paths that have their own node_modules
42
+ *
43
+ * pnpm monorepos create node_modules in each package with symlinks
44
+ * to the .pnpm store. turbo typecheck and tests need these to resolve imports.
45
+ *
46
+ * @type {string[]}
47
+ */
48
+ export const NESTED_PACKAGE_PATHS = [
49
+ // Packages - supabase
50
+ 'packages/supabase',
51
+ // Packages - @patientpath/*
52
+ 'packages/@patientpath/prompts',
53
+ 'packages/@patientpath/shared',
54
+ 'packages/@patientpath/application',
55
+ 'packages/@patientpath/ports',
56
+ 'packages/@patientpath/infrastructure',
57
+ // Packages - @lumenflow/* (WU-2427: added for typecheck resolution)
58
+ 'packages/@lumenflow/api',
59
+ 'packages/@lumenflow/application',
60
+ 'packages/@lumenflow/infrastructure',
61
+ // Packages - lumenflow-*
62
+ 'packages/lumenflow-cli',
63
+ 'packages/lumenflow-tools',
64
+ // Packages - beacon-explainer (WU-2427: added for typecheck resolution)
65
+ 'packages/beacon-explainer',
66
+ // Apps
67
+ 'apps/web',
68
+ 'apps/mobile',
69
+ 'apps/hellm-ai', // WU-2427: added for typecheck resolution
70
+ ];
71
+ /**
72
+ * Check if a symlink target points into a worktrees directory
73
+ *
74
+ * @param {string} linkTarget - The symlink target path (relative or absolute)
75
+ * @param {string} basePath - Base path to resolve relative targets against
76
+ * @returns {{isWorktreePath: boolean, absoluteTarget: string, isBroken: boolean}}
77
+ */
78
+ function checkSymlinkTarget(linkTarget, basePath) {
79
+ const absoluteTarget = path.isAbsolute(linkTarget)
80
+ ? linkTarget
81
+ : path.resolve(basePath, linkTarget);
82
+ const isWorktreePath = absoluteTarget.includes(WORKTREES_PATH_SEGMENT);
83
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated path from symlink
84
+ const isBroken = isWorktreePath && !fs.existsSync(absoluteTarget);
85
+ return { isWorktreePath, absoluteTarget, isBroken };
86
+ }
87
+ /**
88
+ * Process a single symlink entry to check for worktree-path references
89
+ *
90
+ * @param {string} entryPath - Full path to the symlink
91
+ * @param {string} basePath - Base path to resolve relative targets
92
+ * @param {{hasWorktreeSymlinks: boolean, brokenSymlinks: string[]}} result - Result object to mutate
93
+ */
94
+ function processSymlinkEntry(entryPath, basePath, result) {
95
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated path from directory scan
96
+ const linkTarget = fs.readlinkSync(entryPath);
97
+ const check = checkSymlinkTarget(linkTarget, basePath);
98
+ if (check.isWorktreePath) {
99
+ result.hasWorktreeSymlinks = true;
100
+ if (check.isBroken) {
101
+ result.brokenSymlinks.push(entryPath);
102
+ }
103
+ }
104
+ }
105
+ /**
106
+ * Scan .pnpm directory for symlinks pointing into worktrees
107
+ * Only scans top-level entries to avoid deep recursion (performance)
108
+ *
109
+ * @param {string} pnpmPath - Absolute path to node_modules/.pnpm directory
110
+ * @returns {{hasWorktreeSymlinks: boolean, brokenSymlinks: string[]}}
111
+ */
112
+ function scanPnpmForWorktreeSymlinks(pnpmPath) {
113
+ const result = { hasWorktreeSymlinks: false, brokenSymlinks: [] };
114
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated path
115
+ const entries = fs.readdirSync(pnpmPath, { withFileTypes: true });
116
+ for (const entry of entries) {
117
+ if (entry.isSymbolicLink()) {
118
+ const entryPath = path.join(pnpmPath, entry.name);
119
+ processSymlinkEntry(entryPath, pnpmPath, result);
120
+ }
121
+ }
122
+ return result;
123
+ }
124
+ /**
125
+ * Check if a node_modules directory contains symlinks pointing into worktrees
126
+ *
127
+ * WU-2238: When pnpm install runs inside a worktree, it can create symlinks
128
+ * that point to the worktree's .pnpm store. When that worktree is removed,
129
+ * these symlinks become broken and cause ERR_MODULE_NOT_FOUND.
130
+ *
131
+ * @param {string} nodeModulesPath - Absolute path to node_modules directory
132
+ * @returns {{hasWorktreeSymlinks: boolean, brokenSymlinks: string[]}}
133
+ */
134
+ export function hasWorktreePathSymlinks(nodeModulesPath) {
135
+ const result = { hasWorktreeSymlinks: false, brokenSymlinks: [] };
136
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated path
137
+ if (!fs.existsSync(nodeModulesPath)) {
138
+ return result;
139
+ }
140
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated path
141
+ const entries = fs.readdirSync(nodeModulesPath, { withFileTypes: true });
142
+ for (const entry of entries) {
143
+ const entryPath = path.join(nodeModulesPath, entry.name);
144
+ if (entry.isSymbolicLink()) {
145
+ processSymlinkEntry(entryPath, nodeModulesPath, result);
146
+ }
147
+ else if (entry.name === PNPM_DIR && entry.isDirectory()) {
148
+ const pnpmResult = scanPnpmForWorktreeSymlinks(entryPath);
149
+ if (pnpmResult.hasWorktreeSymlinks) {
150
+ result.hasWorktreeSymlinks = true;
151
+ result.brokenSymlinks.push(...pnpmResult.brokenSymlinks);
152
+ }
153
+ }
154
+ }
155
+ return result;
156
+ }
157
+ /**
158
+ * Check if node_modules already exists at the target path
159
+ *
160
+ * @param {string} targetPath - Path to check
161
+ * @returns {boolean} True if exists (should skip)
162
+ */
163
+ function nodeModulesExists(targetPath) {
164
+ try {
165
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated worktree path
166
+ fs.lstatSync(targetPath);
167
+ return true;
168
+ }
169
+ catch {
170
+ return false;
171
+ }
172
+ }
173
+ /**
174
+ * Check if main repo node_modules has worktree-path symlinks
175
+ *
176
+ * @param {string} mainRepoPath - Path to main repo
177
+ * @param {object} logger - Logger with warn method
178
+ * @returns {{refused: boolean, reason?: string}}
179
+ */
180
+ function checkMainNodeModulesHealth(mainRepoPath, logger) {
181
+ const mainNodeModulesPath = path.join(mainRepoPath, NODE_MODULES_DIR);
182
+ const check = hasWorktreePathSymlinks(mainNodeModulesPath);
183
+ if (!check.hasWorktreeSymlinks) {
184
+ return { refused: false };
185
+ }
186
+ const reason = check.brokenSymlinks.length > 0
187
+ ? `Main node_modules contains broken worktree-path symlinks: ${check.brokenSymlinks.slice(0, 3).join(', ')}${check.brokenSymlinks.length > 3 ? ` (+${check.brokenSymlinks.length - 3} more)` : ''}`
188
+ : 'Main node_modules contains symlinks pointing into worktrees directory';
189
+ if (logger.warn) {
190
+ logger.warn(`${LOG_PREFIX.CLAIM} Refusing to symlink node_modules: ${reason}`);
191
+ logger.warn(`${LOG_PREFIX.CLAIM} Run 'pnpm install' in the worktree instead, or heal main with 'pnpm install --force'`);
192
+ }
193
+ return { refused: true, reason };
194
+ }
195
+ /**
196
+ * Create symlink to node_modules in a newly created worktree
197
+ *
198
+ * This enables immediate pnpm command execution without manual symlink creation.
199
+ * The symlink is relative for portability across different project locations.
200
+ *
201
+ * WU-2238: When mainRepoPath is provided, checks if the target node_modules
202
+ * contains symlinks pointing into worktrees. If so, refuses to create the
203
+ * symlink to prevent inheriting broken module resolution.
204
+ *
205
+ * @param {string} worktreePath - Absolute path to the worktree directory
206
+ * @param {object} [logger] - Logger object with info/warn methods (defaults to console)
207
+ * @param {string} [mainRepoPath] - Optional: Absolute path to main repo for worktree-path check
208
+ * @returns {{created: boolean, skipped: boolean, refused?: boolean, reason?: string, error?: Error}}
209
+ *
210
+ * @example
211
+ * // In wu-claim.mjs after worktree creation:
212
+ * symlinkNodeModules('/path/to/worktrees/operations-tooling-wu-1443');
213
+ */
214
+ export function symlinkNodeModules(worktreePath, logger = console, mainRepoPath = null) {
215
+ const targetPath = path.join(worktreePath, NODE_MODULES_DIR);
216
+ // Check if node_modules already exists (idempotent)
217
+ if (nodeModulesExists(targetPath)) {
218
+ return { created: false, skipped: true };
219
+ }
220
+ // WU-2238: Check if main repo's node_modules contains worktree-path symlinks
221
+ if (mainRepoPath) {
222
+ const healthCheck = checkMainNodeModulesHealth(mainRepoPath, logger);
223
+ if (healthCheck.refused) {
224
+ return { created: false, skipped: false, refused: true, reason: healthCheck.reason };
225
+ }
226
+ }
227
+ try {
228
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated worktree path
229
+ fs.symlinkSync(RELATIVE_NODE_MODULES_PATH, targetPath);
230
+ if (logger.info) {
231
+ logger.info(`${LOG_PREFIX.CLAIM} node_modules symlink created -> ${RELATIVE_NODE_MODULES_PATH}`);
232
+ }
233
+ return { created: true, skipped: false };
234
+ }
235
+ catch (error) {
236
+ if (logger.warn) {
237
+ logger.warn(`${LOG_PREFIX.CLAIM} Failed to create node_modules symlink: ${error.message}`);
238
+ }
239
+ return { created: false, skipped: false, error };
240
+ }
241
+ }
242
+ /**
243
+ * Check if a directory should be skipped for symlink creation
244
+ *
245
+ * @param {string} targetDir - Target directory path
246
+ * @param {string} sourceNodeModules - Source node_modules path
247
+ * @returns {boolean} True if should skip
248
+ */
249
+ function shouldSkipNestedPackage(targetDir, sourceNodeModules) {
250
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated paths
251
+ if (!fs.existsSync(targetDir)) {
252
+ return true;
253
+ }
254
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated paths
255
+ if (!fs.existsSync(sourceNodeModules)) {
256
+ return true;
257
+ }
258
+ return false;
259
+ }
260
+ /**
261
+ * Handle existing node_modules in nested package
262
+ *
263
+ * @param {string} targetNodeModules - Target node_modules path
264
+ * @param {string} pkgPath - Package path for logging
265
+ * @param {object|null} logger - Logger object
266
+ * @param {Error[]} errors - Errors array to mutate
267
+ * @returns {'skip'|'replace'|'create'} Action to take
268
+ */
269
+ function handleExistingNestedNodeModules(targetNodeModules, pkgPath, logger, errors) {
270
+ let targetStat;
271
+ try {
272
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated paths
273
+ targetStat = fs.lstatSync(targetNodeModules);
274
+ }
275
+ catch {
276
+ return 'create'; // Doesn't exist, create symlink
277
+ }
278
+ // If already a symlink, skip (idempotent)
279
+ if (targetStat.isSymbolicLink()) {
280
+ return 'skip';
281
+ }
282
+ // If not a directory, skip
283
+ if (!targetStat.isDirectory()) {
284
+ return 'skip';
285
+ }
286
+ // Check if directory has meaningful content
287
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated paths
288
+ const contents = fs.readdirSync(targetNodeModules);
289
+ const hasMeaningfulContent = contents.some((item) => !item.startsWith('.') && item !== '.vite' && item !== '.turbo');
290
+ if (hasMeaningfulContent) {
291
+ return 'skip';
292
+ }
293
+ // Only cache files - remove and replace with symlink
294
+ try {
295
+ fs.rmSync(targetNodeModules, { recursive: true, force: true });
296
+ return 'replace';
297
+ }
298
+ catch (rmError) {
299
+ errors.push(rmError);
300
+ if (logger?.warn) {
301
+ logger.warn(`${LOG_PREFIX.CLAIM} Failed to remove stale ${pkgPath}/node_modules: ${rmError.message}`);
302
+ }
303
+ return 'skip';
304
+ }
305
+ }
306
+ /**
307
+ * Create symlinks for nested package node_modules in a worktree
308
+ *
309
+ * WU-1579: pnpm monorepos have node_modules in each package containing
310
+ * symlinks to the .pnpm store. turbo typecheck needs these to resolve
311
+ * package imports correctly.
312
+ *
313
+ * @param {string} worktreePath - Absolute path to the worktree directory
314
+ * @param {string} mainRepoPath - Absolute path to the main repository root
315
+ * @param {object} [logger] - Logger object with info/warn methods (defaults to silent)
316
+ * @returns {{created: number, skipped: number, errors: Error[]}}
317
+ *
318
+ * @example
319
+ * // In wu-claim.mjs after worktree creation:
320
+ * symlinkNestedNodeModules(
321
+ * '/path/to/worktrees/operations-tooling-wu-1579',
322
+ * '/path/to/main-repo'
323
+ * );
324
+ */
325
+ export function symlinkNestedNodeModules(worktreePath, mainRepoPath, logger = null) {
326
+ let created = 0;
327
+ let skipped = 0;
328
+ const errors = [];
329
+ for (const pkgPath of NESTED_PACKAGE_PATHS) {
330
+ const targetDir = path.join(worktreePath, pkgPath);
331
+ const targetNodeModules = path.join(targetDir, NODE_MODULES_DIR);
332
+ const sourceNodeModules = path.join(mainRepoPath, pkgPath, NODE_MODULES_DIR);
333
+ if (shouldSkipNestedPackage(targetDir, sourceNodeModules)) {
334
+ skipped++;
335
+ continue;
336
+ }
337
+ const action = handleExistingNestedNodeModules(targetNodeModules, pkgPath, logger, errors);
338
+ if (action === 'skip') {
339
+ skipped++;
340
+ continue;
341
+ }
342
+ try {
343
+ const relativePath = path.relative(targetDir, sourceNodeModules);
344
+ // eslint-disable-next-line security/detect-non-literal-fs-filename -- validated paths
345
+ fs.symlinkSync(relativePath, targetNodeModules);
346
+ created++;
347
+ if (logger?.info) {
348
+ logger.info(`${LOG_PREFIX.CLAIM} ${pkgPath}/node_modules symlink created`);
349
+ }
350
+ }
351
+ catch (error) {
352
+ errors.push(error);
353
+ if (logger?.warn) {
354
+ logger.warn(`${LOG_PREFIX.CLAIM} Failed to create ${pkgPath}/node_modules symlink: ${error.message}`);
355
+ }
356
+ }
357
+ }
358
+ return { created, skipped, errors };
359
+ }
@@ -0,0 +1,17 @@
1
+ /**
2
+ * Backlog.md Update Utilities
3
+ *
4
+ * Centralized backlog.md update functions (extracted from wu-done.mjs)
5
+ * Refactored to use BacklogManager (WU-1212) for AST-based manipulation
6
+ *
7
+ * Used by both main wu:done flow AND recovery mode (DRY principle)
8
+ */
9
+ /**
10
+ * Move WU to Done section in backlog.md (idempotent)
11
+ * WU-1574: Simplified to use state store + generator (no BacklogManager)
12
+ *
13
+ * @param {string} backlogPath - Path to backlog.md
14
+ * @param {string} id - WU ID
15
+ * @param {string} title - WU title (unused - state store has it)
16
+ */
17
+ export declare function moveWUToDoneBacklog(backlogPath: any, id: any, title: any): Promise<void>;
@@ -0,0 +1,37 @@
1
+ /**
2
+ * Backlog.md Update Utilities
3
+ *
4
+ * Centralized backlog.md update functions (extracted from wu-done.mjs)
5
+ * Refactored to use BacklogManager (WU-1212) for AST-based manipulation
6
+ *
7
+ * Used by both main wu:done flow AND recovery mode (DRY principle)
8
+ */
9
+ import { writeFile } from 'node:fs/promises';
10
+ import { LOG_PREFIX } from './wu-constants.js';
11
+ import { WUStateStore } from './wu-state-store.js';
12
+ // WU-1574: Use backlog generator instead of BacklogManager
13
+ import { generateBacklog } from './backlog-generator.js';
14
+ import { getStateStoreDirFromBacklog } from './wu-paths.js';
15
+ /**
16
+ * Move WU to Done section in backlog.md (idempotent)
17
+ * WU-1574: Simplified to use state store + generator (no BacklogManager)
18
+ *
19
+ * @param {string} backlogPath - Path to backlog.md
20
+ * @param {string} id - WU ID
21
+ * @param {string} title - WU title (unused - state store has it)
22
+ */
23
+ export async function moveWUToDoneBacklog(backlogPath, id, title) {
24
+ const PREFIX = LOG_PREFIX.DONE;
25
+ const stateDir = getStateStoreDirFromBacklog(backlogPath);
26
+ // WU-1574: State store is now the single source of truth
27
+ // 1. Mark complete in state store
28
+ // 2. Regenerate backlog.md from state
29
+ const store = new WUStateStore(stateDir);
30
+ await store.load();
31
+ await store.complete(id);
32
+ console.log(`${PREFIX} Complete event appended to state store`);
33
+ // Regenerate backlog.md from state store
34
+ const content = await generateBacklog(store);
35
+ await writeFile(backlogPath, content, 'utf-8');
36
+ console.log(`${PREFIX} Backlog.md regenerated from state store`);
37
+ }
@@ -0,0 +1,124 @@
1
+ /**
2
+ * WU-1747: WU Checkpoint Module
3
+ *
4
+ * Provides checkpoint-based gate resumption for wu:done operations.
5
+ * Allows failed wu:done to resume from checkpoint without re-running gates
6
+ * if nothing has changed since the checkpoint was created.
7
+ *
8
+ * Features:
9
+ * - Pre-gates checkpoint creation with worktree state
10
+ * - SHA-based change detection
11
+ * - Schema versioning for forward compatibility
12
+ * - Automatic stale checkpoint cleanup
13
+ *
14
+ * @module wu-checkpoint
15
+ */
16
+ /**
17
+ * Schema version for checkpoint files
18
+ * Increment when checkpoint format changes
19
+ */
20
+ export declare const CHECKPOINT_SCHEMA_VERSION = 1;
21
+ /**
22
+ * @typedef {Object} Checkpoint
23
+ * @property {number} schemaVersion - Checkpoint format version
24
+ * @property {string} checkpointId - Unique checkpoint identifier
25
+ * @property {string} wuId - WU ID this checkpoint is for
26
+ * @property {string} worktreePath - Path to worktree when checkpoint was created
27
+ * @property {string} branchName - Lane branch name
28
+ * @property {string} worktreeHeadSha - Git HEAD SHA of worktree at checkpoint time
29
+ * @property {string} createdAt - ISO timestamp when checkpoint was created
30
+ * @property {boolean} gatesPassed - Whether gates passed at this checkpoint
31
+ * @property {string} [gatesPassedAt] - ISO timestamp when gates passed
32
+ */
33
+ /**
34
+ * Options for checkpoint operations
35
+ */
36
+ interface CheckpointBaseDirOptions {
37
+ /** Base directory (defaults to cwd) */
38
+ baseDir?: string;
39
+ }
40
+ /**
41
+ * Options for creating pre-gates checkpoint
42
+ */
43
+ export interface CreatePreGatesCheckpointOptions extends CheckpointBaseDirOptions {
44
+ /** Whether gates already passed (for testing) */
45
+ gatesPassed?: boolean;
46
+ }
47
+ /**
48
+ * Create a checkpoint before running gates
49
+ *
50
+ * @param {Object} params - Checkpoint parameters
51
+ * @param {string} params.wuId - WU ID
52
+ * @param {string} params.worktreePath - Path to worktree
53
+ * @param {string} params.branchName - Lane branch name
54
+ * @param {CreatePreGatesCheckpointOptions} [options]
55
+ * @returns {Promise<Checkpoint>} Created checkpoint
56
+ */
57
+ export declare function createPreGatesCheckpoint(params: any, options?: CreatePreGatesCheckpointOptions): Promise<{
58
+ schemaVersion: number;
59
+ checkpointId: string;
60
+ wuId: any;
61
+ worktreePath: any;
62
+ branchName: any;
63
+ worktreeHeadSha: string;
64
+ createdAt: string;
65
+ gatesPassed: any;
66
+ gatesPassedAt: string;
67
+ }>;
68
+ /**
69
+ * Mark a checkpoint as having passed gates
70
+ *
71
+ * @param {string} wuId - WU ID
72
+ * @param {CheckpointBaseDirOptions} [options]
73
+ * @returns {boolean} True if checkpoint was updated
74
+ */
75
+ export declare function markGatesPassed(wuId: any, options?: CheckpointBaseDirOptions): boolean;
76
+ /**
77
+ * Get a checkpoint for a WU
78
+ *
79
+ * @param {string} wuId - WU ID
80
+ * @param {CheckpointBaseDirOptions} [options]
81
+ * @returns {Checkpoint|null} Checkpoint or null if not found
82
+ */
83
+ export declare function getCheckpoint(wuId: any, options?: CheckpointBaseDirOptions): any;
84
+ /**
85
+ * Clear a checkpoint for a WU
86
+ *
87
+ * @param {string} wuId - WU ID
88
+ * @param {CheckpointBaseDirOptions} [options]
89
+ */
90
+ export declare function clearCheckpoint(wuId: any, options?: CheckpointBaseDirOptions): void;
91
+ /**
92
+ * @typedef {Object} CanSkipResult
93
+ * @property {boolean} canSkip - Whether gates can be skipped
94
+ * @property {string} [reason] - Reason if canSkip is false
95
+ * @property {Checkpoint} [checkpoint] - Checkpoint if canSkip is true
96
+ */
97
+ /**
98
+ * Check if gates can be skipped based on checkpoint
99
+ *
100
+ * Gates can be skipped if:
101
+ * 1. A valid checkpoint exists
102
+ * 2. Schema version matches
103
+ * 3. Gates already passed at checkpoint
104
+ * 4. Worktree HEAD SHA hasn't changed
105
+ * 5. Checkpoint isn't stale
106
+ *
107
+ * @param {string} wuId - WU ID
108
+ * @param {CanSkipGatesOptions} [options]
109
+ * @returns {CanSkipResult} Result indicating if gates can be skipped
110
+ */
111
+ export interface CanSkipGatesOptions extends CheckpointBaseDirOptions {
112
+ /** Current worktree HEAD SHA to compare */
113
+ currentHeadSha?: string;
114
+ }
115
+ export declare function canSkipGates(wuId: any, options?: CanSkipGatesOptions): {
116
+ canSkip: boolean;
117
+ reason: string;
118
+ checkpoint?: undefined;
119
+ } | {
120
+ canSkip: boolean;
121
+ checkpoint: any;
122
+ reason?: undefined;
123
+ };
124
+ export {};