@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.
- package/dist/arg-parser.d.ts +6 -0
- package/dist/arg-parser.js +16 -0
- package/dist/core/tool.schemas.d.ts +1 -1
- package/dist/coverage-gate.d.ts +3 -0
- package/dist/coverage-gate.js +7 -4
- package/dist/force-bypass-audit.d.ts +63 -0
- package/dist/force-bypass-audit.js +140 -0
- package/dist/gates-config.d.ts +132 -0
- package/dist/gates-config.js +229 -0
- package/dist/git-adapter.d.ts +7 -0
- package/dist/git-adapter.js +15 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.js +6 -0
- package/dist/lumenflow-config-schema.d.ts +97 -0
- package/dist/lumenflow-config-schema.js +9 -0
- package/dist/lumenflow-home.d.ts +130 -0
- package/dist/lumenflow-home.js +211 -0
- package/dist/manual-test-validator.d.ts +3 -0
- package/dist/manual-test-validator.js +7 -4
- package/dist/orphan-detector.d.ts +16 -0
- package/dist/orphan-detector.js +24 -0
- package/dist/prompt-linter.js +2 -1
- package/dist/prompt-monitor.js +3 -1
- package/dist/spec-branch-helpers.d.ts +118 -0
- package/dist/spec-branch-helpers.js +199 -0
- package/dist/user-normalizer.d.ts +5 -1
- package/dist/user-normalizer.js +6 -1
- package/dist/validators/phi-scanner.js +6 -0
- package/dist/worktree-symlink.d.ts +4 -0
- package/dist/worktree-symlink.js +14 -20
- package/dist/wu-consistency-checker.d.ts +2 -0
- package/dist/wu-consistency-checker.js +35 -1
- package/dist/wu-constants.d.ts +193 -0
- package/dist/wu-constants.js +200 -4
- package/dist/wu-create-validators.d.ts +57 -2
- package/dist/wu-create-validators.js +111 -2
- package/dist/wu-done-branch-only.js +9 -0
- package/dist/wu-done-docs-generate.d.ts +73 -0
- package/dist/wu-done-docs-generate.js +108 -0
- package/dist/wu-done-worktree.js +12 -0
- package/dist/wu-schema.js +3 -1
- package/dist/wu-spawn.js +15 -2
- package/dist/wu-yaml-fixer.js +6 -3
- package/package.json +12 -11
package/dist/git-adapter.js
CHANGED
|
@@ -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', '.
|
|
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/@
|
|
47
|
-
'packages/@
|
|
48
|
-
'packages/@
|
|
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
|
*
|
package/dist/orphan-detector.js
CHANGED
|
@@ -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
|
*
|
package/dist/prompt-linter.js
CHANGED
|
@@ -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
|
-
|
|
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
|
package/dist/prompt-monitor.js
CHANGED
|
@@ -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
|
-
|
|
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
|