@lumenflow/core 1.0.0 → 1.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/arg-parser.d.ts +6 -0
- package/dist/arg-parser.js +57 -1
- package/dist/backlog-generator.js +1 -1
- package/dist/backlog-sync-validator.js +3 -3
- package/dist/branch-check.d.ts +21 -0
- package/dist/branch-check.js +77 -0
- package/dist/cli/is-agent-branch.d.ts +11 -0
- package/dist/cli/is-agent-branch.js +15 -0
- package/dist/code-paths-overlap.js +2 -2
- package/dist/error-handler.d.ts +1 -0
- package/dist/error-handler.js +4 -1
- package/dist/git-adapter.d.ts +23 -0
- package/dist/git-adapter.js +38 -2
- package/dist/index.d.ts +3 -0
- package/dist/index.js +5 -0
- package/dist/lane-checker.d.ts +36 -3
- package/dist/lane-checker.js +128 -17
- package/dist/lane-inference.js +3 -4
- package/dist/lumenflow-config-schema.d.ts +125 -0
- package/dist/lumenflow-config-schema.js +76 -0
- package/dist/lumenflow-home.d.ts +130 -0
- package/dist/lumenflow-home.js +208 -0
- package/dist/manual-test-validator.js +1 -1
- package/dist/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- package/dist/orphan-detector.d.ts +16 -0
- package/dist/orphan-detector.js +24 -0
- package/dist/path-classifiers.d.ts +1 -1
- package/dist/path-classifiers.js +1 -1
- package/dist/rebase-artifact-cleanup.d.ts +17 -0
- package/dist/rebase-artifact-cleanup.js +49 -8
- package/dist/spawn-strategy.d.ts +53 -0
- package/dist/spawn-strategy.js +106 -0
- package/dist/spec-branch-helpers.d.ts +118 -0
- package/dist/spec-branch-helpers.js +192 -0
- package/dist/stamp-utils.d.ts +10 -0
- package/dist/stamp-utils.js +17 -19
- package/dist/token-counter.js +2 -2
- package/dist/wu-consistency-checker.d.ts +2 -0
- package/dist/wu-consistency-checker.js +40 -6
- package/dist/wu-constants.d.ts +98 -3
- package/dist/wu-constants.js +108 -3
- package/dist/wu-create-validators.d.ts +40 -2
- package/dist/wu-create-validators.js +76 -2
- package/dist/wu-done-branch-only.js +9 -0
- package/dist/wu-done-branch-utils.d.ts +10 -0
- package/dist/wu-done-branch-utils.js +31 -0
- package/dist/wu-done-cleanup.d.ts +8 -0
- package/dist/wu-done-cleanup.js +122 -0
- package/dist/wu-done-docs-generate.d.ts +73 -0
- package/dist/wu-done-docs-generate.js +108 -0
- package/dist/wu-done-docs-only.d.ts +20 -0
- package/dist/wu-done-docs-only.js +65 -0
- package/dist/wu-done-errors.d.ts +17 -0
- package/dist/wu-done-errors.js +24 -0
- package/dist/wu-done-inputs.d.ts +12 -0
- package/dist/wu-done-inputs.js +51 -0
- package/dist/wu-done-metadata.d.ts +100 -0
- package/dist/wu-done-metadata.js +193 -0
- package/dist/wu-done-paths.d.ts +69 -0
- package/dist/wu-done-paths.js +237 -0
- package/dist/wu-done-preflight.d.ts +48 -0
- package/dist/wu-done-preflight.js +185 -0
- package/dist/wu-done-validation.d.ts +82 -0
- package/dist/wu-done-validation.js +340 -0
- package/dist/wu-done-validators.d.ts +13 -409
- package/dist/wu-done-validators.js +9 -1225
- package/dist/wu-done-worktree.d.ts +0 -1
- package/dist/wu-done-worktree.js +24 -30
- package/dist/wu-schema.js +4 -4
- package/dist/wu-spawn-skills.d.ts +19 -0
- package/dist/wu-spawn-skills.js +148 -0
- package/dist/wu-spawn.d.ts +17 -4
- package/dist/wu-spawn.js +113 -177
- package/dist/wu-validation.d.ts +1 -0
- package/dist/wu-validation.js +21 -1
- package/dist/wu-validator.d.ts +51 -0
- package/dist/wu-validator.js +108 -0
- package/package.json +12 -8
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Branch Helpers
|
|
3
|
+
*
|
|
4
|
+
* WU-1062: External plan storage and no-main-write mode
|
|
5
|
+
*
|
|
6
|
+
* Provides helpers for working with spec branches (spec/wu-XXXX).
|
|
7
|
+
* wu:create writes to spec branches by default; wu:claim merges them to main.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import type { SimpleGit } from 'simple-git';
|
|
12
|
+
/**
|
|
13
|
+
* Spec branch prefix
|
|
14
|
+
*/
|
|
15
|
+
export declare const SPEC_BRANCH_PREFIX = "spec/";
|
|
16
|
+
/**
|
|
17
|
+
* WU source location constants
|
|
18
|
+
*/
|
|
19
|
+
export declare const WU_SOURCE: {
|
|
20
|
+
/** WU exists on main branch only */
|
|
21
|
+
readonly MAIN: "main";
|
|
22
|
+
/** WU exists on spec branch only */
|
|
23
|
+
readonly SPEC_BRANCH: "spec_branch";
|
|
24
|
+
/** WU exists on both main and spec branch */
|
|
25
|
+
readonly BOTH: "both";
|
|
26
|
+
/** WU not found anywhere */
|
|
27
|
+
readonly NOT_FOUND: "not_found";
|
|
28
|
+
};
|
|
29
|
+
export type WUSourceType = (typeof WU_SOURCE)[keyof typeof WU_SOURCE];
|
|
30
|
+
/**
|
|
31
|
+
* Get the spec branch name for a WU
|
|
32
|
+
*
|
|
33
|
+
* @param {string} wuId - Work Unit ID (e.g., 'WU-1062')
|
|
34
|
+
* @returns {string} Spec branch name (e.g., 'spec/wu-1062')
|
|
35
|
+
*
|
|
36
|
+
* @example
|
|
37
|
+
* getSpecBranchName('WU-1062') // 'spec/wu-1062'
|
|
38
|
+
*/
|
|
39
|
+
export declare function getSpecBranchName(wuId: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Get the origin-qualified spec branch name
|
|
42
|
+
*
|
|
43
|
+
* @param {string} wuId - Work Unit ID
|
|
44
|
+
* @returns {string} Origin-qualified branch name (e.g., 'origin/spec/wu-1062')
|
|
45
|
+
*/
|
|
46
|
+
export declare function getOriginSpecBranch(wuId: string): string;
|
|
47
|
+
/**
|
|
48
|
+
* Check if a spec branch exists on origin
|
|
49
|
+
*
|
|
50
|
+
* @param {string} wuId - Work Unit ID
|
|
51
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
52
|
+
* @returns {Promise<boolean>} True if spec branch exists
|
|
53
|
+
*
|
|
54
|
+
* @example
|
|
55
|
+
* const exists = await specBranchExists('WU-1062', git);
|
|
56
|
+
*/
|
|
57
|
+
export declare function specBranchExists(wuId: string, git: SimpleGit): Promise<boolean>;
|
|
58
|
+
/**
|
|
59
|
+
* Check if a WU exists on main branch
|
|
60
|
+
*
|
|
61
|
+
* @param {string} wuId - Work Unit ID
|
|
62
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
63
|
+
* @returns {Promise<boolean>} True if WU YAML exists on main
|
|
64
|
+
*/
|
|
65
|
+
export declare function isWUOnMain(wuId: string, git: SimpleGit): Promise<boolean>;
|
|
66
|
+
/**
|
|
67
|
+
* Merge spec branch to main branch (fast-forward only)
|
|
68
|
+
*
|
|
69
|
+
* This is used by wu:claim when a WU exists only on a spec branch.
|
|
70
|
+
* The spec branch is merged to main before creating the worktree.
|
|
71
|
+
*
|
|
72
|
+
* @param {string} wuId - Work Unit ID
|
|
73
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
74
|
+
* @throws {Error} If merge fails (e.g., due to conflicts)
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* await mergeSpecBranchToMain('WU-1062', git);
|
|
78
|
+
*/
|
|
79
|
+
export declare function mergeSpecBranchToMain(wuId: string, git: SimpleGit): Promise<void>;
|
|
80
|
+
/**
|
|
81
|
+
* Delete spec branch after merge
|
|
82
|
+
*
|
|
83
|
+
* @param {string} wuId - Work Unit ID
|
|
84
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
85
|
+
*/
|
|
86
|
+
export declare function deleteSpecBranch(wuId: string, git: SimpleGit): Promise<void>;
|
|
87
|
+
/**
|
|
88
|
+
* Determine the source of a WU (main, spec branch, both, or not found)
|
|
89
|
+
*
|
|
90
|
+
* Used by wu:claim to decide whether to merge spec branch before creating worktree.
|
|
91
|
+
*
|
|
92
|
+
* @param {string} wuId - Work Unit ID
|
|
93
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
94
|
+
* @returns {Promise<WUSourceType>} Source location constant
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* const source = await getWUSource('WU-1062', git);
|
|
98
|
+
* if (source === WU_SOURCE.SPEC_BRANCH) {
|
|
99
|
+
* await mergeSpecBranchToMain('WU-1062', git);
|
|
100
|
+
* }
|
|
101
|
+
*/
|
|
102
|
+
export declare function getWUSource(wuId: string, git: SimpleGit): Promise<WUSourceType>;
|
|
103
|
+
/**
|
|
104
|
+
* Create a spec branch from current HEAD
|
|
105
|
+
*
|
|
106
|
+
* Used by wu:create in default mode (no --direct flag).
|
|
107
|
+
*
|
|
108
|
+
* @param {string} wuId - Work Unit ID
|
|
109
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
110
|
+
*/
|
|
111
|
+
export declare function createSpecBranch(wuId: string, git: SimpleGit): Promise<void>;
|
|
112
|
+
/**
|
|
113
|
+
* Push spec branch to origin
|
|
114
|
+
*
|
|
115
|
+
* @param {string} wuId - Work Unit ID
|
|
116
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
117
|
+
*/
|
|
118
|
+
export declare function pushSpecBranch(wuId: string, git: SimpleGit): Promise<void>;
|
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spec Branch Helpers
|
|
3
|
+
*
|
|
4
|
+
* WU-1062: External plan storage and no-main-write mode
|
|
5
|
+
*
|
|
6
|
+
* Provides helpers for working with spec branches (spec/wu-XXXX).
|
|
7
|
+
* wu:create writes to spec branches by default; wu:claim merges them to main.
|
|
8
|
+
*
|
|
9
|
+
* @module
|
|
10
|
+
*/
|
|
11
|
+
import { WU_PATHS } from './wu-paths.js';
|
|
12
|
+
/**
|
|
13
|
+
* Spec branch prefix
|
|
14
|
+
*/
|
|
15
|
+
export const SPEC_BRANCH_PREFIX = 'spec/';
|
|
16
|
+
/**
|
|
17
|
+
* WU source location constants
|
|
18
|
+
*/
|
|
19
|
+
export const WU_SOURCE = {
|
|
20
|
+
/** WU exists on main branch only */
|
|
21
|
+
MAIN: 'main',
|
|
22
|
+
/** WU exists on spec branch only */
|
|
23
|
+
SPEC_BRANCH: 'spec_branch',
|
|
24
|
+
/** WU exists on both main and spec branch */
|
|
25
|
+
BOTH: 'both',
|
|
26
|
+
/** WU not found anywhere */
|
|
27
|
+
NOT_FOUND: 'not_found',
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Get the spec branch name for a WU
|
|
31
|
+
*
|
|
32
|
+
* @param {string} wuId - Work Unit ID (e.g., 'WU-1062')
|
|
33
|
+
* @returns {string} Spec branch name (e.g., 'spec/wu-1062')
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* getSpecBranchName('WU-1062') // 'spec/wu-1062'
|
|
37
|
+
*/
|
|
38
|
+
export function getSpecBranchName(wuId) {
|
|
39
|
+
return `${SPEC_BRANCH_PREFIX}${wuId.toLowerCase()}`;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Get the origin-qualified spec branch name
|
|
43
|
+
*
|
|
44
|
+
* @param {string} wuId - Work Unit ID
|
|
45
|
+
* @returns {string} Origin-qualified branch name (e.g., 'origin/spec/wu-1062')
|
|
46
|
+
*/
|
|
47
|
+
export function getOriginSpecBranch(wuId) {
|
|
48
|
+
return `origin/${getSpecBranchName(wuId)}`;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Check if a spec branch exists on origin
|
|
52
|
+
*
|
|
53
|
+
* @param {string} wuId - Work Unit ID
|
|
54
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
55
|
+
* @returns {Promise<boolean>} True if spec branch exists
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* const exists = await specBranchExists('WU-1062', git);
|
|
59
|
+
*/
|
|
60
|
+
export async function specBranchExists(wuId, git) {
|
|
61
|
+
try {
|
|
62
|
+
const originBranch = getOriginSpecBranch(wuId);
|
|
63
|
+
// Use branchExists if available, otherwise check with ls-remote
|
|
64
|
+
if ('branchExists' in git && typeof git.branchExists === 'function') {
|
|
65
|
+
return await git.branchExists(originBranch);
|
|
66
|
+
}
|
|
67
|
+
// Fallback: use ls-remote to check if branch exists
|
|
68
|
+
const result = await git.raw(['ls-remote', '--heads', 'origin', getSpecBranchName(wuId)]);
|
|
69
|
+
return result.trim().length > 0;
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Check if a WU exists on main branch
|
|
77
|
+
*
|
|
78
|
+
* @param {string} wuId - Work Unit ID
|
|
79
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
80
|
+
* @returns {Promise<boolean>} True if WU YAML exists on main
|
|
81
|
+
*/
|
|
82
|
+
export async function isWUOnMain(wuId, git) {
|
|
83
|
+
try {
|
|
84
|
+
const wuPath = WU_PATHS.WU(wuId);
|
|
85
|
+
// Check if file exists on origin/main
|
|
86
|
+
await git.raw(['ls-tree', 'origin/main', '--', wuPath]);
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Merge spec branch to main branch (fast-forward only)
|
|
95
|
+
*
|
|
96
|
+
* This is used by wu:claim when a WU exists only on a spec branch.
|
|
97
|
+
* The spec branch is merged to main before creating the worktree.
|
|
98
|
+
*
|
|
99
|
+
* @param {string} wuId - Work Unit ID
|
|
100
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
101
|
+
* @throws {Error} If merge fails (e.g., due to conflicts)
|
|
102
|
+
*
|
|
103
|
+
* @example
|
|
104
|
+
* await mergeSpecBranchToMain('WU-1062', git);
|
|
105
|
+
*/
|
|
106
|
+
export async function mergeSpecBranchToMain(wuId, git) {
|
|
107
|
+
const specBranch = getSpecBranchName(wuId);
|
|
108
|
+
const originSpecBranch = getOriginSpecBranch(wuId);
|
|
109
|
+
// Fetch the spec branch
|
|
110
|
+
await git.fetch('origin', specBranch);
|
|
111
|
+
// Merge with fast-forward only (safe merge)
|
|
112
|
+
await git.merge([originSpecBranch, '--ff-only']);
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Delete spec branch after merge
|
|
116
|
+
*
|
|
117
|
+
* @param {string} wuId - Work Unit ID
|
|
118
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
119
|
+
*/
|
|
120
|
+
export async function deleteSpecBranch(wuId, git) {
|
|
121
|
+
const specBranch = getSpecBranchName(wuId);
|
|
122
|
+
try {
|
|
123
|
+
// Delete local branch if exists
|
|
124
|
+
await git.branch(['-d', specBranch]);
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
// Ignore if local branch doesn't exist
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
// Delete remote branch
|
|
131
|
+
await git.push(['origin', '--delete', specBranch]);
|
|
132
|
+
}
|
|
133
|
+
catch {
|
|
134
|
+
// Ignore if remote branch doesn't exist
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Determine the source of a WU (main, spec branch, both, or not found)
|
|
139
|
+
*
|
|
140
|
+
* Used by wu:claim to decide whether to merge spec branch before creating worktree.
|
|
141
|
+
*
|
|
142
|
+
* @param {string} wuId - Work Unit ID
|
|
143
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
144
|
+
* @returns {Promise<WUSourceType>} Source location constant
|
|
145
|
+
*
|
|
146
|
+
* @example
|
|
147
|
+
* const source = await getWUSource('WU-1062', git);
|
|
148
|
+
* if (source === WU_SOURCE.SPEC_BRANCH) {
|
|
149
|
+
* await mergeSpecBranchToMain('WU-1062', git);
|
|
150
|
+
* }
|
|
151
|
+
*/
|
|
152
|
+
export async function getWUSource(wuId, git) {
|
|
153
|
+
// Check both locations in parallel for efficiency
|
|
154
|
+
const [onMain, hasSpecBranch] = await Promise.all([
|
|
155
|
+
isWUOnMain(wuId, git),
|
|
156
|
+
specBranchExists(wuId, git),
|
|
157
|
+
]);
|
|
158
|
+
if (onMain && hasSpecBranch) {
|
|
159
|
+
return WU_SOURCE.BOTH;
|
|
160
|
+
}
|
|
161
|
+
if (onMain) {
|
|
162
|
+
return WU_SOURCE.MAIN;
|
|
163
|
+
}
|
|
164
|
+
if (hasSpecBranch) {
|
|
165
|
+
return WU_SOURCE.SPEC_BRANCH;
|
|
166
|
+
}
|
|
167
|
+
return WU_SOURCE.NOT_FOUND;
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* Create a spec branch from current HEAD
|
|
171
|
+
*
|
|
172
|
+
* Used by wu:create in default mode (no --direct flag).
|
|
173
|
+
*
|
|
174
|
+
* @param {string} wuId - Work Unit ID
|
|
175
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
176
|
+
*/
|
|
177
|
+
export async function createSpecBranch(wuId, git) {
|
|
178
|
+
const specBranch = getSpecBranchName(wuId);
|
|
179
|
+
// Create local branch
|
|
180
|
+
await git.checkoutLocalBranch(specBranch);
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Push spec branch to origin
|
|
184
|
+
*
|
|
185
|
+
* @param {string} wuId - Work Unit ID
|
|
186
|
+
* @param {SimpleGit} git - Git adapter instance
|
|
187
|
+
*/
|
|
188
|
+
export async function pushSpecBranch(wuId, git) {
|
|
189
|
+
const specBranch = getSpecBranchName(wuId);
|
|
190
|
+
// Push to origin
|
|
191
|
+
await git.push(['origin', specBranch]);
|
|
192
|
+
}
|
package/dist/stamp-utils.d.ts
CHANGED
|
@@ -26,6 +26,16 @@ export declare const STAMP_FORMAT_ERRORS: Readonly<{
|
|
|
26
26
|
/** WU ID in stamp does not match expected ID */
|
|
27
27
|
WU_ID_MISMATCH: "WU_ID_MISMATCH";
|
|
28
28
|
}>;
|
|
29
|
+
/**
|
|
30
|
+
* Validate that a date string is a valid ISO date (YYYY-MM-DD)
|
|
31
|
+
*
|
|
32
|
+
* WU-1006: Uses date-fns parse() and isValid() instead of manual parseInt parsing
|
|
33
|
+
* Library-First principle: leverage well-known libraries over brittle custom code
|
|
34
|
+
*
|
|
35
|
+
* @param {string} dateStr - Date string in YYYY-MM-DD format
|
|
36
|
+
* @returns {boolean} True if date is valid
|
|
37
|
+
*/
|
|
38
|
+
export declare function isValidDateString(dateStr: string): boolean;
|
|
29
39
|
/**
|
|
30
40
|
* Create stamp file (idempotent - safe to call multiple times)
|
|
31
41
|
*
|
package/dist/stamp-utils.js
CHANGED
|
@@ -14,6 +14,7 @@ import { existsSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
|
14
14
|
import { readFile, access } from 'node:fs/promises';
|
|
15
15
|
import { constants } from 'node:fs';
|
|
16
16
|
import path from 'node:path';
|
|
17
|
+
import { parse, isValid } from 'date-fns';
|
|
17
18
|
import { WU_PATHS } from './wu-paths.js';
|
|
18
19
|
import { todayISO } from './date-utils.js';
|
|
19
20
|
/**
|
|
@@ -34,34 +35,31 @@ export const STAMP_FORMAT_ERRORS = Object.freeze({
|
|
|
34
35
|
WU_ID_MISMATCH: 'WU_ID_MISMATCH',
|
|
35
36
|
});
|
|
36
37
|
/**
|
|
37
|
-
* Valid date regex: YYYY-MM-DD format
|
|
38
|
+
* Valid date regex: YYYY-MM-DD format (for format checking before parsing)
|
|
38
39
|
* @type {RegExp}
|
|
39
40
|
*/
|
|
40
|
-
const
|
|
41
|
+
const DATE_FORMAT_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
|
|
41
42
|
/**
|
|
42
|
-
* Validate that a date string is a valid ISO date
|
|
43
|
+
* Validate that a date string is a valid ISO date (YYYY-MM-DD)
|
|
44
|
+
*
|
|
45
|
+
* WU-1006: Uses date-fns parse() and isValid() instead of manual parseInt parsing
|
|
46
|
+
* Library-First principle: leverage well-known libraries over brittle custom code
|
|
47
|
+
*
|
|
43
48
|
* @param {string} dateStr - Date string in YYYY-MM-DD format
|
|
44
49
|
* @returns {boolean} True if date is valid
|
|
45
50
|
*/
|
|
46
|
-
function
|
|
47
|
-
|
|
48
|
-
if (!
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
const year = parseInt(match[1], 10);
|
|
52
|
-
const month = parseInt(match[2], 10);
|
|
53
|
-
const day = parseInt(match[3], 10);
|
|
54
|
-
// Basic validation
|
|
55
|
-
if (month < 1 || month > 12) {
|
|
56
|
-
return false;
|
|
57
|
-
}
|
|
58
|
-
// Check day validity for the month
|
|
59
|
-
const daysInMonth = new Date(year, month, 0).getDate();
|
|
60
|
-
if (day < 1 || day > daysInMonth) {
|
|
51
|
+
export function isValidDateString(dateStr) {
|
|
52
|
+
// Quick format check - must be YYYY-MM-DD pattern
|
|
53
|
+
if (!dateStr || !DATE_FORMAT_PATTERN.test(dateStr)) {
|
|
61
54
|
return false;
|
|
62
55
|
}
|
|
63
|
-
|
|
56
|
+
// Parse with date-fns and validate the result
|
|
57
|
+
// parse() with strict format ensures proper date validation
|
|
58
|
+
const parsed = parse(dateStr, 'yyyy-MM-dd', new Date());
|
|
59
|
+
return isValid(parsed);
|
|
64
60
|
}
|
|
61
|
+
// Internal alias for backward compatibility
|
|
62
|
+
const isValidDate = isValidDateString;
|
|
65
63
|
/**
|
|
66
64
|
* Stamp file body template (eliminates magic string)
|
|
67
65
|
* Single source of truth for stamp format
|
package/dist/token-counter.js
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
import { get_encoding } from 'tiktoken';
|
|
10
10
|
import { readFileSync } from 'fs';
|
|
11
11
|
import { createHash } from 'crypto';
|
|
12
|
-
import {
|
|
12
|
+
import { parseYAML } from './wu-yaml.js';
|
|
13
13
|
import { createError, ErrorCodes } from './error-handler.js';
|
|
14
14
|
import { EXIT_CODES, STRING_LITERALS } from './wu-constants.js';
|
|
15
15
|
// Cache tokenizer instance (expensive to create)
|
|
@@ -65,7 +65,7 @@ export function loadPrompt(promptPath) {
|
|
|
65
65
|
try {
|
|
66
66
|
const raw = readFileSync(promptPath, { encoding: 'utf-8' });
|
|
67
67
|
// Parse YAML to access prompt structure
|
|
68
|
-
const parsed =
|
|
68
|
+
const parsed = parseYAML(raw);
|
|
69
69
|
// Extract prompt text (handle different YAML structures)
|
|
70
70
|
let promptText = '';
|
|
71
71
|
if (typeof parsed === 'string') {
|
|
@@ -30,6 +30,7 @@ export declare function checkWUConsistency(id: any, projectRoot?: string): Promi
|
|
|
30
30
|
backlogInProgress?: undefined;
|
|
31
31
|
statusInProgress?: undefined;
|
|
32
32
|
hasWorktree?: undefined;
|
|
33
|
+
worktreePathExists?: undefined;
|
|
33
34
|
};
|
|
34
35
|
} | {
|
|
35
36
|
valid: boolean;
|
|
@@ -41,6 +42,7 @@ export declare function checkWUConsistency(id: any, projectRoot?: string): Promi
|
|
|
41
42
|
backlogInProgress: boolean;
|
|
42
43
|
statusInProgress: boolean;
|
|
43
44
|
hasWorktree: boolean;
|
|
45
|
+
worktreePathExists: boolean;
|
|
44
46
|
wuExists?: undefined;
|
|
45
47
|
};
|
|
46
48
|
}>;
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
import { readFile, writeFile, readdir, mkdir, access } from 'node:fs/promises';
|
|
16
16
|
import { constants } from 'node:fs';
|
|
17
17
|
import path from 'node:path';
|
|
18
|
-
import
|
|
18
|
+
import { parseYAML, stringifyYAML } from './wu-yaml.js';
|
|
19
19
|
import { WU_PATHS } from './wu-paths.js';
|
|
20
|
-
import { CONSISTENCY_TYPES, LOG_PREFIX, REMOTES, STRING_LITERALS, toKebab, WU_STATUS, YAML_OPTIONS, } from './wu-constants.js';
|
|
20
|
+
import { CONSISTENCY_TYPES, CONSISTENCY_MESSAGES, LOG_PREFIX, REMOTES, STRING_LITERALS, toKebab, WU_STATUS, YAML_OPTIONS, } from './wu-constants.js';
|
|
21
21
|
import { todayISO } from './date-utils.js';
|
|
22
22
|
import { createGitForPath } from './git-adapter.js';
|
|
23
23
|
/**
|
|
@@ -41,10 +41,11 @@ export async function checkWUConsistency(id, projectRoot = process.cwd()) {
|
|
|
41
41
|
return { valid: true, errors: [], stats: { wuExists: false } };
|
|
42
42
|
}
|
|
43
43
|
const wuContent = await readFile(wuPath, { encoding: 'utf-8' });
|
|
44
|
-
const wuDoc =
|
|
44
|
+
const wuDoc = parseYAML(wuContent);
|
|
45
45
|
const yamlStatus = wuDoc?.status || 'unknown';
|
|
46
46
|
const lane = wuDoc?.lane || '';
|
|
47
47
|
const title = wuDoc?.title || '';
|
|
48
|
+
const worktreePathFromYaml = wuDoc?.worktree_path || '';
|
|
48
49
|
// Check stamp existence
|
|
49
50
|
let hasStamp = false;
|
|
50
51
|
try {
|
|
@@ -74,6 +75,7 @@ export async function checkWUConsistency(id, projectRoot = process.cwd()) {
|
|
|
74
75
|
const { inProgress: statusInProgress } = parseStatusSections(statusContent, id);
|
|
75
76
|
// Check for worktree
|
|
76
77
|
const hasWorktree = await checkWorktreeExists(id, projectRoot);
|
|
78
|
+
const worktreePathExists = await checkWorktreePathExists(worktreePathFromYaml);
|
|
77
79
|
// Detection logic
|
|
78
80
|
// 1. YAML done but in status.md In Progress
|
|
79
81
|
if (yamlStatus === WU_STATUS.DONE && statusInProgress) {
|
|
@@ -129,6 +131,19 @@ export async function checkWUConsistency(id, projectRoot = process.cwd()) {
|
|
|
129
131
|
canAutoRepair: true,
|
|
130
132
|
});
|
|
131
133
|
}
|
|
134
|
+
// 6. Claimed WU missing worktree directory
|
|
135
|
+
if (worktreePathFromYaml &&
|
|
136
|
+
!worktreePathExists &&
|
|
137
|
+
(yamlStatus === WU_STATUS.IN_PROGRESS || yamlStatus === WU_STATUS.BLOCKED)) {
|
|
138
|
+
errors.push({
|
|
139
|
+
type: CONSISTENCY_TYPES.MISSING_WORKTREE_CLAIMED,
|
|
140
|
+
wuId: id,
|
|
141
|
+
title,
|
|
142
|
+
description: CONSISTENCY_MESSAGES.MISSING_WORKTREE_CLAIMED(id, yamlStatus, worktreePathFromYaml),
|
|
143
|
+
repairAction: CONSISTENCY_MESSAGES.MISSING_WORKTREE_CLAIMED_REPAIR,
|
|
144
|
+
canAutoRepair: false,
|
|
145
|
+
});
|
|
146
|
+
}
|
|
132
147
|
return {
|
|
133
148
|
valid: errors.length === 0,
|
|
134
149
|
errors,
|
|
@@ -139,6 +154,7 @@ export async function checkWUConsistency(id, projectRoot = process.cwd()) {
|
|
|
139
154
|
backlogInProgress,
|
|
140
155
|
statusInProgress,
|
|
141
156
|
hasWorktree,
|
|
157
|
+
worktreePathExists,
|
|
142
158
|
},
|
|
143
159
|
};
|
|
144
160
|
}
|
|
@@ -202,7 +218,7 @@ export async function checkLaneForOrphanDoneWU(lane, excludeId, projectRoot = pr
|
|
|
202
218
|
}
|
|
203
219
|
let wuDoc;
|
|
204
220
|
try {
|
|
205
|
-
wuDoc =
|
|
221
|
+
wuDoc = parseYAML(wuContent);
|
|
206
222
|
}
|
|
207
223
|
catch {
|
|
208
224
|
// Skip malformed YAML files - they're a separate issue
|
|
@@ -341,7 +357,7 @@ async function updateYamlToDone(id, projectRoot) {
|
|
|
341
357
|
const wuPath = path.join(projectRoot, WU_PATHS.WU(id));
|
|
342
358
|
// Read current YAML
|
|
343
359
|
const content = await readFile(wuPath, { encoding: 'utf-8' });
|
|
344
|
-
const wuDoc =
|
|
360
|
+
const wuDoc = parseYAML(content);
|
|
345
361
|
if (!wuDoc) {
|
|
346
362
|
throw new Error(`Failed to parse WU YAML: ${wuPath}`);
|
|
347
363
|
}
|
|
@@ -353,7 +369,7 @@ async function updateYamlToDone(id, projectRoot) {
|
|
|
353
369
|
wuDoc.completed = todayISO();
|
|
354
370
|
}
|
|
355
371
|
// Write updated YAML
|
|
356
|
-
const updatedContent =
|
|
372
|
+
const updatedContent = stringifyYAML(wuDoc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
|
|
357
373
|
await writeFile(wuPath, updatedContent, { encoding: 'utf-8' });
|
|
358
374
|
}
|
|
359
375
|
/**
|
|
@@ -565,3 +581,21 @@ async function checkWorktreeExists(id, projectRoot) {
|
|
|
565
581
|
return false;
|
|
566
582
|
}
|
|
567
583
|
}
|
|
584
|
+
/**
|
|
585
|
+
* Check whether a worktree path exists on disk
|
|
586
|
+
*
|
|
587
|
+
* @param {string} worktreePath - Worktree path from WU YAML
|
|
588
|
+
* @returns {Promise<boolean>} True if path exists
|
|
589
|
+
*/
|
|
590
|
+
async function checkWorktreePathExists(worktreePath) {
|
|
591
|
+
if (!worktreePath) {
|
|
592
|
+
return false;
|
|
593
|
+
}
|
|
594
|
+
try {
|
|
595
|
+
await access(worktreePath, constants.R_OK);
|
|
596
|
+
return true;
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
return false;
|
|
600
|
+
}
|
|
601
|
+
}
|
package/dist/wu-constants.d.ts
CHANGED
|
@@ -40,6 +40,10 @@ export declare const GIT_REFS: {
|
|
|
40
40
|
ORIGIN_MAIN: string;
|
|
41
41
|
/** Current HEAD ref */
|
|
42
42
|
HEAD: string;
|
|
43
|
+
/** Upstream ref */
|
|
44
|
+
UPSTREAM: string;
|
|
45
|
+
/** Range of upstream..HEAD */
|
|
46
|
+
UPSTREAM_RANGE: string;
|
|
43
47
|
/** Fetch head ref */
|
|
44
48
|
FETCH_HEAD: string;
|
|
45
49
|
};
|
|
@@ -258,6 +262,22 @@ export declare const CONSISTENCY_TYPES: {
|
|
|
258
262
|
ORPHAN_WORKTREE_DONE: string;
|
|
259
263
|
/** Stamp file exists but WU YAML status is not 'done' (partial wu:done failure) */
|
|
260
264
|
STAMP_EXISTS_YAML_NOT_DONE: string;
|
|
265
|
+
/** WU is claimed but its worktree directory is missing */
|
|
266
|
+
MISSING_WORKTREE_CLAIMED: string;
|
|
267
|
+
};
|
|
268
|
+
/**
|
|
269
|
+
* Consistency check messages
|
|
270
|
+
*/
|
|
271
|
+
export declare const CONSISTENCY_MESSAGES: {
|
|
272
|
+
MISSING_WORKTREE_CLAIMED: (id: any, status: any, worktreePath: any) => string;
|
|
273
|
+
MISSING_WORKTREE_CLAIMED_REPAIR: string;
|
|
274
|
+
};
|
|
275
|
+
/**
|
|
276
|
+
* Worktree warning messages
|
|
277
|
+
*/
|
|
278
|
+
export declare const WORKTREE_WARNINGS: {
|
|
279
|
+
MISSING_TRACKED_HEADER: string;
|
|
280
|
+
MISSING_TRACKED_LINE: (worktreePath: any) => string;
|
|
261
281
|
};
|
|
262
282
|
/**
|
|
263
283
|
* File system constants
|
|
@@ -270,6 +290,23 @@ export declare const FILE_SYSTEM: {
|
|
|
270
290
|
/** UTF-8 encoding (alias for compatibility) */
|
|
271
291
|
UTF8: string;
|
|
272
292
|
};
|
|
293
|
+
/**
|
|
294
|
+
* Build artifact cleanup globs
|
|
295
|
+
*
|
|
296
|
+
* Centralized glob patterns for worktree artifact cleanup.
|
|
297
|
+
*/
|
|
298
|
+
export declare const BUILD_ARTIFACT_GLOBS: {
|
|
299
|
+
/** Common dist directories inside worktrees */
|
|
300
|
+
DIST_DIRS: string[];
|
|
301
|
+
/** TypeScript build info files */
|
|
302
|
+
TSBUILDINFO_FILES: string[];
|
|
303
|
+
};
|
|
304
|
+
/**
|
|
305
|
+
* Build artifact cleanup ignore patterns
|
|
306
|
+
*
|
|
307
|
+
* Centralized ignore globs for artifact cleanup.
|
|
308
|
+
*/
|
|
309
|
+
export declare const BUILD_ARTIFACT_IGNORES: string[];
|
|
273
310
|
/**
|
|
274
311
|
* Process stdio constants
|
|
275
312
|
*
|
|
@@ -352,12 +389,12 @@ export declare const DEFAULTS: {
|
|
|
352
389
|
* YAML serialization options
|
|
353
390
|
*
|
|
354
391
|
* Centralized from duplicated { lineWidth: 100 } across wu-* scripts (WU-1256).
|
|
355
|
-
* Use with
|
|
392
|
+
* Use with yaml stringify() options.
|
|
356
393
|
*/
|
|
357
394
|
export declare const YAML_OPTIONS: {
|
|
358
395
|
/** Standard line width for YAML dump (100 chars) */
|
|
359
396
|
LINE_WIDTH: number;
|
|
360
|
-
/** No line wrapping (-1 disables wrapping
|
|
397
|
+
/** No line wrapping (-1 disables wrapping) */
|
|
361
398
|
NO_WRAP: number;
|
|
362
399
|
};
|
|
363
400
|
/**
|
|
@@ -387,6 +424,59 @@ export declare const BOX: {
|
|
|
387
424
|
/** Side border for content lines */
|
|
388
425
|
SIDE: string;
|
|
389
426
|
};
|
|
427
|
+
/**
|
|
428
|
+
* Cleanup guard constants
|
|
429
|
+
*/
|
|
430
|
+
export declare const CLEANUP_GUARD: {
|
|
431
|
+
REASONS: {
|
|
432
|
+
UNCOMMITTED_CHANGES: string;
|
|
433
|
+
UNPUSHED_COMMITS: string;
|
|
434
|
+
STATUS_NOT_DONE: string;
|
|
435
|
+
MISSING_STAMP: string;
|
|
436
|
+
PR_NOT_MERGED: string;
|
|
437
|
+
};
|
|
438
|
+
TITLES: {
|
|
439
|
+
BLOCKED: string;
|
|
440
|
+
NEXT_STEPS: string;
|
|
441
|
+
};
|
|
442
|
+
MESSAGES: {
|
|
443
|
+
UNCOMMITTED_CHANGES: string;
|
|
444
|
+
UNPUSHED_COMMITS: string;
|
|
445
|
+
STATUS_NOT_DONE: string;
|
|
446
|
+
MISSING_STAMP: string;
|
|
447
|
+
PR_NOT_MERGED: string;
|
|
448
|
+
};
|
|
449
|
+
NEXT_STEPS: {
|
|
450
|
+
DEFAULT: {
|
|
451
|
+
text: string;
|
|
452
|
+
appendId: boolean;
|
|
453
|
+
}[];
|
|
454
|
+
UNCOMMITTED_CHANGES: {
|
|
455
|
+
text: string;
|
|
456
|
+
appendId: boolean;
|
|
457
|
+
}[];
|
|
458
|
+
UNPUSHED_COMMITS: {
|
|
459
|
+
text: string;
|
|
460
|
+
appendId: boolean;
|
|
461
|
+
}[];
|
|
462
|
+
STATUS_NOT_DONE: {
|
|
463
|
+
text: string;
|
|
464
|
+
appendId: boolean;
|
|
465
|
+
}[];
|
|
466
|
+
MISSING_STAMP: {
|
|
467
|
+
text: string;
|
|
468
|
+
appendId: boolean;
|
|
469
|
+
}[];
|
|
470
|
+
PR_NOT_MERGED: {
|
|
471
|
+
text: string;
|
|
472
|
+
appendId: boolean;
|
|
473
|
+
}[];
|
|
474
|
+
};
|
|
475
|
+
PR_CHECK: {
|
|
476
|
+
START: string;
|
|
477
|
+
RESULT: string;
|
|
478
|
+
};
|
|
479
|
+
};
|
|
390
480
|
/**
|
|
391
481
|
* Git display constants
|
|
392
482
|
*
|
|
@@ -454,6 +544,8 @@ export declare const GIT_FLAGS: {
|
|
|
454
544
|
NO_VERIFY: string;
|
|
455
545
|
/** No GPG sign flag (skip commit signing) */
|
|
456
546
|
NO_GPG_SIGN: string;
|
|
547
|
+
/** One-line log format */
|
|
548
|
+
ONELINE: string;
|
|
457
549
|
};
|
|
458
550
|
/**
|
|
459
551
|
* Git commands
|
|
@@ -470,6 +562,8 @@ export declare const GIT_COMMANDS: {
|
|
|
470
562
|
LS_TREE: string;
|
|
471
563
|
/** Git diff command */
|
|
472
564
|
DIFF: string;
|
|
565
|
+
/** Git log command */
|
|
566
|
+
LOG: string;
|
|
473
567
|
/** Git merge-base command */
|
|
474
568
|
MERGE_BASE: string;
|
|
475
569
|
/** Git rev-parse command */
|
|
@@ -918,7 +1012,6 @@ export declare const ESLINT_DEFAULTS: {
|
|
|
918
1012
|
*
|
|
919
1013
|
* WU-1866: Temporarily increased from 0 to 100 to unblock gates.
|
|
920
1014
|
* There are ~82 pre-existing warnings that need proper fixes.
|
|
921
|
-
* TODO: Create follow-up WU to fix warnings and restore zero-warnings policy.
|
|
922
1015
|
*/
|
|
923
1016
|
MAX_WARNINGS: string;
|
|
924
1017
|
};
|
|
@@ -997,6 +1090,8 @@ export declare const GITLEAKS_ARGS: {
|
|
|
997
1090
|
export declare const PRETTIER_ARGS: {
|
|
998
1091
|
/** Check formatting without writing */
|
|
999
1092
|
CHECK: string;
|
|
1093
|
+
/** List files with formatting differences */
|
|
1094
|
+
LIST_DIFFERENT: string;
|
|
1000
1095
|
};
|
|
1001
1096
|
/**
|
|
1002
1097
|
* Audit command arguments
|