@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.
Files changed (65) hide show
  1. package/dist/arg-parser.js +31 -1
  2. package/dist/backlog-generator.js +1 -1
  3. package/dist/backlog-sync-validator.js +3 -3
  4. package/dist/branch-check.d.ts +21 -0
  5. package/dist/branch-check.js +77 -0
  6. package/dist/cli/is-agent-branch.d.ts +11 -0
  7. package/dist/cli/is-agent-branch.js +15 -0
  8. package/dist/code-paths-overlap.js +2 -2
  9. package/dist/error-handler.d.ts +1 -0
  10. package/dist/error-handler.js +4 -1
  11. package/dist/git-adapter.d.ts +16 -0
  12. package/dist/git-adapter.js +23 -1
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +2 -0
  15. package/dist/lane-checker.d.ts +36 -3
  16. package/dist/lane-checker.js +128 -17
  17. package/dist/lane-inference.js +3 -4
  18. package/dist/lumenflow-config-schema.d.ts +125 -0
  19. package/dist/lumenflow-config-schema.js +76 -0
  20. package/dist/orchestration-rules.d.ts +1 -1
  21. package/dist/orchestration-rules.js +2 -2
  22. package/dist/path-classifiers.d.ts +1 -1
  23. package/dist/path-classifiers.js +1 -1
  24. package/dist/rebase-artifact-cleanup.d.ts +17 -0
  25. package/dist/rebase-artifact-cleanup.js +49 -8
  26. package/dist/spawn-strategy.d.ts +53 -0
  27. package/dist/spawn-strategy.js +106 -0
  28. package/dist/stamp-utils.d.ts +10 -0
  29. package/dist/stamp-utils.js +17 -19
  30. package/dist/token-counter.js +2 -2
  31. package/dist/wu-consistency-checker.js +5 -5
  32. package/dist/wu-constants.d.ts +21 -3
  33. package/dist/wu-constants.js +28 -3
  34. package/dist/wu-done-branch-utils.d.ts +10 -0
  35. package/dist/wu-done-branch-utils.js +31 -0
  36. package/dist/wu-done-cleanup.d.ts +8 -0
  37. package/dist/wu-done-cleanup.js +122 -0
  38. package/dist/wu-done-docs-only.d.ts +20 -0
  39. package/dist/wu-done-docs-only.js +65 -0
  40. package/dist/wu-done-errors.d.ts +17 -0
  41. package/dist/wu-done-errors.js +24 -0
  42. package/dist/wu-done-inputs.d.ts +12 -0
  43. package/dist/wu-done-inputs.js +51 -0
  44. package/dist/wu-done-metadata.d.ts +100 -0
  45. package/dist/wu-done-metadata.js +193 -0
  46. package/dist/wu-done-paths.d.ts +69 -0
  47. package/dist/wu-done-paths.js +237 -0
  48. package/dist/wu-done-preflight.d.ts +48 -0
  49. package/dist/wu-done-preflight.js +185 -0
  50. package/dist/wu-done-validation.d.ts +82 -0
  51. package/dist/wu-done-validation.js +340 -0
  52. package/dist/wu-done-validators.d.ts +13 -409
  53. package/dist/wu-done-validators.js +9 -1225
  54. package/dist/wu-done-worktree.d.ts +0 -1
  55. package/dist/wu-done-worktree.js +12 -30
  56. package/dist/wu-schema.js +1 -3
  57. package/dist/wu-spawn-skills.d.ts +19 -0
  58. package/dist/wu-spawn-skills.js +148 -0
  59. package/dist/wu-spawn.d.ts +17 -4
  60. package/dist/wu-spawn.js +99 -176
  61. package/dist/wu-validation.d.ts +1 -0
  62. package/dist/wu-validation.js +21 -1
  63. package/dist/wu-validator.d.ts +51 -0
  64. package/dist/wu-validator.js +108 -0
  65. 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 yaml from 'js-yaml';
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 = yaml.load(content);
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 = yaml.load(content);
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 = yaml.load(content);
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 = yaml.dump(doc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
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
+ }
@@ -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
  *
@@ -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 DATE_PATTERN = /^(\d{4})-(\d{2})-(\d{2})$/;
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 isValidDate(dateStr) {
47
- const match = dateStr.match(DATE_PATTERN);
48
- if (!match) {
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
- return true;
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
@@ -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 { load as loadYAML } from 'js-yaml';
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 = loadYAML(raw);
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 yaml from 'js-yaml';
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 = yaml.load(wuContent);
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 = yaml.load(wuContent);
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 = yaml.load(content);
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 = yaml.dump(wuDoc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
356
+ const updatedContent = stringifyYAML(wuDoc, { lineWidth: YAML_OPTIONS.LINE_WIDTH });
357
357
  await writeFile(wuPath, updatedContent, { encoding: 'utf-8' });
358
358
  }
359
359
  /**
@@ -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 js-yaml dump() function.
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 in js-yaml) */
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
@@ -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 js-yaml dump() function.
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 in js-yaml) */
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
+ }
@@ -0,0 +1,8 @@
1
+ /**
2
+ * Cleanup helpers for wu:done.
3
+ */
4
+ /**
5
+ * Run cleanup operations after successful merge
6
+ * Removes worktree and optionally deletes lane branch
7
+ */
8
+ export declare function runCleanup(docMain: any, args: any): Promise<void>;