cclaw-cli 0.55.2 → 2.0.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/README.md +3 -3
- package/dist/artifact-linter/brainstorm.js +59 -1
- package/dist/artifact-linter/design.js +46 -1
- package/dist/artifact-linter/plan.js +22 -1
- package/dist/artifact-linter/review.js +35 -1
- package/dist/artifact-linter/scope.js +33 -9
- package/dist/artifact-linter/shared.d.ts +12 -10
- package/dist/artifact-linter/shared.js +102 -41
- package/dist/artifact-linter/ship.js +36 -0
- package/dist/artifact-linter/spec.js +23 -1
- package/dist/artifact-linter/tdd.js +74 -0
- package/dist/artifact-linter.d.ts +1 -1
- package/dist/artifact-linter.js +11 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -0
- package/dist/content/closeout-guidance.d.ts +1 -1
- package/dist/content/closeout-guidance.js +10 -11
- package/dist/content/core-agents.d.ts +35 -36
- package/dist/content/core-agents.js +189 -99
- package/dist/content/diff-command.js +1 -1
- package/dist/content/examples.d.ts +0 -3
- package/dist/content/examples.js +197 -752
- package/dist/content/hook-events.js +1 -2
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +22 -25
- package/dist/content/hooks.js +54 -14
- package/dist/content/idea.d.ts +60 -0
- package/dist/content/idea.js +404 -0
- package/dist/content/learnings.d.ts +2 -4
- package/dist/content/learnings.js +10 -26
- package/dist/content/meta-skill.js +4 -3
- package/dist/content/node-hooks.js +368 -164
- package/dist/content/observe.js +3 -3
- package/dist/content/opencode-plugin.js +12 -32
- package/dist/content/reference-patterns.js +2 -2
- package/dist/content/runtime-shared-snippets.d.ts +8 -0
- package/dist/content/runtime-shared-snippets.js +80 -0
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/skills-elicitation.d.ts +1 -0
- package/dist/content/skills-elicitation.js +123 -0
- package/dist/content/skills.d.ts +1 -0
- package/dist/content/skills.js +54 -2
- package/dist/content/stage-schema.js +107 -63
- package/dist/content/stages/brainstorm.js +7 -3
- package/dist/content/stages/design.js +4 -0
- package/dist/content/stages/review.js +8 -8
- package/dist/content/stages/schema-types.d.ts +2 -2
- package/dist/content/stages/scope.js +7 -3
- package/dist/content/stages/ship.js +1 -1
- package/dist/content/start-command.js +4 -4
- package/dist/content/status-command.js +3 -3
- package/dist/content/subagent-context-skills.js +156 -1
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +12 -82
- package/dist/content/templates.js +108 -6
- package/dist/content/utility-skills.js +26 -97
- package/dist/flow-state.d.ts +12 -6
- package/dist/flow-state.js +5 -6
- package/dist/gate-evidence.d.ts +0 -31
- package/dist/gate-evidence.js +3 -181
- package/dist/harness-adapters.js +1 -1
- package/dist/hook-schemas/claude-hooks.v1.json +2 -3
- package/dist/hook-schemas/codex-hooks.v1.json +1 -1
- package/dist/hook-schemas/cursor-hooks.v1.json +1 -1
- package/dist/install.js +50 -7
- package/dist/internal/advance-stage/advance.js +22 -2
- package/dist/internal/advance-stage/parsers.d.ts +1 -0
- package/dist/internal/advance-stage/parsers.js +6 -0
- package/dist/internal/advance-stage/review-loop.js +1 -10
- package/dist/knowledge-store.d.ts +2 -20
- package/dist/knowledge-store.js +43 -57
- package/dist/policy.js +3 -3
- package/dist/retro-gate.js +8 -90
- package/dist/run-archive.js +1 -4
- package/dist/run-persistence.d.ts +1 -1
- package/dist/run-persistence.js +43 -111
- package/dist/runtime/run-hook.entry.d.ts +3 -0
- package/dist/runtime/run-hook.entry.js +5 -0
- package/dist/runtime/run-hook.mjs +9647 -0
- package/dist/track-heuristics.d.ts +7 -1
- package/dist/track-heuristics.js +12 -0
- package/package.json +4 -2
- package/dist/content/hook-inline-snippets.d.ts +0 -96
- package/dist/content/hook-inline-snippets.js +0 -515
- package/dist/content/idea-command.d.ts +0 -8
- package/dist/content/idea-command.js +0 -322
- package/dist/content/idea-frames.d.ts +0 -31
- package/dist/content/idea-frames.js +0 -140
- package/dist/content/idea-ranking.d.ts +0 -25
- package/dist/content/idea-ranking.js +0 -65
- package/dist/trace-matrix.d.ts +0 -27
- package/dist/trace-matrix.js +0 -226
package/dist/run-persistence.js
CHANGED
|
@@ -17,11 +17,6 @@ export class InvalidStageTransitionError extends Error {
|
|
|
17
17
|
const FLOW_STATE_REL_PATH = `${RUNTIME_ROOT}/state/flow-state.json`;
|
|
18
18
|
const ARCHIVE_DIR_REL_PATH = `${RUNTIME_ROOT}/archive`;
|
|
19
19
|
const ACTIVE_ARTIFACTS_REL_PATH = `${RUNTIME_ROOT}/artifacts`;
|
|
20
|
-
const RECONCILIATION_NOTICES_REL_PATH = `${RUNTIME_ROOT}/state/reconciliation-notices.json`;
|
|
21
|
-
const RECONCILIATION_NOTICES_LOCK_REL_PATH = `${RUNTIME_ROOT}/state/.reconciliation-notices.lock`;
|
|
22
|
-
const RECONCILIATION_NOTICES_SCHEMA_VERSION = 1;
|
|
23
|
-
const CLOSEOUT_SUBSTATE_DEMOTION_KIND = "closeout_substate_demotion";
|
|
24
|
-
const CLOSEOUT_SUBSTATE_GATE_ID = "closeout.shipSubstate";
|
|
25
20
|
const FLOW_STAGE_SET = new Set(FLOW_STAGES);
|
|
26
21
|
function validateFlowTransition(prev, next) {
|
|
27
22
|
if (prev.activeRunId !== next.activeRunId) {
|
|
@@ -74,88 +69,12 @@ function flowStatePath(projectRoot) {
|
|
|
74
69
|
function flowStateLockPath(projectRoot) {
|
|
75
70
|
return path.join(projectRoot, RUNTIME_ROOT, "state", ".flow-state.lock");
|
|
76
71
|
}
|
|
77
|
-
function reconciliationNoticesPath(projectRoot) {
|
|
78
|
-
return path.join(projectRoot, RECONCILIATION_NOTICES_REL_PATH);
|
|
79
|
-
}
|
|
80
|
-
function reconciliationNoticesLockPath(projectRoot) {
|
|
81
|
-
return path.join(projectRoot, RECONCILIATION_NOTICES_LOCK_REL_PATH);
|
|
82
|
-
}
|
|
83
72
|
function archiveRoot(projectRoot) {
|
|
84
73
|
return path.join(projectRoot, ARCHIVE_DIR_REL_PATH);
|
|
85
74
|
}
|
|
86
75
|
function activeArtifactsPath(projectRoot) {
|
|
87
76
|
return path.join(projectRoot, ACTIVE_ARTIFACTS_REL_PATH);
|
|
88
77
|
}
|
|
89
|
-
function asObjectRecord(value) {
|
|
90
|
-
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
91
|
-
return null;
|
|
92
|
-
return value;
|
|
93
|
-
}
|
|
94
|
-
async function appendCloseoutSubstateDemotionNotice(projectRoot, state, demotion) {
|
|
95
|
-
await withDirectoryLock(reconciliationNoticesLockPath(projectRoot), async () => {
|
|
96
|
-
const filePath = reconciliationNoticesPath(projectRoot);
|
|
97
|
-
const existingNotices = [];
|
|
98
|
-
if (await exists(filePath)) {
|
|
99
|
-
try {
|
|
100
|
-
const raw = JSON.parse(await fs.readFile(filePath, "utf8"));
|
|
101
|
-
const parsed = asObjectRecord(raw);
|
|
102
|
-
const noticesRaw = parsed?.notices;
|
|
103
|
-
if (Array.isArray(noticesRaw)) {
|
|
104
|
-
for (const notice of noticesRaw) {
|
|
105
|
-
const typed = asObjectRecord(notice);
|
|
106
|
-
if (typed)
|
|
107
|
-
existingNotices.push(typed);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
catch {
|
|
112
|
-
// Keep going with an empty payload; sync can still report parse errors.
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
const alreadyRecorded = existingNotices.some((notice) => {
|
|
116
|
-
if (notice.runId !== state.activeRunId)
|
|
117
|
-
return false;
|
|
118
|
-
if (notice.kind !== CLOSEOUT_SUBSTATE_DEMOTION_KIND)
|
|
119
|
-
return false;
|
|
120
|
-
const payload = asObjectRecord(notice.payload);
|
|
121
|
-
return payload?.previous === demotion.previous &&
|
|
122
|
-
payload?.next === demotion.next &&
|
|
123
|
-
payload?.reason === demotion.reason;
|
|
124
|
-
});
|
|
125
|
-
if (alreadyRecorded)
|
|
126
|
-
return;
|
|
127
|
-
const ts = new Date().toISOString();
|
|
128
|
-
existingNotices.push({
|
|
129
|
-
id: `${state.activeRunId}:${state.currentStage}:${CLOSEOUT_SUBSTATE_GATE_ID}:${CLOSEOUT_SUBSTATE_DEMOTION_KIND}:${ts}`,
|
|
130
|
-
runId: state.activeRunId,
|
|
131
|
-
stage: state.currentStage,
|
|
132
|
-
gateId: CLOSEOUT_SUBSTATE_GATE_ID,
|
|
133
|
-
reason: demotion.reason,
|
|
134
|
-
demotedAt: ts,
|
|
135
|
-
kind: CLOSEOUT_SUBSTATE_DEMOTION_KIND,
|
|
136
|
-
payload: {
|
|
137
|
-
previous: demotion.previous,
|
|
138
|
-
next: demotion.next,
|
|
139
|
-
reason: demotion.reason
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
|
-
existingNotices.sort((left, right) => {
|
|
143
|
-
const leftTs = typeof left.demotedAt === "string" ? left.demotedAt : "";
|
|
144
|
-
const rightTs = typeof right.demotedAt === "string" ? right.demotedAt : "";
|
|
145
|
-
if (leftTs === rightTs) {
|
|
146
|
-
const leftId = typeof left.id === "string" ? left.id : "";
|
|
147
|
-
const rightId = typeof right.id === "string" ? right.id : "";
|
|
148
|
-
return leftId.localeCompare(rightId);
|
|
149
|
-
}
|
|
150
|
-
return leftTs.localeCompare(rightTs);
|
|
151
|
-
});
|
|
152
|
-
await ensureDir(path.dirname(filePath));
|
|
153
|
-
await writeFileSafe(filePath, `${JSON.stringify({
|
|
154
|
-
schemaVersion: RECONCILIATION_NOTICES_SCHEMA_VERSION,
|
|
155
|
-
notices: existingNotices
|
|
156
|
-
}, null, 2)}\n`, { mode: 0o600 });
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
78
|
function isFlowStage(value) {
|
|
160
79
|
return typeof value === "string" && FLOW_STAGE_SET.has(value);
|
|
161
80
|
}
|
|
@@ -313,6 +232,31 @@ function sanitizeRewinds(value) {
|
|
|
313
232
|
}
|
|
314
233
|
return out;
|
|
315
234
|
}
|
|
235
|
+
function sanitizeInteractionHints(value) {
|
|
236
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
237
|
+
return {};
|
|
238
|
+
}
|
|
239
|
+
const out = {};
|
|
240
|
+
for (const [stage, raw] of Object.entries(value)) {
|
|
241
|
+
if (!isFlowStage(stage))
|
|
242
|
+
continue;
|
|
243
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
244
|
+
continue;
|
|
245
|
+
const typed = raw;
|
|
246
|
+
const skipQuestions = typed.skipQuestions === true ? true : undefined;
|
|
247
|
+
const sourceStage = isFlowStage(typed.sourceStage) ? typed.sourceStage : undefined;
|
|
248
|
+
const recordedAt = typeof typed.recordedAt === "string" ? typed.recordedAt : undefined;
|
|
249
|
+
if (skipQuestions !== true && !sourceStage && !recordedAt) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
out[stage] = {
|
|
253
|
+
...(skipQuestions ? { skipQuestions } : {}),
|
|
254
|
+
...(sourceStage ? { sourceStage } : {}),
|
|
255
|
+
...(recordedAt ? { recordedAt } : {})
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return out;
|
|
259
|
+
}
|
|
316
260
|
function sanitizeRetroState(value) {
|
|
317
261
|
const fallback = {
|
|
318
262
|
required: false,
|
|
@@ -338,14 +282,20 @@ function sanitizeRetroState(value) {
|
|
|
338
282
|
function isShipSubstate(value) {
|
|
339
283
|
return typeof value === "string" && SHIP_SUBSTATES.includes(value);
|
|
340
284
|
}
|
|
341
|
-
function sanitizeCloseoutState(value
|
|
285
|
+
function sanitizeCloseoutState(value) {
|
|
342
286
|
const fallback = createInitialCloseoutState();
|
|
343
287
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
344
288
|
return fallback;
|
|
345
289
|
}
|
|
346
290
|
const typed = value;
|
|
347
|
-
|
|
348
|
-
|
|
291
|
+
const rawShipSubstate = typeof typed.shipSubstate === "string" ? typed.shipSubstate : undefined;
|
|
292
|
+
let shipSubstate;
|
|
293
|
+
if (rawShipSubstate === "retro_review" || rawShipSubstate === "compound_review") {
|
|
294
|
+
shipSubstate = "post_ship_review";
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
shipSubstate = isShipSubstate(rawShipSubstate) ? rawShipSubstate : fallback.shipSubstate;
|
|
298
|
+
}
|
|
349
299
|
const retroDraftedAt = typeof typed.retroDraftedAt === "string" ? typed.retroDraftedAt : undefined;
|
|
350
300
|
const retroAcceptedAt = typeof typed.retroAcceptedAt === "string" ? typed.retroAcceptedAt : undefined;
|
|
351
301
|
const retroSkipReason = typeof typed.retroSkipReason === "string"
|
|
@@ -370,21 +320,8 @@ function sanitizeCloseoutState(value, onDemotion) {
|
|
|
370
320
|
// the compound leg, which would let `archive` skip durable closeout proof.
|
|
371
321
|
const retroDone = retroAcceptedAt !== undefined || retroSkipped === true;
|
|
372
322
|
const compoundDone = compoundCompletedAt !== undefined || compoundPromoted > 0 || compoundSkipped === true;
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
shipSubstate = "retro_review";
|
|
376
|
-
demotionReason = "retro closeout leg is incomplete (missing retroAcceptedAt or explicit retro skip).";
|
|
377
|
-
}
|
|
378
|
-
else if (shipSubstate === "ready_to_archive" && !compoundDone) {
|
|
379
|
-
shipSubstate = "compound_review";
|
|
380
|
-
demotionReason = "compound closeout leg is incomplete (missing compound proof).";
|
|
381
|
-
}
|
|
382
|
-
if (demotionReason && previousShipSubstate !== shipSubstate) {
|
|
383
|
-
onDemotion?.({
|
|
384
|
-
previous: previousShipSubstate,
|
|
385
|
-
next: shipSubstate,
|
|
386
|
-
reason: demotionReason
|
|
387
|
-
});
|
|
323
|
+
if (shipSubstate === "ready_to_archive" && (!retroDone || !compoundDone)) {
|
|
324
|
+
shipSubstate = "post_ship_review";
|
|
388
325
|
}
|
|
389
326
|
return {
|
|
390
327
|
shipSubstate,
|
|
@@ -405,7 +342,6 @@ function coerceFlowState(parsed) {
|
|
|
405
342
|
const activeRunId = typeof activeRunIdRaw === "string" && activeRunIdRaw.trim().length > 0
|
|
406
343
|
? activeRunIdRaw.trim()
|
|
407
344
|
: next.activeRunId;
|
|
408
|
-
let closeoutDemotion;
|
|
409
345
|
const state = {
|
|
410
346
|
schemaVersion: FLOW_STATE_SCHEMA_VERSION,
|
|
411
347
|
activeRunId,
|
|
@@ -417,12 +353,11 @@ function coerceFlowState(parsed) {
|
|
|
417
353
|
skippedStages: sanitizeSkippedStages(parsed.skippedStages, track),
|
|
418
354
|
staleStages: sanitizeStaleStages(parsed.staleStages),
|
|
419
355
|
rewinds: sanitizeRewinds(parsed.rewinds),
|
|
356
|
+
interactionHints: sanitizeInteractionHints(parsed.interactionHints),
|
|
420
357
|
retro: sanitizeRetroState(parsed.retro),
|
|
421
|
-
closeout: sanitizeCloseoutState(parsed.closeout
|
|
422
|
-
closeoutDemotion = demotion;
|
|
423
|
-
})
|
|
358
|
+
closeout: sanitizeCloseoutState(parsed.closeout)
|
|
424
359
|
};
|
|
425
|
-
return { state
|
|
360
|
+
return { state };
|
|
426
361
|
}
|
|
427
362
|
export class CorruptFlowStateError extends Error {
|
|
428
363
|
statePath;
|
|
@@ -484,11 +419,7 @@ export async function readFlowState(projectRoot, options = {}) {
|
|
|
484
419
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
485
420
|
await quarantineCorruptState(statePath, new Error("flow-state.json did not deserialize to a JSON object"));
|
|
486
421
|
}
|
|
487
|
-
|
|
488
|
-
if (coerced.closeoutDemotion) {
|
|
489
|
-
await appendCloseoutSubstateDemotionNotice(projectRoot, coerced.state, coerced.closeoutDemotion);
|
|
490
|
-
}
|
|
491
|
-
return coerced.state;
|
|
422
|
+
return coerceFlowState(parsed).state;
|
|
492
423
|
}
|
|
493
424
|
export async function writeFlowState(projectRoot, state, options = {}) {
|
|
494
425
|
const doWrite = async () => {
|
|
@@ -527,12 +458,13 @@ export async function writeFlowState(projectRoot, state, options = {}) {
|
|
|
527
458
|
export function flowStateLockPathFor(projectRoot) {
|
|
528
459
|
return flowStateLockPath(projectRoot);
|
|
529
460
|
}
|
|
530
|
-
export async function ensureRunSystem(projectRoot,
|
|
461
|
+
export async function ensureRunSystem(projectRoot, options = {}) {
|
|
531
462
|
await ensureDir(archiveRoot(projectRoot));
|
|
532
463
|
await ensureDir(activeArtifactsPath(projectRoot));
|
|
533
464
|
const statePath = flowStatePath(projectRoot);
|
|
534
465
|
const state = await readFlowState(projectRoot);
|
|
535
|
-
|
|
466
|
+
const createIfMissing = options.createIfMissing !== false;
|
|
467
|
+
if (createIfMissing && !(await exists(statePath))) {
|
|
536
468
|
await writeFlowState(projectRoot, state, { allowReset: true });
|
|
537
469
|
}
|
|
538
470
|
return state;
|