@lumenflow/core 1.0.0 → 1.3.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.
- package/dist/arg-parser.js +31 -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 +16 -0
- package/dist/git-adapter.js +23 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +2 -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/orchestration-rules.d.ts +1 -1
- package/dist/orchestration-rules.js +2 -2
- 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/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.js +5 -5
- package/dist/wu-constants.d.ts +21 -3
- package/dist/wu-constants.js +28 -3
- 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-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 +12 -30
- package/dist/wu-schema.js +1 -3
- 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 +99 -176
- 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 +11 -8
|
@@ -15,11 +15,12 @@
|
|
|
15
15
|
* @see {@link tools/lib/wu-recovery.mjs} - Related zombie state handling
|
|
16
16
|
*/
|
|
17
17
|
import { readFile, writeFile, unlink, access } from 'node:fs/promises';
|
|
18
|
-
import { existsSync } from 'node:fs';
|
|
19
|
-
import { join } from 'node:path';
|
|
20
|
-
import
|
|
18
|
+
import { existsSync, rmSync } from 'node:fs';
|
|
19
|
+
import { join, resolve } from 'node:path';
|
|
20
|
+
import fg from 'fast-glob';
|
|
21
|
+
import { parseYAML, stringifyYAML } from './wu-yaml.js';
|
|
21
22
|
import { WU_PATHS } from './wu-paths.js';
|
|
22
|
-
import { WU_STATUS, LOG_PREFIX, EMOJI, YAML_OPTIONS, BACKLOG_SECTIONS, STATUS_SECTIONS, REMOTES, BRANCHES, } from './wu-constants.js';
|
|
23
|
+
import { WU_STATUS, LOG_PREFIX, EMOJI, YAML_OPTIONS, BACKLOG_SECTIONS, STATUS_SECTIONS, REMOTES, BRANCHES, BUILD_ARTIFACT_GLOBS, BUILD_ARTIFACT_IGNORES, } from './wu-constants.js';
|
|
23
24
|
import { findSectionBounds, removeBulletFromSection } from './backlog-editor.js';
|
|
24
25
|
/** @constant {string} FRONTMATTER_DELIMITER - YAML frontmatter delimiter */
|
|
25
26
|
const FRONTMATTER_DELIMITER = '---';
|
|
@@ -68,7 +69,7 @@ async function yamlIsDoneOnMain(gitAdapter, wuId) {
|
|
|
68
69
|
'show',
|
|
69
70
|
`${REMOTES.ORIGIN}/${BRANCHES.MAIN}:${WU_PATHS.WU(wuId)}`,
|
|
70
71
|
]);
|
|
71
|
-
const doc =
|
|
72
|
+
const doc = parseYAML(content);
|
|
72
73
|
return doc && doc.status === WU_STATUS.DONE;
|
|
73
74
|
}
|
|
74
75
|
catch {
|
|
@@ -162,7 +163,7 @@ export async function detectRebasedArtifacts(worktreePath, wuId, gitAdapter) {
|
|
|
162
163
|
if (await fileExists(wuYamlPath)) {
|
|
163
164
|
try {
|
|
164
165
|
const content = await readFile(wuYamlPath, { encoding: 'utf-8' });
|
|
165
|
-
const doc =
|
|
166
|
+
const doc = parseYAML(content);
|
|
166
167
|
if (doc && doc.status === WU_STATUS.DONE) {
|
|
167
168
|
localYamlDone = true;
|
|
168
169
|
}
|
|
@@ -236,7 +237,7 @@ export async function cleanupRebasedArtifacts(worktreePath, wuId) {
|
|
|
236
237
|
try {
|
|
237
238
|
if (await fileExists(wuYamlPath)) {
|
|
238
239
|
const content = await readFile(wuYamlPath, { encoding: 'utf-8' });
|
|
239
|
-
const doc =
|
|
240
|
+
const doc = parseYAML(content);
|
|
240
241
|
if (doc && doc.status === WU_STATUS.DONE) {
|
|
241
242
|
// Reset status
|
|
242
243
|
doc.status = WU_STATUS.IN_PROGRESS;
|
|
@@ -244,7 +245,7 @@ export async function cleanupRebasedArtifacts(worktreePath, wuId) {
|
|
|
244
245
|
delete doc.locked;
|
|
245
246
|
delete doc.completed_at;
|
|
246
247
|
// Write back
|
|
247
|
-
const updatedContent =
|
|
248
|
+
const updatedContent = stringifyYAML(doc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
|
|
248
249
|
await writeFile(wuYamlPath, updatedContent, { encoding: 'utf-8' });
|
|
249
250
|
yamlReset = true;
|
|
250
251
|
console.log(LOG_PREFIX.CLEANUP, `${EMOJI.WARNING} Reset YAML status from done to in_progress for ${wuId} (artifact from main rebase)`);
|
|
@@ -431,3 +432,43 @@ export async function deduplicateBacklogAfterRebase(worktreePath, wuId) {
|
|
|
431
432
|
errors,
|
|
432
433
|
};
|
|
433
434
|
}
|
|
435
|
+
/**
|
|
436
|
+
* Clean up build artifacts in a worktree (dist folders + tsbuildinfo files)
|
|
437
|
+
*
|
|
438
|
+
* WU-1042: Provide a safe helper for clearing build artifacts that can
|
|
439
|
+
* trigger TS5055 or stale build issues in worktrees.
|
|
440
|
+
*
|
|
441
|
+
* @param {string} worktreePath - Path to the worktree directory
|
|
442
|
+
* @returns {Promise<object>} Cleanup result
|
|
443
|
+
* @returns {string[]} result.distDirectories - Removed dist directories (relative paths)
|
|
444
|
+
* @returns {string[]} result.tsbuildinfoFiles - Removed tsbuildinfo files (relative paths)
|
|
445
|
+
* @returns {number} result.removedCount - Total removed artifacts
|
|
446
|
+
*/
|
|
447
|
+
export async function cleanupWorktreeBuildArtifacts(worktreePath) {
|
|
448
|
+
const root = resolve(worktreePath);
|
|
449
|
+
const distDirectories = await fg(BUILD_ARTIFACT_GLOBS.DIST_DIRS, {
|
|
450
|
+
cwd: root,
|
|
451
|
+
onlyDirectories: true,
|
|
452
|
+
dot: true,
|
|
453
|
+
followSymbolicLinks: false,
|
|
454
|
+
ignore: BUILD_ARTIFACT_IGNORES,
|
|
455
|
+
});
|
|
456
|
+
const tsbuildinfoFiles = await fg(BUILD_ARTIFACT_GLOBS.TSBUILDINFO_FILES, {
|
|
457
|
+
cwd: root,
|
|
458
|
+
onlyFiles: true,
|
|
459
|
+
dot: true,
|
|
460
|
+
followSymbolicLinks: false,
|
|
461
|
+
ignore: BUILD_ARTIFACT_IGNORES,
|
|
462
|
+
});
|
|
463
|
+
for (const dir of distDirectories) {
|
|
464
|
+
rmSync(join(root, dir), { recursive: true, force: true });
|
|
465
|
+
}
|
|
466
|
+
for (const file of tsbuildinfoFiles) {
|
|
467
|
+
rmSync(join(root, file), { force: true });
|
|
468
|
+
}
|
|
469
|
+
return {
|
|
470
|
+
distDirectories,
|
|
471
|
+
tsbuildinfoFiles,
|
|
472
|
+
removedCount: distDirectories.length + tsbuildinfoFiles.length,
|
|
473
|
+
};
|
|
474
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strategy interface for client-specific spawn behavior
|
|
3
|
+
*/
|
|
4
|
+
export interface SpawnStrategy {
|
|
5
|
+
/**
|
|
6
|
+
* Get the context loading preamble for the specific client
|
|
7
|
+
*/
|
|
8
|
+
getPreamble(wuId: string): string;
|
|
9
|
+
/**
|
|
10
|
+
* Get instructions for loading agent skills/tools
|
|
11
|
+
*/
|
|
12
|
+
getSkillLoadingInstruction(): string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Base class with shared preamble logic
|
|
16
|
+
*/
|
|
17
|
+
declare abstract class BaseSpawnStrategy implements SpawnStrategy {
|
|
18
|
+
protected getCorePreamble(wuId: string): string;
|
|
19
|
+
abstract getPreamble(wuId: string): string;
|
|
20
|
+
abstract getSkillLoadingInstruction(): string;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Strategy for Claude Code (Local/Terminal)
|
|
24
|
+
*/
|
|
25
|
+
export declare class ClaudeCodeStrategy extends BaseSpawnStrategy {
|
|
26
|
+
getPreamble(wuId: string): string;
|
|
27
|
+
getSkillLoadingInstruction(): string;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Strategy for Gemini CLI (Multimodal/Ecosystem)
|
|
31
|
+
*/
|
|
32
|
+
export declare class GeminiCliStrategy extends BaseSpawnStrategy {
|
|
33
|
+
getPreamble(wuId: string): string;
|
|
34
|
+
getSkillLoadingInstruction(): string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Generic Strategy (Unknown/Other clients)
|
|
38
|
+
*/
|
|
39
|
+
export declare class GenericStrategy extends BaseSpawnStrategy {
|
|
40
|
+
getPreamble(wuId: string): string;
|
|
41
|
+
getSkillLoadingInstruction(): string;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Factory for creating strategies
|
|
45
|
+
*/
|
|
46
|
+
export declare class SpawnStrategyFactory {
|
|
47
|
+
/**
|
|
48
|
+
* Create a strategy for the given client
|
|
49
|
+
* @param clientName - Client name (e.g. 'claude-code', 'gemini-cli')
|
|
50
|
+
*/
|
|
51
|
+
static create(clientName: string): SpawnStrategy;
|
|
52
|
+
}
|
|
53
|
+
export {};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
/**
|
|
3
|
+
* Base class with shared preamble logic
|
|
4
|
+
*/
|
|
5
|
+
class BaseSpawnStrategy {
|
|
6
|
+
getCorePreamble(wuId) {
|
|
7
|
+
return `Load the following context in this order:
|
|
8
|
+
|
|
9
|
+
1. Read LUMENFLOW.md (workflow fundamentals and critical rules)
|
|
10
|
+
2. Read .lumenflow/constraints.md (non-negotiable constraints)
|
|
11
|
+
3. Read README.md (project structure and tech stack)
|
|
12
|
+
4. Read docs/04-operations/_frameworks/lumenflow/lumenflow-complete.md sections 1-7 (TDD, gates, Definition of Done)
|
|
13
|
+
5. Read docs/04-operations/tasks/wu/${wuId}.yaml (the specific WU you're working on)`;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Strategy for Claude Code (Local/Terminal)
|
|
18
|
+
*/
|
|
19
|
+
export class ClaudeCodeStrategy extends BaseSpawnStrategy {
|
|
20
|
+
getPreamble(wuId) {
|
|
21
|
+
let preamble = this.getCorePreamble(wuId);
|
|
22
|
+
// Vendor overlay
|
|
23
|
+
if (existsSync('.claude/CLAUDE.md')) {
|
|
24
|
+
// Insert after LUMENFLOW.md if possible, or just append/prepend
|
|
25
|
+
// For simplicity and clarity, we'll prepend the vendor specific instructions
|
|
26
|
+
// relying on the user to follow the specific order if stated.
|
|
27
|
+
// Actually, checking original behavior: CLAUDE.md was #1.
|
|
28
|
+
// But new plan says LUMENFLOW.md is core.
|
|
29
|
+
// We will append it as an overlay step.
|
|
30
|
+
preamble += `\n6. Read .claude/CLAUDE.md (Claude-specific workflow overlay)`;
|
|
31
|
+
}
|
|
32
|
+
return preamble;
|
|
33
|
+
}
|
|
34
|
+
getSkillLoadingInstruction() {
|
|
35
|
+
return `## Skills Selection
|
|
36
|
+
|
|
37
|
+
1. Check \`.lumenflow/agents\` for available skills.
|
|
38
|
+
2. Check \`.claude/agents\` for Claude-specific overrides or additions.
|
|
39
|
+
3. Select relevant skills for this task.`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Strategy for Gemini CLI (Multimodal/Ecosystem)
|
|
44
|
+
*/
|
|
45
|
+
export class GeminiCliStrategy extends BaseSpawnStrategy {
|
|
46
|
+
getPreamble(wuId) {
|
|
47
|
+
let preamble = this.getCorePreamble(wuId);
|
|
48
|
+
if (existsSync('GEMINI.md')) {
|
|
49
|
+
preamble += `\n6. Read GEMINI.md (Gemini-specific workflow overlay)`;
|
|
50
|
+
}
|
|
51
|
+
return preamble;
|
|
52
|
+
}
|
|
53
|
+
getSkillLoadingInstruction() {
|
|
54
|
+
return `## Skills Selection
|
|
55
|
+
|
|
56
|
+
1. Check \`.lumenflow/agents\` for available skills.
|
|
57
|
+
2. Select relevant skills for this task.`;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Generic Strategy (Unknown/Other clients)
|
|
62
|
+
*/
|
|
63
|
+
export class GenericStrategy extends BaseSpawnStrategy {
|
|
64
|
+
getPreamble(wuId) {
|
|
65
|
+
return this.getCorePreamble(wuId);
|
|
66
|
+
}
|
|
67
|
+
getSkillLoadingInstruction() {
|
|
68
|
+
return `## Skills Selection
|
|
69
|
+
|
|
70
|
+
1. Check \`.lumenflow/agents\` for available skills.
|
|
71
|
+
2. Select relevant skills for this task.`;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Factory for creating strategies
|
|
76
|
+
*/
|
|
77
|
+
export class SpawnStrategyFactory {
|
|
78
|
+
/**
|
|
79
|
+
* Create a strategy for the given client
|
|
80
|
+
* @param clientName - Client name (e.g. 'claude-code', 'gemini-cli')
|
|
81
|
+
*/
|
|
82
|
+
static create(clientName) {
|
|
83
|
+
switch (clientName.toLowerCase()) {
|
|
84
|
+
case 'claude': // Legacy alias
|
|
85
|
+
case 'claude-code':
|
|
86
|
+
return new ClaudeCodeStrategy();
|
|
87
|
+
case 'gemini': // Alias
|
|
88
|
+
case 'gemini-cli':
|
|
89
|
+
return new GeminiCliStrategy();
|
|
90
|
+
case 'codex': // Deprecated alias
|
|
91
|
+
case 'codex-cli':
|
|
92
|
+
// Codex might need its own strategy later (sandbox), but for now generic or claude-like?
|
|
93
|
+
// Plan says: "codex: preamble: false, strategy: cloud-sandbox" -> implies Generic or dedicated.
|
|
94
|
+
// For now, let's map to Generic but maybe we should add CodexStrategy if it has diff behavior.
|
|
95
|
+
// Re-reading plan: "CodexStrategy: Emphasizes cloud sandbox constraints"
|
|
96
|
+
// But for this "Minimal" pass, let's stick to Generic with a comment,
|
|
97
|
+
// OR essentially treat it as Generic since we don't have constraints logic here yet (it's in wu-spawn).
|
|
98
|
+
// Actually, let's return GenericStrategy but we might handle constraints elsewhere.
|
|
99
|
+
return new GenericStrategy();
|
|
100
|
+
default:
|
|
101
|
+
// Warn? The factory just creates. The caller should warn if it fell back.
|
|
102
|
+
// But here we just return Generic.
|
|
103
|
+
return new GenericStrategy();
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
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') {
|
|
@@ -15,7 +15,7 @@
|
|
|
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
20
|
import { CONSISTENCY_TYPES, LOG_PREFIX, REMOTES, STRING_LITERALS, toKebab, WU_STATUS, YAML_OPTIONS, } from './wu-constants.js';
|
|
21
21
|
import { todayISO } from './date-utils.js';
|
|
@@ -41,7 +41,7 @@ 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 || '';
|
|
@@ -202,7 +202,7 @@ export async function checkLaneForOrphanDoneWU(lane, excludeId, projectRoot = pr
|
|
|
202
202
|
}
|
|
203
203
|
let wuDoc;
|
|
204
204
|
try {
|
|
205
|
-
wuDoc =
|
|
205
|
+
wuDoc = parseYAML(wuContent);
|
|
206
206
|
}
|
|
207
207
|
catch {
|
|
208
208
|
// Skip malformed YAML files - they're a separate issue
|
|
@@ -341,7 +341,7 @@ async function updateYamlToDone(id, projectRoot) {
|
|
|
341
341
|
const wuPath = path.join(projectRoot, WU_PATHS.WU(id));
|
|
342
342
|
// Read current YAML
|
|
343
343
|
const content = await readFile(wuPath, { encoding: 'utf-8' });
|
|
344
|
-
const wuDoc =
|
|
344
|
+
const wuDoc = parseYAML(content);
|
|
345
345
|
if (!wuDoc) {
|
|
346
346
|
throw new Error(`Failed to parse WU YAML: ${wuPath}`);
|
|
347
347
|
}
|
|
@@ -353,7 +353,7 @@ async function updateYamlToDone(id, projectRoot) {
|
|
|
353
353
|
wuDoc.completed = todayISO();
|
|
354
354
|
}
|
|
355
355
|
// Write updated YAML
|
|
356
|
-
const updatedContent =
|
|
356
|
+
const updatedContent = stringifyYAML(wuDoc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
|
|
357
357
|
await writeFile(wuPath, updatedContent, { encoding: 'utf-8' });
|
|
358
358
|
}
|
|
359
359
|
/**
|
package/dist/wu-constants.d.ts
CHANGED
|
@@ -270,6 +270,23 @@ export declare const FILE_SYSTEM: {
|
|
|
270
270
|
/** UTF-8 encoding (alias for compatibility) */
|
|
271
271
|
UTF8: string;
|
|
272
272
|
};
|
|
273
|
+
/**
|
|
274
|
+
* Build artifact cleanup globs
|
|
275
|
+
*
|
|
276
|
+
* Centralized glob patterns for worktree artifact cleanup.
|
|
277
|
+
*/
|
|
278
|
+
export declare const BUILD_ARTIFACT_GLOBS: {
|
|
279
|
+
/** Common dist directories inside worktrees */
|
|
280
|
+
DIST_DIRS: string[];
|
|
281
|
+
/** TypeScript build info files */
|
|
282
|
+
TSBUILDINFO_FILES: string[];
|
|
283
|
+
};
|
|
284
|
+
/**
|
|
285
|
+
* Build artifact cleanup ignore patterns
|
|
286
|
+
*
|
|
287
|
+
* Centralized ignore globs for artifact cleanup.
|
|
288
|
+
*/
|
|
289
|
+
export declare const BUILD_ARTIFACT_IGNORES: string[];
|
|
273
290
|
/**
|
|
274
291
|
* Process stdio constants
|
|
275
292
|
*
|
|
@@ -352,12 +369,12 @@ export declare const DEFAULTS: {
|
|
|
352
369
|
* YAML serialization options
|
|
353
370
|
*
|
|
354
371
|
* Centralized from duplicated { lineWidth: 100 } across wu-* scripts (WU-1256).
|
|
355
|
-
* Use with
|
|
372
|
+
* Use with yaml stringify() options.
|
|
356
373
|
*/
|
|
357
374
|
export declare const YAML_OPTIONS: {
|
|
358
375
|
/** Standard line width for YAML dump (100 chars) */
|
|
359
376
|
LINE_WIDTH: number;
|
|
360
|
-
/** No line wrapping (-1 disables wrapping
|
|
377
|
+
/** No line wrapping (-1 disables wrapping) */
|
|
361
378
|
NO_WRAP: number;
|
|
362
379
|
};
|
|
363
380
|
/**
|
|
@@ -918,7 +935,6 @@ export declare const ESLINT_DEFAULTS: {
|
|
|
918
935
|
*
|
|
919
936
|
* WU-1866: Temporarily increased from 0 to 100 to unblock gates.
|
|
920
937
|
* 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
938
|
*/
|
|
923
939
|
MAX_WARNINGS: string;
|
|
924
940
|
};
|
|
@@ -997,6 +1013,8 @@ export declare const GITLEAKS_ARGS: {
|
|
|
997
1013
|
export declare const PRETTIER_ARGS: {
|
|
998
1014
|
/** Check formatting without writing */
|
|
999
1015
|
CHECK: string;
|
|
1016
|
+
/** List files with formatting differences */
|
|
1017
|
+
LIST_DIFFERENT: string;
|
|
1000
1018
|
};
|
|
1001
1019
|
/**
|
|
1002
1020
|
* Audit command arguments
|
package/dist/wu-constants.js
CHANGED
|
@@ -285,6 +285,30 @@ export const FILE_SYSTEM = {
|
|
|
285
285
|
/** UTF-8 encoding (alias for compatibility) */
|
|
286
286
|
UTF8: 'utf8',
|
|
287
287
|
};
|
|
288
|
+
/**
|
|
289
|
+
* Build artifact cleanup globs
|
|
290
|
+
*
|
|
291
|
+
* Centralized glob patterns for worktree artifact cleanup.
|
|
292
|
+
*/
|
|
293
|
+
export const BUILD_ARTIFACT_GLOBS = {
|
|
294
|
+
/** Common dist directories inside worktrees */
|
|
295
|
+
DIST_DIRS: [
|
|
296
|
+
'packages/*/dist',
|
|
297
|
+
'packages/**/dist',
|
|
298
|
+
'apps/*/dist',
|
|
299
|
+
'apps/**/dist',
|
|
300
|
+
'tools/*/dist',
|
|
301
|
+
'tools/**/dist',
|
|
302
|
+
],
|
|
303
|
+
/** TypeScript build info files */
|
|
304
|
+
TSBUILDINFO_FILES: ['**/*.tsbuildinfo'],
|
|
305
|
+
};
|
|
306
|
+
/**
|
|
307
|
+
* Build artifact cleanup ignore patterns
|
|
308
|
+
*
|
|
309
|
+
* Centralized ignore globs for artifact cleanup.
|
|
310
|
+
*/
|
|
311
|
+
export const BUILD_ARTIFACT_IGNORES = ['**/node_modules/**', '**/.git/**', '**/.turbo/**'];
|
|
288
312
|
/**
|
|
289
313
|
* Process stdio constants
|
|
290
314
|
*
|
|
@@ -367,12 +391,12 @@ export const DEFAULTS = {
|
|
|
367
391
|
* YAML serialization options
|
|
368
392
|
*
|
|
369
393
|
* Centralized from duplicated { lineWidth: 100 } across wu-* scripts (WU-1256).
|
|
370
|
-
* Use with
|
|
394
|
+
* Use with yaml stringify() options.
|
|
371
395
|
*/
|
|
372
396
|
export const YAML_OPTIONS = {
|
|
373
397
|
/** Standard line width for YAML dump (100 chars) */
|
|
374
398
|
LINE_WIDTH: 100,
|
|
375
|
-
/** No line wrapping (-1 disables wrapping
|
|
399
|
+
/** No line wrapping (-1 disables wrapping) */
|
|
376
400
|
NO_WRAP: -1,
|
|
377
401
|
};
|
|
378
402
|
/**
|
|
@@ -951,7 +975,6 @@ export const ESLINT_DEFAULTS = {
|
|
|
951
975
|
*
|
|
952
976
|
* WU-1866: Temporarily increased from 0 to 100 to unblock gates.
|
|
953
977
|
* There are ~82 pre-existing warnings that need proper fixes.
|
|
954
|
-
* TODO: Create follow-up WU to fix warnings and restore zero-warnings policy.
|
|
955
978
|
*/
|
|
956
979
|
MAX_WARNINGS: '100',
|
|
957
980
|
};
|
|
@@ -1030,6 +1053,8 @@ export const GITLEAKS_ARGS = {
|
|
|
1030
1053
|
export const PRETTIER_ARGS = {
|
|
1031
1054
|
/** Check formatting without writing */
|
|
1032
1055
|
CHECK: '--check',
|
|
1056
|
+
/** List files with formatting differences */
|
|
1057
|
+
LIST_DIFFERENT: '--list-different',
|
|
1033
1058
|
};
|
|
1034
1059
|
/**
|
|
1035
1060
|
* Audit command arguments
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch utilities for wu:done workflows.
|
|
3
|
+
*/
|
|
4
|
+
/**
|
|
5
|
+
* Check if branch is already merged to main
|
|
6
|
+
*
|
|
7
|
+
* @param {string} branch - Lane branch name
|
|
8
|
+
* @returns {Promise<boolean>} Whether branch is already merged
|
|
9
|
+
*/
|
|
10
|
+
export declare function isBranchAlreadyMerged(branch: any): Promise<boolean>;
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Branch utilities for wu:done workflows.
|
|
3
|
+
*/
|
|
4
|
+
import { getGitForCwd } from './git-adapter.js';
|
|
5
|
+
import { BRANCHES, LOG_PREFIX } from './wu-constants.js';
|
|
6
|
+
import { PREFLIGHT } from './wu-done-messages.js';
|
|
7
|
+
/** @constant {number} SHA_SHORT_LENGTH - Length of shortened git SHA hashes for display */
|
|
8
|
+
const SHA_SHORT_LENGTH = 8;
|
|
9
|
+
/**
|
|
10
|
+
* Check if branch is already merged to main
|
|
11
|
+
*
|
|
12
|
+
* @param {string} branch - Lane branch name
|
|
13
|
+
* @returns {Promise<boolean>} Whether branch is already merged
|
|
14
|
+
*/
|
|
15
|
+
export async function isBranchAlreadyMerged(branch) {
|
|
16
|
+
const gitAdapter = getGitForCwd();
|
|
17
|
+
try {
|
|
18
|
+
const branchTip = (await gitAdapter.getCommitHash(branch)).trim();
|
|
19
|
+
const mergeBase = (await gitAdapter.mergeBase(BRANCHES.MAIN, branch)).trim();
|
|
20
|
+
const mainHead = (await gitAdapter.getCommitHash(BRANCHES.MAIN)).trim();
|
|
21
|
+
if (branchTip === mergeBase) {
|
|
22
|
+
console.log(PREFLIGHT.BRANCH_INFO(branch, branchTip.substring(0, SHA_SHORT_LENGTH), mergeBase.substring(0, SHA_SHORT_LENGTH), mainHead.substring(0, SHA_SHORT_LENGTH)));
|
|
23
|
+
return true;
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
catch (e) {
|
|
28
|
+
console.warn(`${LOG_PREFIX.DONE} Warning: Could not check if branch is merged: ${e.message}`);
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
}
|