@lumenflow/core 1.3.0 → 1.3.3

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 (44) hide show
  1. package/dist/arg-parser.d.ts +6 -0
  2. package/dist/arg-parser.js +16 -0
  3. package/dist/core/tool.schemas.d.ts +1 -1
  4. package/dist/coverage-gate.d.ts +3 -0
  5. package/dist/coverage-gate.js +7 -4
  6. package/dist/force-bypass-audit.d.ts +63 -0
  7. package/dist/force-bypass-audit.js +140 -0
  8. package/dist/gates-config.d.ts +132 -0
  9. package/dist/gates-config.js +229 -0
  10. package/dist/git-adapter.d.ts +7 -0
  11. package/dist/git-adapter.js +15 -1
  12. package/dist/index.d.ts +3 -0
  13. package/dist/index.js +6 -0
  14. package/dist/lumenflow-config-schema.d.ts +97 -0
  15. package/dist/lumenflow-config-schema.js +9 -0
  16. package/dist/lumenflow-home.d.ts +130 -0
  17. package/dist/lumenflow-home.js +211 -0
  18. package/dist/manual-test-validator.d.ts +3 -0
  19. package/dist/manual-test-validator.js +7 -4
  20. package/dist/orphan-detector.d.ts +16 -0
  21. package/dist/orphan-detector.js +24 -0
  22. package/dist/prompt-linter.js +2 -1
  23. package/dist/prompt-monitor.js +3 -1
  24. package/dist/spec-branch-helpers.d.ts +118 -0
  25. package/dist/spec-branch-helpers.js +199 -0
  26. package/dist/user-normalizer.d.ts +5 -1
  27. package/dist/user-normalizer.js +6 -1
  28. package/dist/validators/phi-scanner.js +6 -0
  29. package/dist/worktree-symlink.d.ts +4 -0
  30. package/dist/worktree-symlink.js +14 -20
  31. package/dist/wu-consistency-checker.d.ts +2 -0
  32. package/dist/wu-consistency-checker.js +35 -1
  33. package/dist/wu-constants.d.ts +193 -0
  34. package/dist/wu-constants.js +200 -4
  35. package/dist/wu-create-validators.d.ts +57 -2
  36. package/dist/wu-create-validators.js +111 -2
  37. package/dist/wu-done-branch-only.js +9 -0
  38. package/dist/wu-done-docs-generate.d.ts +73 -0
  39. package/dist/wu-done-docs-generate.js +108 -0
  40. package/dist/wu-done-worktree.js +12 -0
  41. package/dist/wu-schema.js +3 -1
  42. package/dist/wu-spawn.js +15 -2
  43. package/dist/wu-yaml-fixer.js +6 -3
  44. package/package.json +12 -11
@@ -18,7 +18,7 @@
18
18
  */
19
19
  import { simpleGit } from 'simple-git';
20
20
  import { existsSync, rmSync } from 'node:fs';
21
- import { GIT_FLAGS } from './wu-constants.js';
21
+ import { GIT_COMMANDS, GIT_FLAGS, GIT_REFS } from './wu-constants.js';
22
22
  // WU-2242: Runtime assertion helpers
23
23
  /**
24
24
  * Assert that a value is a non-empty string
@@ -117,6 +117,20 @@ export class GitAdapter {
117
117
  const result = await this.git.raw(['status', GIT_FLAGS.PORCELAIN]);
118
118
  return result.trim();
119
119
  }
120
+ /**
121
+ * Get unpushed commits (compared to upstream)
122
+ * @returns {Promise<string>} Oneline log output for unpushed commits
123
+ * @example
124
+ * await git.getUnpushedCommits(); // "abc123 fix: ...\n"
125
+ */
126
+ async getUnpushedCommits() {
127
+ const result = await this.git.raw([
128
+ GIT_COMMANDS.LOG,
129
+ GIT_REFS.UPSTREAM_RANGE,
130
+ GIT_FLAGS.ONELINE,
131
+ ]);
132
+ return result.trim();
133
+ }
120
134
  /**
121
135
  * Check if a branch exists
122
136
  * @param {string} branch - Branch name
package/dist/index.d.ts CHANGED
@@ -40,4 +40,7 @@ export * from './dependency-guard.js';
40
40
  export * from './stamp-utils.js';
41
41
  export * from './lumenflow-config.js';
42
42
  export * from './lumenflow-config-schema.js';
43
+ export * from './gates-config.js';
43
44
  export * from './branch-check.js';
45
+ export * from './lumenflow-home.js';
46
+ export * from './force-bypass-audit.js';
package/dist/index.js CHANGED
@@ -59,5 +59,11 @@ export * from './stamp-utils.js';
59
59
  // Configuration
60
60
  export * from './lumenflow-config.js';
61
61
  export * from './lumenflow-config-schema.js';
62
+ // Gates configuration (WU-1067)
63
+ export * from './gates-config.js';
62
64
  // Branch check utilities
63
65
  export * from './branch-check.js';
66
+ // WU-1062: External plan storage
67
+ export * from './lumenflow-home.js';
68
+ // WU-1070: Force bypass audit logging
69
+ export * from './force-bypass-audit.js';
@@ -67,6 +67,7 @@ export declare const WuConfigSchema: z.ZodObject<{
67
67
  }, z.core.$strip>;
68
68
  /**
69
69
  * Gates configuration
70
+ * Note: GatesExecutionConfigSchema is imported from gates-config.ts
70
71
  */
71
72
  export declare const GatesConfigSchema: z.ZodObject<{
72
73
  maxEslintWarnings: z.ZodDefault<z.ZodNumber>;
@@ -74,6 +75,38 @@ export declare const GatesConfigSchema: z.ZodObject<{
74
75
  minCoverage: z.ZodDefault<z.ZodNumber>;
75
76
  enableSafetyCriticalTests: z.ZodDefault<z.ZodBoolean>;
76
77
  enableInvariants: z.ZodDefault<z.ZodBoolean>;
78
+ execution: z.ZodOptional<z.ZodObject<{
79
+ preset: z.ZodOptional<z.ZodString>;
80
+ setup: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
81
+ command: z.ZodString;
82
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
83
+ timeout: z.ZodOptional<z.ZodNumber>;
84
+ }, z.core.$strip>]>>;
85
+ format: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
86
+ command: z.ZodString;
87
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
88
+ timeout: z.ZodOptional<z.ZodNumber>;
89
+ }, z.core.$strip>]>>;
90
+ lint: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
91
+ command: z.ZodString;
92
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
93
+ timeout: z.ZodOptional<z.ZodNumber>;
94
+ }, z.core.$strip>]>>;
95
+ typecheck: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
96
+ command: z.ZodString;
97
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
98
+ timeout: z.ZodOptional<z.ZodNumber>;
99
+ }, z.core.$strip>]>>;
100
+ test: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
101
+ command: z.ZodString;
102
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
103
+ timeout: z.ZodOptional<z.ZodNumber>;
104
+ }, z.core.$strip>]>>;
105
+ coverage: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
106
+ command: z.ZodString;
107
+ threshold: z.ZodOptional<z.ZodNumber>;
108
+ }, z.core.$strip>]>>;
109
+ }, z.core.$strip>>;
77
110
  }, z.core.$strip>;
78
111
  /**
79
112
  * Memory layer configuration
@@ -227,6 +260,38 @@ export declare const LumenFlowConfigSchema: z.ZodObject<{
227
260
  minCoverage: z.ZodDefault<z.ZodNumber>;
228
261
  enableSafetyCriticalTests: z.ZodDefault<z.ZodBoolean>;
229
262
  enableInvariants: z.ZodDefault<z.ZodBoolean>;
263
+ execution: z.ZodOptional<z.ZodObject<{
264
+ preset: z.ZodOptional<z.ZodString>;
265
+ setup: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
266
+ command: z.ZodString;
267
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
268
+ timeout: z.ZodOptional<z.ZodNumber>;
269
+ }, z.core.$strip>]>>;
270
+ format: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
271
+ command: z.ZodString;
272
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
273
+ timeout: z.ZodOptional<z.ZodNumber>;
274
+ }, z.core.$strip>]>>;
275
+ lint: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
276
+ command: z.ZodString;
277
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
278
+ timeout: z.ZodOptional<z.ZodNumber>;
279
+ }, z.core.$strip>]>>;
280
+ typecheck: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
281
+ command: z.ZodString;
282
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
283
+ timeout: z.ZodOptional<z.ZodNumber>;
284
+ }, z.core.$strip>]>>;
285
+ test: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
286
+ command: z.ZodString;
287
+ continueOnError: z.ZodOptional<z.ZodBoolean>;
288
+ timeout: z.ZodOptional<z.ZodNumber>;
289
+ }, z.core.$strip>]>>;
290
+ coverage: z.ZodOptional<z.ZodUnion<readonly [z.ZodString, z.ZodObject<{
291
+ command: z.ZodString;
292
+ threshold: z.ZodOptional<z.ZodNumber>;
293
+ }, z.core.$strip>]>>;
294
+ }, z.core.$strip>>;
230
295
  }, z.core.$strip>>;
231
296
  memory: z.ZodDefault<z.ZodObject<{
232
297
  directory: z.ZodDefault<z.ZodString>;
@@ -344,6 +409,38 @@ export declare function validateConfig(data: unknown): z.ZodSafeParseResult<{
344
409
  minCoverage: number;
345
410
  enableSafetyCriticalTests: boolean;
346
411
  enableInvariants: boolean;
412
+ execution?: {
413
+ preset?: string;
414
+ setup?: string | {
415
+ command: string;
416
+ continueOnError?: boolean;
417
+ timeout?: number;
418
+ };
419
+ format?: string | {
420
+ command: string;
421
+ continueOnError?: boolean;
422
+ timeout?: number;
423
+ };
424
+ lint?: string | {
425
+ command: string;
426
+ continueOnError?: boolean;
427
+ timeout?: number;
428
+ };
429
+ typecheck?: string | {
430
+ command: string;
431
+ continueOnError?: boolean;
432
+ timeout?: number;
433
+ };
434
+ test?: string | {
435
+ command: string;
436
+ continueOnError?: boolean;
437
+ timeout?: number;
438
+ };
439
+ coverage?: string | {
440
+ command: string;
441
+ threshold?: number;
442
+ };
443
+ };
347
444
  };
348
445
  memory: {
349
446
  directory: string;
@@ -7,6 +7,8 @@
7
7
  * @module lumenflow-config-schema
8
8
  */
9
9
  import { z } from 'zod';
10
+ // WU-1067: Import gates execution schema from canonical source
11
+ import { GatesExecutionConfigSchema } from './gates-config.js';
10
12
  /**
11
13
  * Directory paths configuration
12
14
  */
@@ -110,6 +112,7 @@ export const WuConfigSchema = z.object({
110
112
  });
111
113
  /**
112
114
  * Gates configuration
115
+ * Note: GatesExecutionConfigSchema is imported from gates-config.ts
113
116
  */
114
117
  export const GatesConfigSchema = z.object({
115
118
  /** Maximum ESLint warnings allowed (default: 100) */
@@ -122,6 +125,12 @@ export const GatesConfigSchema = z.object({
122
125
  enableSafetyCriticalTests: z.boolean().default(true),
123
126
  /** Enable invariants check (default: true) */
124
127
  enableInvariants: z.boolean().default(true),
128
+ /**
129
+ * WU-1067: Config-driven gates execution
130
+ * Custom commands for each gate, with optional preset expansion.
131
+ * When set, gates runner uses these instead of hardcoded commands.
132
+ */
133
+ execution: GatesExecutionConfigSchema.optional(),
125
134
  });
126
135
  /**
127
136
  * Memory layer configuration
@@ -0,0 +1,130 @@
1
+ /**
2
+ * LumenFlow Home Directory Resolution
3
+ *
4
+ * WU-1062: External plan storage and no-main-write mode
5
+ *
6
+ * Provides helpers for resolving the $LUMENFLOW_HOME directory and related paths.
7
+ * Plans are stored externally in $LUMENFLOW_HOME/plans/ instead of in the repo.
8
+ *
9
+ * Default: ~/.lumenflow/
10
+ * Override: Set $LUMENFLOW_HOME environment variable
11
+ *
12
+ * @module
13
+ */
14
+ /**
15
+ * Environment variable name for LumenFlow home directory
16
+ */
17
+ export declare const LUMENFLOW_HOME_ENV = "LUMENFLOW_HOME";
18
+ /**
19
+ * Default LumenFlow home directory name
20
+ */
21
+ export declare const DEFAULT_LUMENFLOW_DIR = ".lumenflow";
22
+ /**
23
+ * Plans subdirectory name
24
+ */
25
+ export declare const PLANS_SUBDIR = "plans";
26
+ /**
27
+ * Custom protocol for external LumenFlow paths
28
+ */
29
+ export declare const LUMENFLOW_PROTOCOL = "lumenflow://";
30
+ /**
31
+ * Environment variable prefix for spec_refs
32
+ */
33
+ export declare const LUMENFLOW_HOME_VAR_PREFIX = "$LUMENFLOW_HOME";
34
+ /**
35
+ * Get the LumenFlow home directory path
36
+ *
37
+ * Resolution order:
38
+ * 1. $LUMENFLOW_HOME environment variable (with ~ expansion)
39
+ * 2. ~/.lumenflow/ default
40
+ *
41
+ * @returns {string} Absolute path to LumenFlow home directory
42
+ *
43
+ * @example
44
+ * // With LUMENFLOW_HOME=/custom/path
45
+ * getLumenflowHome() // '/custom/path'
46
+ *
47
+ * @example
48
+ * // With LUMENFLOW_HOME=~/.custom-lumenflow
49
+ * getLumenflowHome() // '/home/user/.custom-lumenflow'
50
+ *
51
+ * @example
52
+ * // Without LUMENFLOW_HOME set
53
+ * getLumenflowHome() // '/home/user/.lumenflow'
54
+ */
55
+ export declare function getLumenflowHome(): string;
56
+ /**
57
+ * Get the plans directory path
58
+ *
59
+ * Plans are stored in $LUMENFLOW_HOME/plans/
60
+ *
61
+ * @returns {string} Absolute path to plans directory
62
+ *
63
+ * @example
64
+ * getPlansDir() // '/home/user/.lumenflow/plans'
65
+ */
66
+ export declare function getPlansDir(): string;
67
+ /**
68
+ * Check if a path is an external (non-repo) path
69
+ *
70
+ * External paths include:
71
+ * - Paths starting with ~/
72
+ * - Paths starting with $LUMENFLOW_HOME
73
+ * - Paths using lumenflow:// protocol
74
+ * - Absolute paths (starting with /)
75
+ *
76
+ * @param {string} path - Path to check
77
+ * @returns {boolean} True if path is external
78
+ *
79
+ * @example
80
+ * isExternalPath('~/.lumenflow/plans/plan.md') // true
81
+ * isExternalPath('$LUMENFLOW_HOME/plans/plan.md') // true
82
+ * isExternalPath('lumenflow://plans/plan.md') // true
83
+ * isExternalPath('/home/user/.lumenflow/plans/plan.md') // true
84
+ * isExternalPath('docs/04-operations/plans/plan.md') // false
85
+ */
86
+ export declare function isExternalPath(path: string): boolean;
87
+ /**
88
+ * Normalize a spec_ref path by expanding variables and protocols
89
+ *
90
+ * Expands:
91
+ * - lumenflow://path -> $LUMENFLOW_HOME/path
92
+ * - ~/path -> /home/user/path
93
+ * - $LUMENFLOW_HOME/path -> actual LUMENFLOW_HOME value
94
+ *
95
+ * Repo-relative paths are returned unchanged.
96
+ *
97
+ * @param {string} specRef - Spec reference path
98
+ * @returns {string} Normalized absolute path or unchanged relative path
99
+ *
100
+ * @example
101
+ * normalizeSpecRef('lumenflow://plans/WU-1062-plan.md')
102
+ * // '/home/user/.lumenflow/plans/WU-1062-plan.md'
103
+ *
104
+ * @example
105
+ * normalizeSpecRef('docs/04-operations/plans/plan.md')
106
+ * // 'docs/04-operations/plans/plan.md' (unchanged)
107
+ */
108
+ export declare function normalizeSpecRef(specRef: string): string;
109
+ /**
110
+ * Get the full path for a plan file given a WU ID
111
+ *
112
+ * @param {string} wuId - Work Unit ID (e.g., 'WU-1062')
113
+ * @returns {string} Full path to the plan file
114
+ *
115
+ * @example
116
+ * getPlanPath('WU-1062')
117
+ * // '/home/user/.lumenflow/plans/WU-1062-plan.md'
118
+ */
119
+ export declare function getPlanPath(wuId: string): string;
120
+ /**
121
+ * Get the lumenflow:// protocol reference for a plan
122
+ *
123
+ * @param {string} wuId - Work Unit ID (e.g., 'WU-1062')
124
+ * @returns {string} Protocol reference (e.g., 'lumenflow://plans/WU-1062-plan.md')
125
+ *
126
+ * @example
127
+ * getPlanProtocolRef('WU-1062')
128
+ * // 'lumenflow://plans/WU-1062-plan.md'
129
+ */
130
+ export declare function getPlanProtocolRef(wuId: string): string;
@@ -0,0 +1,211 @@
1
+ /**
2
+ * LumenFlow Home Directory Resolution
3
+ *
4
+ * WU-1062: External plan storage and no-main-write mode
5
+ *
6
+ * Provides helpers for resolving the $LUMENFLOW_HOME directory and related paths.
7
+ * Plans are stored externally in $LUMENFLOW_HOME/plans/ instead of in the repo.
8
+ *
9
+ * Default: ~/.lumenflow/
10
+ * Override: Set $LUMENFLOW_HOME environment variable
11
+ *
12
+ * @module
13
+ */
14
+ import { homedir } from 'node:os';
15
+ import { join } from 'node:path';
16
+ import { PATH_LITERALS, PATH_SLICE_LENGTHS, STRING_LITERALS } from './wu-constants.js';
17
+ /**
18
+ * Environment variable name for LumenFlow home directory
19
+ */
20
+ export const LUMENFLOW_HOME_ENV = 'LUMENFLOW_HOME';
21
+ /**
22
+ * Default LumenFlow home directory name
23
+ */
24
+ export const DEFAULT_LUMENFLOW_DIR = '.lumenflow';
25
+ /**
26
+ * Plans subdirectory name
27
+ */
28
+ export const PLANS_SUBDIR = 'plans';
29
+ /**
30
+ * Custom protocol for external LumenFlow paths
31
+ */
32
+ export const LUMENFLOW_PROTOCOL = 'lumenflow://';
33
+ /**
34
+ * Environment variable prefix for spec_refs
35
+ */
36
+ export const LUMENFLOW_HOME_VAR_PREFIX = '$LUMENFLOW_HOME';
37
+ /**
38
+ * Expand ~ to user's home directory
39
+ *
40
+ * @param {string} path - Path that may contain ~
41
+ * @returns {string} Expanded path
42
+ */
43
+ function expandTilde(path) {
44
+ if (path.startsWith(PATH_LITERALS.TILDE_PREFIX)) {
45
+ return join(homedir(), path.slice(PATH_SLICE_LENGTHS.TILDE_PREFIX_LENGTH));
46
+ }
47
+ if (path === PATH_LITERALS.TILDE) {
48
+ return homedir();
49
+ }
50
+ return path;
51
+ }
52
+ /**
53
+ * Remove trailing slashes from path
54
+ *
55
+ * @param {string} path - Path that may have trailing slashes
56
+ * @returns {string} Path without trailing slashes
57
+ */
58
+ function removeTrailingSlash(path) {
59
+ return path.replace(PATH_LITERALS.TRAILING_SLASH_REGEX, STRING_LITERALS.EMPTY);
60
+ }
61
+ /**
62
+ * Get the LumenFlow home directory path
63
+ *
64
+ * Resolution order:
65
+ * 1. $LUMENFLOW_HOME environment variable (with ~ expansion)
66
+ * 2. ~/.lumenflow/ default
67
+ *
68
+ * @returns {string} Absolute path to LumenFlow home directory
69
+ *
70
+ * @example
71
+ * // With LUMENFLOW_HOME=/custom/path
72
+ * getLumenflowHome() // '/custom/path'
73
+ *
74
+ * @example
75
+ * // With LUMENFLOW_HOME=~/.custom-lumenflow
76
+ * getLumenflowHome() // '/home/user/.custom-lumenflow'
77
+ *
78
+ * @example
79
+ * // Without LUMENFLOW_HOME set
80
+ * getLumenflowHome() // '/home/user/.lumenflow'
81
+ */
82
+ export function getLumenflowHome() {
83
+ const envValue = process.env[LUMENFLOW_HOME_ENV];
84
+ if (envValue) {
85
+ const expanded = expandTilde(envValue);
86
+ return removeTrailingSlash(expanded);
87
+ }
88
+ return join(homedir(), DEFAULT_LUMENFLOW_DIR);
89
+ }
90
+ /**
91
+ * Get the plans directory path
92
+ *
93
+ * Plans are stored in $LUMENFLOW_HOME/plans/
94
+ *
95
+ * @returns {string} Absolute path to plans directory
96
+ *
97
+ * @example
98
+ * getPlansDir() // '/home/user/.lumenflow/plans'
99
+ */
100
+ export function getPlansDir() {
101
+ return join(getLumenflowHome(), PLANS_SUBDIR);
102
+ }
103
+ /**
104
+ * Check if a path is an external (non-repo) path
105
+ *
106
+ * External paths include:
107
+ * - Paths starting with ~/
108
+ * - Paths starting with $LUMENFLOW_HOME
109
+ * - Paths using lumenflow:// protocol
110
+ * - Absolute paths (starting with /)
111
+ *
112
+ * @param {string} path - Path to check
113
+ * @returns {boolean} True if path is external
114
+ *
115
+ * @example
116
+ * isExternalPath('~/.lumenflow/plans/plan.md') // true
117
+ * isExternalPath('$LUMENFLOW_HOME/plans/plan.md') // true
118
+ * isExternalPath('lumenflow://plans/plan.md') // true
119
+ * isExternalPath('/home/user/.lumenflow/plans/plan.md') // true
120
+ * isExternalPath('docs/04-operations/plans/plan.md') // false
121
+ */
122
+ export function isExternalPath(path) {
123
+ // Check for tilde-prefixed paths
124
+ if (path.startsWith(PATH_LITERALS.TILDE_PREFIX)) {
125
+ return true;
126
+ }
127
+ // Check for environment variable reference
128
+ if (path.startsWith(LUMENFLOW_HOME_VAR_PREFIX)) {
129
+ return true;
130
+ }
131
+ // Check for lumenflow:// protocol
132
+ if (path.startsWith(LUMENFLOW_PROTOCOL)) {
133
+ return true;
134
+ }
135
+ // Check for absolute paths (starting with /)
136
+ if (path.startsWith(STRING_LITERALS.SLASH)) {
137
+ return true;
138
+ }
139
+ return false;
140
+ }
141
+ /**
142
+ * Normalize a spec_ref path by expanding variables and protocols
143
+ *
144
+ * Expands:
145
+ * - lumenflow://path -> $LUMENFLOW_HOME/path
146
+ * - ~/path -> /home/user/path
147
+ * - $LUMENFLOW_HOME/path -> actual LUMENFLOW_HOME value
148
+ *
149
+ * Repo-relative paths are returned unchanged.
150
+ *
151
+ * @param {string} specRef - Spec reference path
152
+ * @returns {string} Normalized absolute path or unchanged relative path
153
+ *
154
+ * @example
155
+ * normalizeSpecRef('lumenflow://plans/WU-1062-plan.md')
156
+ * // '/home/user/.lumenflow/plans/WU-1062-plan.md'
157
+ *
158
+ * @example
159
+ * normalizeSpecRef('docs/04-operations/plans/plan.md')
160
+ * // 'docs/04-operations/plans/plan.md' (unchanged)
161
+ */
162
+ export function normalizeSpecRef(specRef) {
163
+ // Handle lumenflow:// protocol
164
+ if (specRef.startsWith(LUMENFLOW_PROTOCOL)) {
165
+ const relativePath = specRef.slice(LUMENFLOW_PROTOCOL.length);
166
+ return join(getLumenflowHome(), relativePath);
167
+ }
168
+ // Handle $LUMENFLOW_HOME variable
169
+ if (specRef.startsWith(LUMENFLOW_HOME_VAR_PREFIX)) {
170
+ const relativePath = specRef.slice(LUMENFLOW_HOME_VAR_PREFIX.length);
171
+ // Remove leading slash if present
172
+ const cleanPath = relativePath.startsWith(STRING_LITERALS.SLASH)
173
+ ? relativePath.slice(PATH_SLICE_LENGTHS.LEADING_SLASH_LENGTH)
174
+ : relativePath;
175
+ return join(getLumenflowHome(), cleanPath);
176
+ }
177
+ // Handle tilde expansion
178
+ if (specRef.startsWith(PATH_LITERALS.TILDE_PREFIX)) {
179
+ return expandTilde(specRef);
180
+ }
181
+ // Return relative paths unchanged
182
+ return specRef;
183
+ }
184
+ /**
185
+ * Get the full path for a plan file given a WU ID
186
+ *
187
+ * @param {string} wuId - Work Unit ID (e.g., 'WU-1062')
188
+ * @returns {string} Full path to the plan file
189
+ *
190
+ * @example
191
+ * getPlanPath('WU-1062')
192
+ * // '/home/user/.lumenflow/plans/WU-1062-plan.md'
193
+ */
194
+ export function getPlanPath(wuId) {
195
+ const filename = `${wuId}${PATH_LITERALS.PLAN_FILE_SUFFIX}`;
196
+ return join(getPlansDir(), filename);
197
+ }
198
+ /**
199
+ * Get the lumenflow:// protocol reference for a plan
200
+ *
201
+ * @param {string} wuId - Work Unit ID (e.g., 'WU-1062')
202
+ * @returns {string} Protocol reference (e.g., 'lumenflow://plans/WU-1062-plan.md')
203
+ *
204
+ * @example
205
+ * getPlanProtocolRef('WU-1062')
206
+ * // 'lumenflow://plans/WU-1062-plan.md'
207
+ */
208
+ export function getPlanProtocolRef(wuId) {
209
+ const filename = `${wuId}${PATH_LITERALS.PLAN_FILE_SUFFIX}`;
210
+ return `${LUMENFLOW_PROTOCOL}${PLANS_SUBDIR}${STRING_LITERALS.SLASH}${filename}`;
211
+ }
@@ -18,6 +18,9 @@
18
18
  * Path prefixes for hex core code requiring automated tests.
19
19
  * These are the critical application layer paths.
20
20
  *
21
+ * WU-1068: Changed from @patientpath to @lumenflow for framework reusability.
22
+ * Project-specific patterns should be configured in .lumenflow.config.yaml.
23
+ *
21
24
  * @constant {string[]}
22
25
  */
23
26
  export declare const HEX_CORE_CODE_PATTERNS: readonly string[];
@@ -20,7 +20,7 @@ import { TEST_TYPES, WU_TYPES } from './wu-constants.js';
20
20
  * Code file extensions that require automated tests.
21
21
  * @constant {string[]}
22
22
  */
23
- const CODE_EXTENSIONS = Object.freeze(['.js', '.ts', '.tsx', '.js']);
23
+ const CODE_EXTENSIONS = Object.freeze(['.js', '.ts', '.tsx', '.mjs']);
24
24
  /**
25
25
  * Non-code file extensions (documentation, data, config).
26
26
  * @constant {string[]}
@@ -40,12 +40,15 @@ const CONFIG_PATTERNS = Object.freeze([
40
40
  * Path prefixes for hex core code requiring automated tests.
41
41
  * These are the critical application layer paths.
42
42
  *
43
+ * WU-1068: Changed from @patientpath to @lumenflow for framework reusability.
44
+ * Project-specific patterns should be configured in .lumenflow.config.yaml.
45
+ *
43
46
  * @constant {string[]}
44
47
  */
45
48
  export const HEX_CORE_CODE_PATTERNS = Object.freeze([
46
- 'packages/@patientpath/application/',
47
- 'packages/@patientpath/prompts/',
48
- 'packages/@patientpath/ports/',
49
+ 'packages/@lumenflow/core/',
50
+ 'packages/@lumenflow/cli/',
51
+ 'packages/@lumenflow/agent/',
49
52
  ]);
50
53
  /**
51
54
  * @deprecated Lane-based exemptions removed in WU-2332.
@@ -18,6 +18,7 @@
18
18
  * @see {@link tools/wu-claim.mjs} - Pre-flight orphan check
19
19
  * @see {@link tools/lib/git-adapter.mjs} - worktreeRemove with cleanup
20
20
  */
21
+ import { existsSync } from 'node:fs';
21
22
  /**
22
23
  * Result of orphan detection
23
24
  * @typedef {object} OrphanDetectionResult
@@ -50,6 +51,21 @@ export declare function parseWorktreeList(porcelainOutput: string): WorktreeEntr
50
51
  * @returns {Promise<Set<string>>} Set of absolute paths tracked by git worktree
51
52
  */
52
53
  export declare function getTrackedWorktreePaths(projectRoot: any): Promise<Set<string>>;
54
+ /**
55
+ * Identify tracked worktrees that are missing on disk
56
+ *
57
+ * @param {string[]} trackedPaths - Absolute paths from git worktree list
58
+ * @param {(path: string) => boolean} [existsFn] - Optional fs.existsSync override
59
+ * @returns {string[]} Missing worktree paths
60
+ */
61
+ export declare function getMissingWorktreesFromTracked(trackedPaths: any, existsFn?: typeof existsSync): any[];
62
+ /**
63
+ * Detect tracked worktrees that are missing on disk
64
+ *
65
+ * @param {string} [projectRoot] - Project root directory (defaults to cwd)
66
+ * @returns {Promise<string[]>} Missing tracked worktree paths
67
+ */
68
+ export declare function detectMissingTrackedWorktrees(projectRoot: any): Promise<any[]>;
53
69
  /**
54
70
  * Get list of directories in worktrees/ folder
55
71
  *
@@ -75,6 +75,30 @@ export async function getTrackedWorktreePaths(projectRoot) {
75
75
  const entries = parseWorktreeList(output);
76
76
  return new Set(entries.map((e) => e.path));
77
77
  }
78
+ /**
79
+ * Identify tracked worktrees that are missing on disk
80
+ *
81
+ * @param {string[]} trackedPaths - Absolute paths from git worktree list
82
+ * @param {(path: string) => boolean} [existsFn] - Optional fs.existsSync override
83
+ * @returns {string[]} Missing worktree paths
84
+ */
85
+ export function getMissingWorktreesFromTracked(trackedPaths, existsFn = existsSync) {
86
+ if (!Array.isArray(trackedPaths)) {
87
+ return [];
88
+ }
89
+ return trackedPaths.filter((trackedPath) => !existsFn(trackedPath));
90
+ }
91
+ /**
92
+ * Detect tracked worktrees that are missing on disk
93
+ *
94
+ * @param {string} [projectRoot] - Project root directory (defaults to cwd)
95
+ * @returns {Promise<string[]>} Missing tracked worktree paths
96
+ */
97
+ export async function detectMissingTrackedWorktrees(projectRoot) {
98
+ const root = projectRoot || process.cwd();
99
+ const tracked = await getTrackedWorktreePaths(root);
100
+ return getMissingWorktreesFromTracked([...tracked]);
101
+ }
78
102
  /**
79
103
  * Get list of directories in worktrees/ folder
80
104
  *
@@ -249,7 +249,8 @@ export async function lintPrompts(filePaths = [], mode = 'local', configPath = u
249
249
  const output = { quiet: options.quiet === true, verbose: options.verbose === true };
250
250
  // If no files provided, find all orchestrator prompt files (WU-676 scope only)
251
251
  if (filePaths.length === 0) {
252
- const pattern = 'packages/@patientpath/prompts/orchestrator-*/**/*.yaml';
252
+ // WU-1068: Changed from @patientpath to generic ai/prompts for framework reusability
253
+ const pattern = 'ai/prompts/orchestrator-*/**/*.yaml';
253
254
  filePaths = await glob(pattern, { cwd: ROOT_DIR, absolute: true });
254
255
  }
255
256
  // Load previous metrics for delta calculation
@@ -96,7 +96,9 @@ async function log(event, data) {
96
96
  async function monitor() {
97
97
  console.log('\nšŸŒ™ Nightly Prompt Monitor Starting...\n');
98
98
  // Find all prompt files
99
- const pattern = 'packages/@patientpath/prompts/**/*.yaml';
99
+ // WU-1068: Changed from @patientpath to generic ai/prompts for framework reusability
100
+ // Projects should configure prompt paths in .lumenflow.config.yaml
101
+ const pattern = 'ai/prompts/**/*.yaml';
100
102
  const promptFiles = await glob(pattern, { cwd: ROOT_DIR, absolute: true });
101
103
  console.log(`Found ${promptFiles.length} prompt files to analyze\n`);
102
104
  // Load yesterday's metrics for delta calculation