@lumenflow/cli 2.2.2 → 2.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/README.md +147 -57
- package/dist/__tests__/agent-log-issue.test.js +56 -0
- package/dist/__tests__/cli-entry-point.test.js +66 -17
- package/dist/__tests__/cli-subprocess.test.js +25 -0
- package/dist/__tests__/init.test.js +298 -0
- package/dist/__tests__/initiative-plan.test.js +340 -0
- package/dist/__tests__/mem-cleanup-execution.test.js +19 -0
- package/dist/__tests__/merge-block.test.js +220 -0
- package/dist/__tests__/release.test.js +61 -0
- package/dist/__tests__/safe-git.test.js +191 -0
- package/dist/__tests__/state-doctor.test.js +274 -0
- package/dist/__tests__/wu-done.test.js +36 -0
- package/dist/__tests__/wu-edit.test.js +119 -0
- package/dist/__tests__/wu-prep.test.js +108 -0
- package/dist/agent-issues-query.js +4 -3
- package/dist/agent-log-issue.js +25 -4
- package/dist/backlog-prune.js +5 -4
- package/dist/cli-entry-point.js +11 -1
- package/dist/doctor.js +368 -0
- package/dist/flow-bottlenecks.js +6 -5
- package/dist/flow-report.js +4 -3
- package/dist/gates.js +356 -101
- package/dist/guard-locked.js +4 -3
- package/dist/guard-worktree-commit.js +4 -3
- package/dist/init.js +517 -86
- package/dist/initiative-add-wu.js +4 -3
- package/dist/initiative-bulk-assign-wus.js +8 -5
- package/dist/initiative-create.js +73 -37
- package/dist/initiative-edit.js +37 -21
- package/dist/initiative-list.js +4 -3
- package/dist/initiative-plan.js +337 -0
- package/dist/initiative-status.js +4 -3
- package/dist/lane-health.js +377 -0
- package/dist/lane-suggest.js +382 -0
- package/dist/mem-checkpoint.js +2 -2
- package/dist/mem-cleanup.js +2 -2
- package/dist/mem-context.js +306 -0
- package/dist/mem-create.js +2 -2
- package/dist/mem-delete.js +293 -0
- package/dist/mem-inbox.js +2 -2
- package/dist/mem-index.js +211 -0
- package/dist/mem-init.js +1 -1
- package/dist/mem-profile.js +207 -0
- package/dist/mem-promote.js +254 -0
- package/dist/mem-ready.js +2 -2
- package/dist/mem-signal.js +2 -2
- package/dist/mem-start.js +2 -2
- package/dist/mem-summarize.js +2 -2
- package/dist/mem-triage.js +2 -2
- package/dist/merge-block.js +222 -0
- package/dist/metrics-cli.js +7 -4
- package/dist/metrics-snapshot.js +4 -3
- package/dist/orchestrate-initiative.js +10 -4
- package/dist/orchestrate-monitor.js +379 -31
- package/dist/release.js +69 -29
- package/dist/signal-cleanup.js +296 -0
- package/dist/spawn-list.js +6 -5
- package/dist/state-bootstrap.js +5 -4
- package/dist/state-cleanup.js +360 -0
- package/dist/state-doctor-fix.js +196 -0
- package/dist/state-doctor.js +501 -0
- package/dist/validate-agent-skills.js +4 -3
- package/dist/validate-agent-sync.js +4 -3
- package/dist/validate-backlog-sync.js +4 -3
- package/dist/validate-skills-spec.js +4 -3
- package/dist/validate.js +4 -3
- package/dist/wu-block.js +3 -3
- package/dist/wu-claim.js +208 -98
- package/dist/wu-cleanup.js +5 -4
- package/dist/wu-create.js +71 -46
- package/dist/wu-delete.js +88 -60
- package/dist/wu-deps.js +6 -5
- package/dist/wu-done-check.js +34 -0
- package/dist/wu-done.js +39 -12
- package/dist/wu-edit.js +63 -28
- package/dist/wu-infer-lane.js +7 -6
- package/dist/wu-preflight.js +23 -81
- package/dist/wu-prep.js +125 -0
- package/dist/wu-prune.js +4 -3
- package/dist/wu-recover.js +88 -22
- package/dist/wu-repair.js +7 -6
- package/dist/wu-spawn.js +226 -270
- package/dist/wu-status.js +4 -3
- package/dist/wu-unblock.js +5 -5
- package/dist/wu-unlock-lane.js +4 -3
- package/dist/wu-validate.js +5 -4
- package/package.json +16 -7
- package/templates/core/.lumenflow/constraints.md.template +192 -0
- package/templates/core/.lumenflow/rules/git-safety.md.template +27 -0
- package/templates/core/.lumenflow/rules/wu-workflow.md.template +48 -0
- package/templates/core/AGENTS.md.template +60 -0
- package/templates/core/LUMENFLOW.md.template +255 -0
- package/templates/core/UPGRADING.md.template +121 -0
- package/templates/core/ai/onboarding/agent-safety-card.md.template +106 -0
- package/templates/core/ai/onboarding/first-wu-mistakes.md.template +198 -0
- package/templates/core/ai/onboarding/quick-ref-commands.md.template +186 -0
- package/templates/core/ai/onboarding/release-process.md.template +362 -0
- package/templates/core/ai/onboarding/troubleshooting-wu-done.md.template +159 -0
- package/templates/core/ai/onboarding/wu-create-checklist.md.template +117 -0
- package/templates/vendors/aider/.aider.conf.yml.template +27 -0
- package/templates/vendors/claude/.claude/CLAUDE.md.template +52 -0
- package/templates/vendors/claude/.claude/settings.json.template +49 -0
- package/templates/vendors/claude/.claude/skills/bug-classification/SKILL.md.template +192 -0
- package/templates/vendors/claude/.claude/skills/code-quality/SKILL.md.template +152 -0
- package/templates/vendors/claude/.claude/skills/context-management/SKILL.md.template +155 -0
- package/templates/vendors/claude/.claude/skills/execution-memory/SKILL.md.template +304 -0
- package/templates/vendors/claude/.claude/skills/frontend-design/SKILL.md.template +131 -0
- package/templates/vendors/claude/.claude/skills/initiative-management/SKILL.md.template +164 -0
- package/templates/vendors/claude/.claude/skills/library-first/SKILL.md.template +98 -0
- package/templates/vendors/claude/.claude/skills/lumenflow-gates/SKILL.md.template +87 -0
- package/templates/vendors/claude/.claude/skills/multi-agent-coordination/SKILL.md.template +84 -0
- package/templates/vendors/claude/.claude/skills/ops-maintenance/SKILL.md.template +254 -0
- package/templates/vendors/claude/.claude/skills/orchestration/SKILL.md.template +189 -0
- package/templates/vendors/claude/.claude/skills/tdd-workflow/SKILL.md.template +139 -0
- package/templates/vendors/claude/.claude/skills/worktree-discipline/SKILL.md.template +138 -0
- package/templates/vendors/claude/.claude/skills/wu-lifecycle/SKILL.md.template +106 -0
- package/templates/vendors/cline/.clinerules.template +53 -0
- package/templates/vendors/cursor/.cursor/rules/lumenflow.md.template +34 -0
- package/templates/vendors/cursor/.cursor/rules.md.template +28 -0
- package/templates/vendors/windsurf/.windsurf/rules/lumenflow.md.template +34 -0
package/dist/wu-claim.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
/* eslint-disable no-console -- CLI tool requires console output */
|
|
2
3
|
/**
|
|
3
4
|
* WU Claim Helper
|
|
4
5
|
*
|
|
@@ -8,7 +9,7 @@
|
|
|
8
9
|
* 3) Create a dedicated worktree+branch for the WU
|
|
9
10
|
*
|
|
10
11
|
* Usage:
|
|
11
|
-
* node tools/wu-claim.
|
|
12
|
+
* node tools/wu-claim.ts --id WU-334 --lane Intelligence \
|
|
12
13
|
* [--worktree worktrees/intelligence-wu-334] [--branch lane/intelligence/wu-334]
|
|
13
14
|
*
|
|
14
15
|
* WU-2542: This script imports utilities from @lumenflow/core package.
|
|
@@ -18,21 +19,24 @@ import { existsSync, readFileSync, rmSync } from 'node:fs';
|
|
|
18
19
|
import { access, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
19
20
|
import path from 'node:path';
|
|
20
21
|
import { isOrphanWorktree } from '@lumenflow/core/dist/orphan-detector.js';
|
|
21
|
-
// WU-1352: Use centralized YAML functions from wu-yaml.
|
|
22
|
+
// WU-1352: Use centralized YAML functions from wu-yaml.ts
|
|
22
23
|
import { parseYAML, stringifyYAML } from '@lumenflow/core/dist/wu-yaml.js';
|
|
23
24
|
import { assertTransition } from '@lumenflow/core/dist/state-machine.js';
|
|
24
|
-
import { checkLaneFree, validateLaneFormat } from '@lumenflow/core/dist/lane-checker.js';
|
|
25
|
+
import { checkLaneFree, validateLaneFormat, checkWipJustification, } from '@lumenflow/core/dist/lane-checker.js';
|
|
25
26
|
// WU-1603: Atomic lane locking to prevent TOCTOU race conditions
|
|
26
27
|
import { acquireLaneLock, releaseLaneLock, checkLaneLock, forceRemoveStaleLock, } from '@lumenflow/core/dist/lane-lock.js';
|
|
27
28
|
// WU-1825: Import from unified code-path-validator (consolidates 3 validators)
|
|
28
|
-
|
|
29
|
+
// WU-1213: Using deprecated sync API - async validate() requires larger refactor (separate WU)
|
|
30
|
+
import {
|
|
31
|
+
// eslint-disable-next-line sonarjs/deprecation -- sync API required for current architecture
|
|
32
|
+
validateLaneCodePaths, logLaneValidationWarnings, } from '@lumenflow/core/dist/code-path-validator.js';
|
|
29
33
|
// WU-1574: parseBacklogFrontmatter/getSectionHeadings removed - state store replaces backlog parsing
|
|
30
34
|
import { detectConflicts } from '@lumenflow/core/dist/code-paths-overlap.js';
|
|
31
35
|
import { getGitForCwd, createGitForPath } from '@lumenflow/core/dist/git-adapter.js';
|
|
32
36
|
import { die } from '@lumenflow/core/dist/error-handler.js';
|
|
33
37
|
import { createWUParser, WU_OPTIONS } from '@lumenflow/core/dist/arg-parser.js';
|
|
34
38
|
import { WU_PATHS, getStateStoreDirFromBacklog } from '@lumenflow/core/dist/wu-paths.js';
|
|
35
|
-
import { BRANCHES, REMOTES, WU_STATUS, CLAIMED_MODES, STATUS_SECTIONS, PATTERNS, toKebab, LOG_PREFIX, GIT_REFS, MICRO_WORKTREE_OPERATIONS, COMMIT_FORMATS, EMOJI, FILE_SYSTEM, STRING_LITERALS, } from '@lumenflow/core/dist/wu-constants.js';
|
|
39
|
+
import { BRANCHES, REMOTES, WU_STATUS, CLAIMED_MODES, STATUS_SECTIONS, PATTERNS, toKebab, LOG_PREFIX, GIT_REFS, MICRO_WORKTREE_OPERATIONS, COMMIT_FORMATS, EMOJI, FILE_SYSTEM, STRING_LITERALS, LUMENFLOW_PATHS, } from '@lumenflow/core/dist/wu-constants.js';
|
|
36
40
|
import { withMicroWorktree } from '@lumenflow/core/dist/micro-worktree.js';
|
|
37
41
|
import { ensureOnMain, ensureMainUpToDate } from '@lumenflow/core/dist/wu-helpers.js';
|
|
38
42
|
import { emitWUFlowEvent } from '@lumenflow/core/dist/telemetry.js';
|
|
@@ -51,7 +55,9 @@ import { WUStateStore } from '@lumenflow/core/dist/wu-state-store.js';
|
|
|
51
55
|
import { generateBacklog, generateStatus } from '@lumenflow/core/dist/backlog-generator.js';
|
|
52
56
|
// WU-2411: Import resume helpers for agent handoff
|
|
53
57
|
import { resumeClaimForHandoff, getWorktreeUncommittedChanges, formatUncommittedChanges, createHandoffCheckpoint, } from '@lumenflow/core/dist/wu-claim-resume.js';
|
|
54
|
-
//
|
|
58
|
+
// WU-1211: Import initiative validation for status auto-progression
|
|
59
|
+
import { shouldProgressInitiativeStatus, findInitiative, writeInitiative, getInitiativeWUs, } from '@lumenflow/initiatives/dist/index.js';
|
|
60
|
+
// ensureOnMain() moved to wu-helpers.ts (WU-1256)
|
|
55
61
|
async function ensureCleanOrClaimOnlyWhenNoAuto() {
|
|
56
62
|
// Require staged claim edits only if running with --no-auto
|
|
57
63
|
const status = await getGitForCwd().getStatus();
|
|
@@ -258,7 +264,52 @@ async function updateWUYaml(WU_PATH, id, lane, claimedMode = 'worktree', worktre
|
|
|
258
264
|
// Write file
|
|
259
265
|
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool writes WU files
|
|
260
266
|
await writeFile(WU_PATH, out, { encoding: FILE_SYSTEM.UTF8 });
|
|
261
|
-
|
|
267
|
+
// WU-1211: Return both title and initiative for status progression check
|
|
268
|
+
return { title: doc.title || '', initiative: doc.initiative || null };
|
|
269
|
+
}
|
|
270
|
+
/**
|
|
271
|
+
* WU-1211: Check and progress initiative status from draft/open to in_progress.
|
|
272
|
+
*
|
|
273
|
+
* Called when a WU with an initiative field is claimed. If this is the first
|
|
274
|
+
* WU being claimed for the initiative, progress the initiative status.
|
|
275
|
+
*
|
|
276
|
+
* @param {string} worktreePath - Path to micro-worktree (or main)
|
|
277
|
+
* @param {string} initiativeRef - Initiative ID or slug
|
|
278
|
+
* @param {string} wuId - WU ID being claimed
|
|
279
|
+
* @returns {Promise<{updated: boolean, initPath: string|null}>} Result
|
|
280
|
+
*/
|
|
281
|
+
async function maybeProgressInitiativeStatus(worktreePath, initiativeRef, wuId) {
|
|
282
|
+
try {
|
|
283
|
+
// Find the initiative
|
|
284
|
+
const initiative = findInitiative(initiativeRef);
|
|
285
|
+
if (!initiative) {
|
|
286
|
+
console.log(`${PREFIX} Initiative ${initiativeRef} not found (may be created later)`);
|
|
287
|
+
return { updated: false, initPath: null };
|
|
288
|
+
}
|
|
289
|
+
// Get all WUs for this initiative to check if any are in_progress
|
|
290
|
+
const wus = getInitiativeWUs(initiativeRef);
|
|
291
|
+
// Include the WU we're currently claiming as in_progress
|
|
292
|
+
const wusWithCurrent = wus.map((wu) => wu.id === wuId ? { ...wu, doc: { ...wu.doc, status: 'in_progress' } } : wu);
|
|
293
|
+
const wuDocs = wusWithCurrent.map((wu) => wu.doc);
|
|
294
|
+
// Check if initiative status should progress
|
|
295
|
+
const progressCheck = shouldProgressInitiativeStatus(initiative.doc, wuDocs);
|
|
296
|
+
if (!progressCheck.shouldProgress || !progressCheck.newStatus) {
|
|
297
|
+
return { updated: false, initPath: null };
|
|
298
|
+
}
|
|
299
|
+
// Update initiative status in worktree
|
|
300
|
+
const initRelativePath = initiative.path.replace(process.cwd() + '/', '');
|
|
301
|
+
const initAbsPath = path.join(worktreePath, initRelativePath);
|
|
302
|
+
// Read, update, write
|
|
303
|
+
const initDoc = { ...initiative.doc, status: progressCheck.newStatus };
|
|
304
|
+
writeInitiative(initAbsPath, initDoc);
|
|
305
|
+
console.log(`${PREFIX} ✅ Initiative ${initiativeRef} status progressed: ${initiative.doc.status} → ${progressCheck.newStatus}`);
|
|
306
|
+
return { updated: true, initPath: initRelativePath };
|
|
307
|
+
}
|
|
308
|
+
catch (error) {
|
|
309
|
+
// Non-fatal: log warning and continue
|
|
310
|
+
console.warn(`${PREFIX} ⚠️ Could not check initiative status progression: ${error.message}`);
|
|
311
|
+
return { updated: false, initPath: null };
|
|
312
|
+
}
|
|
262
313
|
}
|
|
263
314
|
async function addOrReplaceInProgressStatus(statusPath, id, title) {
|
|
264
315
|
// Check file exists
|
|
@@ -351,7 +402,7 @@ async function appendClaimEventOnly(stateDir, id, title, lane) {
|
|
|
351
402
|
export function getWorktreeCommitFiles(wuId) {
|
|
352
403
|
return [
|
|
353
404
|
`docs/04-operations/tasks/wu/${wuId}.yaml`,
|
|
354
|
-
|
|
405
|
+
LUMENFLOW_PATHS.WU_EVENTS, // WU-1740: Event store is source of truth
|
|
355
406
|
// WU-1746: Explicitly NOT including:
|
|
356
407
|
// - docs/04-operations/tasks/backlog.md
|
|
357
408
|
// - docs/04-operations/tasks/status.md
|
|
@@ -407,12 +458,7 @@ async function applyCanonicalClaimUpdate(ctx, sessionId) {
|
|
|
407
458
|
let updatedTitle = '';
|
|
408
459
|
const filesToCommit = args.noAuto && stagedChanges.length > 0
|
|
409
460
|
? stagedChanges.map((change) => change.filePath).filter(Boolean)
|
|
410
|
-
: [
|
|
411
|
-
WU_PATHS.WU(id),
|
|
412
|
-
WU_PATHS.STATUS(),
|
|
413
|
-
WU_PATHS.BACKLOG(),
|
|
414
|
-
'.lumenflow/state/wu-events.jsonl',
|
|
415
|
-
];
|
|
461
|
+
: [WU_PATHS.WU(id), WU_PATHS.STATUS(), WU_PATHS.BACKLOG(), LUMENFLOW_PATHS.WU_EVENTS];
|
|
416
462
|
console.log(`${PREFIX} Updating canonical claim state (push-only)...`);
|
|
417
463
|
await withMicroWorktree({
|
|
418
464
|
operation: MICRO_WORKTREE_OPERATIONS.WU_CLAIM,
|
|
@@ -433,10 +479,23 @@ async function applyCanonicalClaimUpdate(ctx, sessionId) {
|
|
|
433
479
|
console.log(`${PREFIX} YAML fixes applied successfully`);
|
|
434
480
|
}
|
|
435
481
|
const microGit = createGitForPath(worktreePath);
|
|
436
|
-
|
|
437
|
-
|
|
482
|
+
// WU-1211: updateWUYaml now returns {title, initiative}
|
|
483
|
+
const updateResult = await updateWUYaml(microWUPath, id, args.lane, claimedMode, worktreePathForYaml, sessionId, microGit);
|
|
484
|
+
updatedTitle = updateResult.title || updatedTitle;
|
|
438
485
|
await addOrReplaceInProgressStatus(microStatusPath, id, updatedTitle);
|
|
439
486
|
await removeFromReadyAndAddToInProgressBacklog(microBacklogPath, id, updatedTitle, args.lane);
|
|
487
|
+
// WU-1211: Check and progress initiative status
|
|
488
|
+
let initPath = null;
|
|
489
|
+
if (updateResult.initiative) {
|
|
490
|
+
const initProgress = await maybeProgressInitiativeStatus(worktreePath, updateResult.initiative, id);
|
|
491
|
+
initPath = initProgress.initPath;
|
|
492
|
+
}
|
|
493
|
+
// Include initiative path in files to commit if updated
|
|
494
|
+
const allFilesToCommit = initPath ? [...filesToCommit, initPath] : filesToCommit;
|
|
495
|
+
return {
|
|
496
|
+
commitMessage: commitMsg,
|
|
497
|
+
files: allFilesToCommit,
|
|
498
|
+
};
|
|
440
499
|
}
|
|
441
500
|
return {
|
|
442
501
|
commitMessage: commitMsg,
|
|
@@ -459,10 +518,14 @@ async function readWUTitle(id) {
|
|
|
459
518
|
// Read file
|
|
460
519
|
// eslint-disable-next-line security/detect-non-literal-fs-filename -- CLI tool validates WU files
|
|
461
520
|
const text = await readFile(p, { encoding: FILE_SYSTEM.UTF8 });
|
|
462
|
-
|
|
521
|
+
// Match title field - use RegExp.exec for sonarjs/prefer-regexp-exec compliance
|
|
522
|
+
// Regex is safe: runs on trusted WU YAML files with bounded input
|
|
523
|
+
// eslint-disable-next-line sonarjs/slow-regex -- Bounded input from WU YAML files
|
|
524
|
+
const titlePattern = /^title:\s*"?([^"\n]+)"?$/m;
|
|
525
|
+
const m = titlePattern.exec(text);
|
|
463
526
|
return m ? m[1] : null;
|
|
464
527
|
}
|
|
465
|
-
// emitWUFlowEvent() moved to telemetry.
|
|
528
|
+
// emitWUFlowEvent() moved to telemetry.ts as emitWUFlowEvent() (WU-1256)
|
|
466
529
|
/**
|
|
467
530
|
* Check if there's already a Branch-Only WU in progress
|
|
468
531
|
* Branch-Only mode doesn't support parallel WUs (only one WU at a time in main checkout)
|
|
@@ -492,11 +555,12 @@ async function checkExistingBranchOnlyWU(statusPath, currentWU) {
|
|
|
492
555
|
else
|
|
493
556
|
endIdx = startIdx + 1 + endIdx;
|
|
494
557
|
// Extract WU IDs from In Progress section
|
|
558
|
+
// Use RegExp.exec for sonarjs/prefer-regexp-exec compliance
|
|
495
559
|
const wuPattern = /\[?(WU-\d+)/i;
|
|
496
560
|
const inProgressWUs = lines
|
|
497
561
|
.slice(startIdx + 1, endIdx)
|
|
498
562
|
.map((line) => {
|
|
499
|
-
const match =
|
|
563
|
+
const match = wuPattern.exec(line);
|
|
500
564
|
return match ? match[1].toUpperCase() : null;
|
|
501
565
|
})
|
|
502
566
|
.filter(Boolean)
|
|
@@ -566,7 +630,7 @@ async function handleOrphanCheck(lane, id) {
|
|
|
566
630
|
WU_PATHS.BACKLOG(),
|
|
567
631
|
WU_PATHS.STATUS(),
|
|
568
632
|
`.lumenflow/stamps/${orphanId}.done`,
|
|
569
|
-
|
|
633
|
+
LUMENFLOW_PATHS.WU_EVENTS,
|
|
570
634
|
],
|
|
571
635
|
};
|
|
572
636
|
},
|
|
@@ -655,7 +719,12 @@ function handleCodePathOverlap(WU_PATH, STATUS_PATH, id, args) {
|
|
|
655
719
|
});
|
|
656
720
|
if (overlapCheck.hasBlocker && !args.forceOverlap) {
|
|
657
721
|
const conflictList = overlapCheck.conflicts
|
|
658
|
-
.map((c) =>
|
|
722
|
+
.map((c) => {
|
|
723
|
+
const displayedOverlaps = c.overlaps.slice(0, 3).join(', ');
|
|
724
|
+
const remainingCount = c.overlaps.length - 3;
|
|
725
|
+
const suffix = remainingCount > 0 ? ` (+${remainingCount} more)` : '';
|
|
726
|
+
return ` - ${c.wuid}: ${displayedOverlaps}${suffix}`;
|
|
727
|
+
})
|
|
659
728
|
.join(STRING_LITERALS.NEWLINE);
|
|
660
729
|
die(`Code path overlap detected with in-progress WUs:\n\n${conflictList}\n\n` +
|
|
661
730
|
`Merge conflicts are guaranteed if both WUs proceed.\n\n` +
|
|
@@ -727,11 +796,20 @@ async function claimBranchOnlyMode(ctx) {
|
|
|
727
796
|
await ensureCleanOrClaimOnlyWhenNoAuto();
|
|
728
797
|
}
|
|
729
798
|
else {
|
|
730
|
-
|
|
731
|
-
|
|
799
|
+
// WU-1211: updateWUYaml now returns {title, initiative}
|
|
800
|
+
const updateResult = await updateWUYaml(WU_PATH, id, args.lane, claimedMode, null, sessionId);
|
|
801
|
+
finalTitle = updateResult.title || finalTitle;
|
|
732
802
|
await addOrReplaceInProgressStatus(STATUS_PATH, id, finalTitle);
|
|
733
803
|
await removeFromReadyAndAddToInProgressBacklog(BACKLOG_PATH, id, finalTitle, args.lane);
|
|
734
|
-
|
|
804
|
+
const filesToAdd = [WU_PATH, STATUS_PATH, BACKLOG_PATH];
|
|
805
|
+
// WU-1211: Progress initiative status if needed
|
|
806
|
+
if (updateResult.initiative) {
|
|
807
|
+
const initProgress = await maybeProgressInitiativeStatus(process.cwd(), updateResult.initiative, id);
|
|
808
|
+
if (initProgress.initPath) {
|
|
809
|
+
filesToAdd.push(initProgress.initPath);
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
await getGitForCwd().add(filesToAdd.map((f) => JSON.stringify(f)).join(' '));
|
|
735
813
|
}
|
|
736
814
|
await getGitForCwd().commit(msg);
|
|
737
815
|
console.warn(`${PREFIX} Warning: --no-push enabled. Claim is local-only and NOT visible to other agents.`);
|
|
@@ -741,10 +819,12 @@ async function claimBranchOnlyMode(ctx) {
|
|
|
741
819
|
}
|
|
742
820
|
// Summary
|
|
743
821
|
console.log(`\n${PREFIX} Claim recorded in Branch-Only mode.`);
|
|
744
|
-
|
|
822
|
+
const wuDisplay = finalTitle ? `- WU: ${id} — ${finalTitle}` : `- WU: ${id}`;
|
|
823
|
+
console.log(wuDisplay);
|
|
745
824
|
console.log(`- Lane: ${args.lane}`);
|
|
746
825
|
console.log(`- Mode: Branch-Only (no worktree)`);
|
|
747
|
-
|
|
826
|
+
const refDisplay = args.noPush ? `- Commit: ${msg}` : `- Branch: ${branch}`;
|
|
827
|
+
console.log(refDisplay);
|
|
748
828
|
console.log('\n⚠️ LIMITATION: Branch-Only mode does not support parallel WUs (WIP=1 across ALL lanes)');
|
|
749
829
|
console.log('Next: work on this branch in the main checkout.');
|
|
750
830
|
// WU-1360: Print next-steps checklist to prevent common mistakes
|
|
@@ -768,6 +848,82 @@ async function claimBranchOnlyMode(ctx) {
|
|
|
768
848
|
// WU-1763: Print lifecycle nudge with tips for tool adoption
|
|
769
849
|
printLifecycleNudge(id);
|
|
770
850
|
}
|
|
851
|
+
/**
|
|
852
|
+
* WU-1213: Handle local-only claim metadata update (noPush mode).
|
|
853
|
+
* Extracted to reduce cognitive complexity of claimWorktreeMode.
|
|
854
|
+
*
|
|
855
|
+
* @returns {Promise<{finalTitle: string, initPathToCommit: string | null}>}
|
|
856
|
+
*/
|
|
857
|
+
async function handleNoPushMetadataUpdate(ctx) {
|
|
858
|
+
const { args, id, worktree, worktreePath, WU_PATH, BACKLOG_PATH, claimedMode, fixableIssues, sessionId, title, updatedTitle, stagedChanges, } = ctx;
|
|
859
|
+
let finalTitle = updatedTitle || title;
|
|
860
|
+
let initPathToCommit = null;
|
|
861
|
+
if (args.noAuto) {
|
|
862
|
+
await applyStagedChangesToMicroWorktree(worktreePath, stagedChanges);
|
|
863
|
+
}
|
|
864
|
+
else {
|
|
865
|
+
const wtWUPath = path.join(worktreePath, WU_PATH);
|
|
866
|
+
const wtBacklogPath = path.join(worktreePath, BACKLOG_PATH);
|
|
867
|
+
if (fixableIssues && fixableIssues.length > 0) {
|
|
868
|
+
console.log(`${PREFIX} Applying ${fixableIssues.length} YAML fix(es)...`);
|
|
869
|
+
autoFixWUYaml(wtWUPath);
|
|
870
|
+
console.log(`${PREFIX} YAML fixes applied successfully`);
|
|
871
|
+
}
|
|
872
|
+
const updateResult = await updateWUYaml(wtWUPath, id, args.lane, claimedMode, worktree, sessionId);
|
|
873
|
+
finalTitle = updateResult.title || finalTitle;
|
|
874
|
+
const wtStateDir = getStateStoreDirFromBacklog(wtBacklogPath);
|
|
875
|
+
await appendClaimEventOnly(wtStateDir, id, finalTitle, args.lane);
|
|
876
|
+
if (updateResult.initiative) {
|
|
877
|
+
const initProgress = await maybeProgressInitiativeStatus(worktreePath, updateResult.initiative, id);
|
|
878
|
+
initPathToCommit = initProgress.initPath;
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
return { finalTitle, initPathToCommit };
|
|
882
|
+
}
|
|
883
|
+
/**
|
|
884
|
+
* WU-1213: Setup worktree dependencies (symlink or full install).
|
|
885
|
+
* Extracted to reduce cognitive complexity of claimWorktreeMode.
|
|
886
|
+
*/
|
|
887
|
+
async function setupWorktreeDependencies(worktreePath, originalCwd, skipSetup) {
|
|
888
|
+
// eslint-disable-next-line sonarjs/no-selector-parameter -- skipSetup mirrors CLI flag semantics
|
|
889
|
+
if (skipSetup) {
|
|
890
|
+
// WU-1443: Symlink-only mode for fast claims
|
|
891
|
+
const symlinkResult = symlinkNodeModules(worktreePath, console, originalCwd);
|
|
892
|
+
if (symlinkResult.created) {
|
|
893
|
+
console.log(`${PREFIX} ${EMOJI.SUCCESS} node_modules symlinked (--skip-setup mode)`);
|
|
894
|
+
}
|
|
895
|
+
else if (symlinkResult.refused) {
|
|
896
|
+
console.warn(`${PREFIX} Warning: symlink refused: ${symlinkResult.reason}`);
|
|
897
|
+
console.warn(`${PREFIX} Run 'pnpm install' manually in the worktree`);
|
|
898
|
+
}
|
|
899
|
+
// WU-1579: Auto-symlink nested package node_modules for turbo typecheck
|
|
900
|
+
if (!symlinkResult.refused) {
|
|
901
|
+
const nestedResult = symlinkNestedNodeModules(worktreePath, originalCwd);
|
|
902
|
+
if (nestedResult.created > 0) {
|
|
903
|
+
console.log(`${PREFIX} ${EMOJI.SUCCESS} ${nestedResult.created} nested node_modules symlinked for typecheck`);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
// WU-1023: Full setup mode (default) - run pnpm install with progress indicator
|
|
909
|
+
console.log(`${PREFIX} Installing worktree dependencies (this may take a moment)...`);
|
|
910
|
+
try {
|
|
911
|
+
const { execSync } = await import('node:child_process');
|
|
912
|
+
execSync('pnpm install --frozen-lockfile', {
|
|
913
|
+
cwd: worktreePath,
|
|
914
|
+
stdio: 'inherit',
|
|
915
|
+
timeout: 300000, // 5 minute timeout
|
|
916
|
+
});
|
|
917
|
+
console.log(`${PREFIX} ${EMOJI.SUCCESS} Worktree dependencies installed`);
|
|
918
|
+
}
|
|
919
|
+
catch (installError) {
|
|
920
|
+
console.warn(`${PREFIX} Warning: pnpm install failed: ${installError.message}`);
|
|
921
|
+
console.warn(`${PREFIX} You may need to run 'pnpm install' manually in the worktree`);
|
|
922
|
+
console.log(`${PREFIX} Falling back to symlink approach...`);
|
|
923
|
+
applyFallbackSymlinks(worktreePath, originalCwd, console);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
}
|
|
771
927
|
/**
|
|
772
928
|
* Execute worktree mode claim workflow
|
|
773
929
|
*
|
|
@@ -788,14 +944,12 @@ async function claimBranchOnlyMode(ctx) {
|
|
|
788
944
|
* - Cleaner rollback: delete worktree+branch = claim undone
|
|
789
945
|
*/
|
|
790
946
|
async function claimWorktreeMode(ctx) {
|
|
791
|
-
const { args, id, laneK, title, branch, worktree, WU_PATH,
|
|
792
|
-
sessionId, updatedTitle, stagedChanges, } = ctx;
|
|
947
|
+
const { args, id, laneK, title, branch, worktree, WU_PATH, updatedTitle } = ctx;
|
|
793
948
|
const originalCwd = process.cwd();
|
|
794
949
|
const worktreePath = path.resolve(worktree);
|
|
795
950
|
let finalTitle = updatedTitle || title;
|
|
796
951
|
const commitMsg = COMMIT_FORMATS.CLAIM(id.toLowerCase(), laneK);
|
|
797
952
|
// WU-1741: Step 1 - Create work worktree+branch from main
|
|
798
|
-
// Branch creation IS the coordination lock (git prevents duplicate branch names)
|
|
799
953
|
console.log(`${PREFIX} Creating worktree (branch = coordination lock)...`);
|
|
800
954
|
const startPoint = args.noPush ? BRANCHES.MAIN : `${REMOTES.ORIGIN}/${BRANCHES.MAIN}`;
|
|
801
955
|
await getGitForCwd().worktreeAdd(worktree, branch, startPoint);
|
|
@@ -804,80 +958,27 @@ async function claimWorktreeMode(ctx) {
|
|
|
804
958
|
const wtGit = createGitForPath(worktreePath);
|
|
805
959
|
await wtGit.push(REMOTES.ORIGIN, branch, { setUpstream: true });
|
|
806
960
|
}
|
|
961
|
+
// Handle local-only claim metadata update (noPush mode)
|
|
807
962
|
if (args.noPush) {
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
}
|
|
812
|
-
else {
|
|
813
|
-
const wtWUPath = path.join(worktreePath, WU_PATH);
|
|
814
|
-
const wtBacklogPath = path.join(worktreePath, BACKLOG_PATH);
|
|
815
|
-
if (fixableIssues && fixableIssues.length > 0) {
|
|
816
|
-
console.log(`${PREFIX} Applying ${fixableIssues.length} YAML fix(es)...`);
|
|
817
|
-
autoFixWUYaml(wtWUPath);
|
|
818
|
-
console.log(`${PREFIX} YAML fixes applied successfully`);
|
|
819
|
-
}
|
|
820
|
-
finalTitle =
|
|
821
|
-
(await updateWUYaml(wtWUPath, id, args.lane, claimedMode, worktree, sessionId)) ||
|
|
822
|
-
finalTitle;
|
|
823
|
-
// WU-1746: Only append claim event to state store - don't regenerate backlog.md/status.md
|
|
824
|
-
const wtStateDir = getStateStoreDirFromBacklog(wtBacklogPath);
|
|
825
|
-
await appendClaimEventOnly(wtStateDir, id, finalTitle, args.lane);
|
|
826
|
-
}
|
|
963
|
+
const metadataResult = await handleNoPushMetadataUpdate({ ...ctx, worktreePath });
|
|
964
|
+
finalTitle = metadataResult.finalTitle;
|
|
965
|
+
// Commit metadata in worktree
|
|
827
966
|
console.log(`${PREFIX} Committing claim metadata in worktree...`);
|
|
828
967
|
const wtGit = createGitForPath(worktreePath);
|
|
829
968
|
const filesToCommit = getWorktreeCommitFiles(id);
|
|
969
|
+
if (metadataResult.initPathToCommit) {
|
|
970
|
+
filesToCommit.push(metadataResult.initPathToCommit);
|
|
971
|
+
}
|
|
830
972
|
await wtGit.add(filesToCommit);
|
|
831
973
|
await wtGit.commit(commitMsg);
|
|
832
974
|
console.log(`${PREFIX} ${EMOJI.SUCCESS} Claim committed: ${commitMsg}`);
|
|
833
975
|
console.warn(`${PREFIX} Warning: --no-push enabled. Claim is local-only and NOT visible to other agents.`);
|
|
834
976
|
}
|
|
835
977
|
// WU-1023: Auto-setup worktree dependencies
|
|
836
|
-
|
|
837
|
-
// Use --skip-setup to use symlink-only approach for faster claims when deps are already built
|
|
838
|
-
if (args.skipSetup) {
|
|
839
|
-
// WU-1443: Symlink-only mode for fast claims
|
|
840
|
-
// WU-2238: Pass mainRepoPath to detect broken worktree-path symlinks
|
|
841
|
-
const symlinkResult = symlinkNodeModules(worktreePath, console, originalCwd);
|
|
842
|
-
if (symlinkResult.created) {
|
|
843
|
-
console.log(`${PREFIX} ${EMOJI.SUCCESS} node_modules symlinked (--skip-setup mode)`);
|
|
844
|
-
}
|
|
845
|
-
else if (symlinkResult.refused) {
|
|
846
|
-
console.warn(`${PREFIX} Warning: symlink refused: ${symlinkResult.reason}`);
|
|
847
|
-
console.warn(`${PREFIX} Run 'pnpm install' manually in the worktree`);
|
|
848
|
-
}
|
|
849
|
-
// WU-1579: Auto-symlink nested package node_modules for turbo typecheck
|
|
850
|
-
if (!symlinkResult.refused) {
|
|
851
|
-
const nestedResult = symlinkNestedNodeModules(worktreePath, originalCwd);
|
|
852
|
-
if (nestedResult.created > 0) {
|
|
853
|
-
console.log(`${PREFIX} ${EMOJI.SUCCESS} ${nestedResult.created} nested node_modules symlinked for typecheck`);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
}
|
|
857
|
-
else {
|
|
858
|
-
// WU-1023: Full setup mode (default) - run pnpm install with progress indicator
|
|
859
|
-
// This ensures CLI dist is built and all dependencies are properly resolved
|
|
860
|
-
console.log(`${PREFIX} Installing worktree dependencies (this may take a moment)...`);
|
|
861
|
-
try {
|
|
862
|
-
const { execSync } = await import('node:child_process');
|
|
863
|
-
execSync('pnpm install --frozen-lockfile', {
|
|
864
|
-
cwd: worktreePath,
|
|
865
|
-
stdio: 'inherit', // Shows progress output from pnpm
|
|
866
|
-
timeout: 300000, // 5 minute timeout for full install
|
|
867
|
-
});
|
|
868
|
-
console.log(`${PREFIX} ${EMOJI.SUCCESS} Worktree dependencies installed`);
|
|
869
|
-
}
|
|
870
|
-
catch (installError) {
|
|
871
|
-
// Non-fatal: warn but don't block claim
|
|
872
|
-
console.warn(`${PREFIX} Warning: pnpm install failed: ${installError.message}`);
|
|
873
|
-
console.warn(`${PREFIX} You may need to run 'pnpm install' manually in the worktree`);
|
|
874
|
-
// Fall back to symlink approach so worktree is at least somewhat usable
|
|
875
|
-
console.log(`${PREFIX} Falling back to symlink approach...`);
|
|
876
|
-
applyFallbackSymlinks(worktreePath, originalCwd, console);
|
|
877
|
-
}
|
|
878
|
-
}
|
|
978
|
+
await setupWorktreeDependencies(worktreePath, originalCwd, args.skipSetup);
|
|
879
979
|
console.log(`${PREFIX} Claim recorded in worktree`);
|
|
880
|
-
|
|
980
|
+
const worktreeWuDisplay = finalTitle ? `- WU: ${id} — ${finalTitle}` : `- WU: ${id}`;
|
|
981
|
+
console.log(worktreeWuDisplay);
|
|
881
982
|
console.log(`- Lane: ${args.lane}`);
|
|
882
983
|
console.log(`- Worktree: ${worktreePath}`);
|
|
883
984
|
console.log(`- Branch: ${branch}`);
|
|
@@ -1113,7 +1214,13 @@ async function main() {
|
|
|
1113
1214
|
const doc = preflightValidateWU(WU_PATH, id);
|
|
1114
1215
|
await handleOrphanCheck(args.lane, id);
|
|
1115
1216
|
validateLaneFormatWithError(args.lane);
|
|
1217
|
+
// WU-1187: Check for WIP justification when WIP > 1 (soft enforcement - warning only)
|
|
1218
|
+
const wipJustificationCheck = checkWipJustification(args.lane);
|
|
1219
|
+
if (wipJustificationCheck.warning) {
|
|
1220
|
+
console.warn(`${PREFIX} ${wipJustificationCheck.warning}`);
|
|
1221
|
+
}
|
|
1116
1222
|
// WU-1372: Lane-to-code_paths consistency check (advisory only, never blocks)
|
|
1223
|
+
// eslint-disable-next-line sonarjs/deprecation -- sync API required for current architecture
|
|
1117
1224
|
const laneValidation = validateLaneCodePaths(doc, args.lane);
|
|
1118
1225
|
logLaneValidationWarnings(laneValidation, PREFIX);
|
|
1119
1226
|
// WU-1361: YAML schema validation at claim time
|
|
@@ -1190,11 +1297,14 @@ async function main() {
|
|
|
1190
1297
|
const title = (await readWUTitle(id)) || '';
|
|
1191
1298
|
const branch = args.branch || `lane/${laneK}/${idK}`;
|
|
1192
1299
|
const worktree = args.worktree || `worktrees/${laneK}-${idK}`;
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1300
|
+
// Determine claimed mode based on flags (no nested ternaries for sonarjs compliance)
|
|
1301
|
+
let claimedMode = CLAIMED_MODES.WORKTREE;
|
|
1302
|
+
if (args.branchOnly) {
|
|
1303
|
+
claimedMode = CLAIMED_MODES.BRANCH_ONLY;
|
|
1304
|
+
}
|
|
1305
|
+
else if (args.prMode) {
|
|
1306
|
+
claimedMode = CLAIMED_MODES.WORKTREE_PR;
|
|
1307
|
+
}
|
|
1198
1308
|
// Branch-Only mode validation
|
|
1199
1309
|
if (args.branchOnly) {
|
|
1200
1310
|
await validateBranchOnlyMode(STATUS_PATH, id);
|
|
@@ -1307,5 +1417,5 @@ async function main() {
|
|
|
1307
1417
|
// path but import.meta.url resolves to the real path - they never match
|
|
1308
1418
|
import { runCLI } from './cli-entry-point.js';
|
|
1309
1419
|
if (import.meta.main) {
|
|
1310
|
-
runCLI(main);
|
|
1420
|
+
void runCLI(main);
|
|
1311
1421
|
}
|
package/dist/wu-cleanup.js
CHANGED
|
@@ -191,7 +191,7 @@ async function main() {
|
|
|
191
191
|
}
|
|
192
192
|
const id = args.id.toUpperCase();
|
|
193
193
|
const wu = readWU(WU_PATHS.WU(id), id);
|
|
194
|
-
// Use kebab-case lane naming (match wu-claim.
|
|
194
|
+
// Use kebab-case lane naming (match wu-claim.ts logic)
|
|
195
195
|
const laneK = wu.lane
|
|
196
196
|
?.toLowerCase()
|
|
197
197
|
.trim()
|
|
@@ -267,9 +267,10 @@ async function main() {
|
|
|
267
267
|
console.log('║ Branch deleted (local + remote)');
|
|
268
268
|
console.log(BOX.BOT);
|
|
269
269
|
}
|
|
270
|
-
//
|
|
271
|
-
|
|
270
|
+
// WU-1181: Use import.meta.main instead of process.argv[1] comparison
|
|
271
|
+
// The old pattern fails with pnpm symlinks because process.argv[1] is the symlink
|
|
272
|
+
// path but import.meta.url resolves to the real path - they never match
|
|
272
273
|
import { runCLI } from './cli-entry-point.js';
|
|
273
|
-
if (
|
|
274
|
+
if (import.meta.main) {
|
|
274
275
|
runCLI(main);
|
|
275
276
|
}
|