cclaw-cli 0.46.0 → 0.46.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/artifact-linter.d.ts +2 -1
- package/dist/artifact-linter.js +18 -9
- package/dist/content/harnesses-doc.js +1 -3
- package/dist/content/ideate-command.d.ts +0 -7
- package/dist/content/ideate-command.js +0 -7
- package/dist/content/learnings.d.ts +3 -2
- package/dist/content/learnings.js +8 -5
- package/dist/content/retro-command.js +2 -2
- package/dist/content/stage-schema.d.ts +0 -3
- package/dist/content/stage-schema.js +1 -12
- package/dist/content/stages/brainstorm.js +3 -5
- package/dist/content/stages/design.js +7 -9
- package/dist/content/stages/plan.js +1 -3
- package/dist/content/stages/review.js +4 -10
- package/dist/content/stages/schema-types.d.ts +3 -16
- package/dist/content/stages/scope.js +8 -11
- package/dist/content/stages/ship.js +1 -4
- package/dist/content/stages/spec.js +1 -3
- package/dist/content/stages/tdd.js +2 -6
- package/dist/content/start-command.js +5 -5
- package/dist/content/subagents.js +0 -140
- package/dist/delegation.d.ts +2 -5
- package/dist/delegation.js +1 -1
- package/dist/doctor.js +48 -0
- package/dist/flow-state.js +2 -2
- package/dist/gate-evidence.js +23 -60
- package/dist/hook-schema.js +66 -0
- package/dist/hook-schemas/claude-hooks.v1.json +2 -1
- package/dist/internal/advance-stage.js +15 -6
- package/dist/knowledge-store.d.ts +4 -0
- package/dist/knowledge-store.js +21 -2
- package/dist/runs.js +44 -5
- package/dist/tdd-cycle.js +16 -0
- package/package.json +1 -1
package/dist/runs.js
CHANGED
|
@@ -374,6 +374,19 @@ async function uniqueArchiveId(projectRoot, baseId) {
|
|
|
374
374
|
function retroArtifactPath(projectRoot) {
|
|
375
375
|
return path.join(activeArtifactsPath(projectRoot), "09-retro.md");
|
|
376
376
|
}
|
|
377
|
+
function parseIsoTimestamp(value) {
|
|
378
|
+
if (!value || value.trim().length === 0)
|
|
379
|
+
return null;
|
|
380
|
+
const parsed = Date.parse(value);
|
|
381
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
382
|
+
}
|
|
383
|
+
function inInclusiveWindow(timestamp, windowStartMs, windowEndMs) {
|
|
384
|
+
if (windowStartMs !== null && timestamp < windowStartMs)
|
|
385
|
+
return false;
|
|
386
|
+
if (windowEndMs !== null && timestamp > windowEndMs)
|
|
387
|
+
return false;
|
|
388
|
+
return true;
|
|
389
|
+
}
|
|
377
390
|
async function evaluateRetroGate(projectRoot, state) {
|
|
378
391
|
const required = state.completedStages.includes("ship");
|
|
379
392
|
const artifactFile = retroArtifactPath(projectRoot);
|
|
@@ -387,18 +400,33 @@ async function evaluateRetroGate(projectRoot, state) {
|
|
|
387
400
|
hasRetroArtifact = false;
|
|
388
401
|
}
|
|
389
402
|
}
|
|
403
|
+
let compoundEntries = state.retro.compoundEntries;
|
|
404
|
+
const windowStartMs = parseIsoTimestamp(state.closeout.retroDraftedAt);
|
|
405
|
+
const windowEndMs = parseIsoTimestamp(state.closeout.retroAcceptedAt) ?? parseIsoTimestamp(state.retro.completedAt);
|
|
406
|
+
const shouldFallbackScan = compoundEntries <= 0 && (windowStartMs !== null || windowEndMs !== null);
|
|
390
407
|
const knowledgeFile = path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
|
|
391
|
-
|
|
392
|
-
if (await exists(knowledgeFile)) {
|
|
408
|
+
if (shouldFallbackScan && (await exists(knowledgeFile))) {
|
|
393
409
|
try {
|
|
394
410
|
const raw = await fs.readFile(knowledgeFile, "utf8");
|
|
411
|
+
compoundEntries = 0;
|
|
395
412
|
for (const line of raw.split(/\r?\n/)) {
|
|
396
413
|
const trimmed = line.trim();
|
|
397
414
|
if (!trimmed)
|
|
398
415
|
continue;
|
|
399
416
|
try {
|
|
400
417
|
const parsed = JSON.parse(trimmed);
|
|
401
|
-
if (parsed.type
|
|
418
|
+
if (parsed.type !== "compound") {
|
|
419
|
+
continue;
|
|
420
|
+
}
|
|
421
|
+
const created = typeof parsed.created === "string" ? parseIsoTimestamp(parsed.created) : null;
|
|
422
|
+
if (created === null || !inInclusiveWindow(created, windowStartMs, windowEndMs)) {
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const source = typeof parsed.source === "string"
|
|
426
|
+
? parsed.source.trim().toLowerCase()
|
|
427
|
+
: null;
|
|
428
|
+
const legacyRetroStage = parsed.stage === "retro";
|
|
429
|
+
if (source === "retro" || legacyRetroStage) {
|
|
402
430
|
compoundEntries += 1;
|
|
403
431
|
}
|
|
404
432
|
}
|
|
@@ -563,6 +591,7 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
563
591
|
const archiveArtifactsPath = path.join(archivePath, "artifacts");
|
|
564
592
|
let sourceState = await readFlowState(projectRoot);
|
|
565
593
|
const retroGate = await evaluateRetroGate(projectRoot, sourceState);
|
|
594
|
+
const shipCompleted = sourceState.completedStages.includes("ship");
|
|
566
595
|
const skipRetro = options.skipRetro === true;
|
|
567
596
|
const skipRetroReason = options.skipRetroReason?.trim();
|
|
568
597
|
if (skipRetro && (!skipRetroReason || skipRetroReason.length === 0)) {
|
|
@@ -571,6 +600,12 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
571
600
|
const retroSkippedInCloseout = sourceState.closeout.retroSkipped === true &&
|
|
572
601
|
typeof sourceState.closeout.retroSkipReason === "string" &&
|
|
573
602
|
sourceState.closeout.retroSkipReason.trim().length > 0;
|
|
603
|
+
const readyForArchive = sourceState.closeout.shipSubstate === "ready_to_archive";
|
|
604
|
+
if (shipCompleted && !readyForArchive && !skipRetro) {
|
|
605
|
+
throw new Error("Archive blocked: closeout is not ready_to_archive. " +
|
|
606
|
+
"Resume /cc-next until closeout reaches ready_to_archive, " +
|
|
607
|
+
"or run `cclaw archive --skip-retro --retro-reason=<text>` for CLI-only flows.");
|
|
608
|
+
}
|
|
574
609
|
if (retroGate.required && !retroGate.completed && !skipRetro && !retroSkippedInCloseout) {
|
|
575
610
|
throw new Error("Archive blocked: retro gate is required after ship completion. " +
|
|
576
611
|
"Run /cc-next (auto-runs retro) or, for CLI-only flows, re-run `cclaw archive --skip-retro --retro-reason=<text>`.");
|
|
@@ -590,8 +625,12 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
590
625
|
const retroSummary = {
|
|
591
626
|
required: retroGate.required,
|
|
592
627
|
completed: retroGate.completed,
|
|
593
|
-
skipped: skipRetro,
|
|
594
|
-
skipReason: skipRetro
|
|
628
|
+
skipped: skipRetro || retroSkippedInCloseout,
|
|
629
|
+
skipReason: skipRetro
|
|
630
|
+
? skipRetroReason
|
|
631
|
+
: retroSkippedInCloseout
|
|
632
|
+
? sourceState.closeout.retroSkipReason
|
|
633
|
+
: undefined,
|
|
595
634
|
compoundEntries: retroGate.compoundEntries
|
|
596
635
|
};
|
|
597
636
|
await ensureDir(archivePath);
|
package/dist/tdd-cycle.js
CHANGED
|
@@ -48,6 +48,14 @@ export function validateTddCycleOrder(entries, options = {}) {
|
|
|
48
48
|
let state = "need_red";
|
|
49
49
|
for (const entry of sliceEntries) {
|
|
50
50
|
if (entry.phase === "red") {
|
|
51
|
+
if (entry.exitCode === undefined) {
|
|
52
|
+
issues.push(`slice ${slice}: red entry must record a non-zero exitCode`);
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (entry.exitCode === 0) {
|
|
56
|
+
issues.push(`slice ${slice}: red entry exitCode must be non-zero`);
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
51
59
|
if (state === "red_open") {
|
|
52
60
|
issues.push(`slice ${slice}: duplicate red before green`);
|
|
53
61
|
continue;
|
|
@@ -56,6 +64,14 @@ export function validateTddCycleOrder(entries, options = {}) {
|
|
|
56
64
|
continue;
|
|
57
65
|
}
|
|
58
66
|
if (entry.phase === "green") {
|
|
67
|
+
if (entry.exitCode === undefined) {
|
|
68
|
+
issues.push(`slice ${slice}: green entry must record exitCode 0`);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
if (entry.exitCode !== 0) {
|
|
72
|
+
issues.push(`slice ${slice}: green entry exitCode must be 0`);
|
|
73
|
+
continue;
|
|
74
|
+
}
|
|
59
75
|
if (state !== "red_open") {
|
|
60
76
|
issues.push(`slice ${slice}: green logged before red`);
|
|
61
77
|
continue;
|