cclaw-cli 0.49.0 → 0.51.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 +54 -82
- package/dist/artifact-linter.d.ts +4 -0
- package/dist/artifact-linter.js +24 -3
- package/dist/cli.d.ts +1 -19
- package/dist/cli.js +49 -491
- package/dist/constants.d.ts +2 -13
- package/dist/constants.js +1 -43
- package/dist/content/closeout-guidance.d.ts +14 -0
- package/dist/content/closeout-guidance.js +42 -0
- package/dist/content/core-agents.js +51 -9
- package/dist/content/decision-protocol.d.ts +12 -0
- package/dist/content/decision-protocol.js +20 -0
- package/dist/content/diff-command.d.ts +1 -2
- package/dist/content/diff-command.js +8 -94
- package/dist/content/examples.d.ts +4 -10
- package/dist/content/examples.js +10 -20
- package/dist/content/hook-events.js +2 -2
- package/dist/content/hook-inline-snippets.d.ts +5 -2
- package/dist/content/hook-inline-snippets.js +33 -1
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +11 -12
- package/dist/content/hooks.js +2 -0
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +31 -25
- package/dist/content/iron-laws.d.ts +5 -5
- package/dist/content/iron-laws.js +5 -5
- package/dist/content/learnings.d.ts +3 -4
- package/dist/content/learnings.js +24 -50
- package/dist/content/meta-skill.js +31 -21
- package/dist/content/next-command.js +38 -38
- package/dist/content/node-hooks.js +17 -343
- package/dist/content/opencode-plugin.js +2 -100
- package/dist/content/research-playbooks.js +14 -14
- package/dist/content/review-loop.d.ts +2 -0
- package/dist/content/review-loop.js +8 -0
- package/dist/content/session-hooks.js +14 -46
- package/dist/content/skills.d.ts +0 -5
- package/dist/content/skills.js +53 -128
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +15 -14
- package/dist/content/stage-schema.d.ts +26 -1
- package/dist/content/stage-schema.js +121 -40
- package/dist/content/stages/_lint-metadata/index.js +9 -15
- package/dist/content/stages/brainstorm.js +22 -43
- package/dist/content/stages/design.js +37 -57
- package/dist/content/stages/plan.js +22 -13
- package/dist/content/stages/review.js +24 -27
- package/dist/content/stages/scope.js +34 -46
- package/dist/content/stages/ship.js +7 -4
- package/dist/content/stages/spec.js +20 -9
- package/dist/content/stages/tdd.js +64 -44
- package/dist/content/start-command.js +10 -12
- package/dist/content/status-command.d.ts +2 -7
- package/dist/content/status-command.js +19 -146
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +47 -28
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +126 -135
- package/dist/content/track-render-context.d.ts +17 -0
- package/dist/content/track-render-context.js +44 -0
- package/dist/content/tree-command.d.ts +1 -2
- package/dist/content/tree-command.js +4 -87
- package/dist/content/utility-skills.d.ts +2 -29
- package/dist/content/utility-skills.js +2 -1534
- package/dist/content/view-command.js +29 -11
- package/dist/delegation.d.ts +1 -1
- package/dist/delegation.js +5 -15
- package/dist/doctor-registry.js +20 -21
- package/dist/doctor.js +88 -344
- package/dist/flow-state.d.ts +3 -0
- package/dist/flow-state.js +2 -0
- package/dist/harness-adapters.d.ts +1 -1
- package/dist/harness-adapters.js +48 -57
- package/dist/install.js +128 -358
- package/dist/internal/advance-stage.js +3 -9
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +1 -1
- package/dist/internal/tdd-loop-status.d.ts +1 -1
- package/dist/internal/tdd-loop-status.js +1 -1
- package/dist/knowledge-store.d.ts +16 -10
- package/dist/knowledge-store.js +51 -15
- package/dist/policy.js +16 -105
- package/dist/run-archive.d.ts +4 -6
- package/dist/run-archive.js +15 -20
- package/dist/run-persistence.d.ts +2 -2
- package/dist/run-persistence.js +3 -9
- package/package.json +1 -2
- package/dist/content/archive-command.d.ts +0 -2
- package/dist/content/archive-command.js +0 -124
- package/dist/content/compound-command.d.ts +0 -5
- package/dist/content/compound-command.js +0 -193
- package/dist/content/contexts.d.ts +0 -18
- package/dist/content/contexts.js +0 -24
- package/dist/content/contracts.d.ts +0 -2
- package/dist/content/contracts.js +0 -51
- package/dist/content/doctor-references.d.ts +0 -2
- package/dist/content/doctor-references.js +0 -150
- package/dist/content/eval-scaffold.d.ts +0 -15
- package/dist/content/eval-scaffold.js +0 -370
- package/dist/content/feature-command.d.ts +0 -2
- package/dist/content/feature-command.js +0 -123
- package/dist/content/flow-map.d.ts +0 -23
- package/dist/content/flow-map.js +0 -134
- package/dist/content/harness-doc.d.ts +0 -2
- package/dist/content/harness-doc.js +0 -202
- package/dist/content/harness-playbooks.d.ts +0 -24
- package/dist/content/harness-playbooks.js +0 -393
- package/dist/content/harness-tool-refs.d.ts +0 -20
- package/dist/content/harness-tool-refs.js +0 -268
- package/dist/content/ops-command.d.ts +0 -2
- package/dist/content/ops-command.js +0 -71
- package/dist/content/protocols.d.ts +0 -7
- package/dist/content/protocols.js +0 -215
- package/dist/content/retro-command.d.ts +0 -2
- package/dist/content/retro-command.js +0 -165
- package/dist/content/rewind-command.d.ts +0 -2
- package/dist/content/rewind-command.js +0 -106
- package/dist/content/tdd-log-command.d.ts +0 -2
- package/dist/content/tdd-log-command.js +0 -85
- package/dist/eval/agents/single-shot.d.ts +0 -27
- package/dist/eval/agents/single-shot.js +0 -79
- package/dist/eval/agents/with-tools.d.ts +0 -44
- package/dist/eval/agents/with-tools.js +0 -261
- package/dist/eval/agents/workflow.d.ts +0 -31
- package/dist/eval/agents/workflow.js +0 -155
- package/dist/eval/baseline.d.ts +0 -38
- package/dist/eval/baseline.js +0 -282
- package/dist/eval/config-loader.d.ts +0 -14
- package/dist/eval/config-loader.js +0 -395
- package/dist/eval/corpus.d.ts +0 -30
- package/dist/eval/corpus.js +0 -330
- package/dist/eval/cost-guard.d.ts +0 -102
- package/dist/eval/cost-guard.js +0 -190
- package/dist/eval/diff.d.ts +0 -64
- package/dist/eval/diff.js +0 -323
- package/dist/eval/llm-client.d.ts +0 -176
- package/dist/eval/llm-client.js +0 -267
- package/dist/eval/mode.d.ts +0 -28
- package/dist/eval/mode.js +0 -61
- package/dist/eval/progress.d.ts +0 -83
- package/dist/eval/progress.js +0 -59
- package/dist/eval/report.d.ts +0 -11
- package/dist/eval/report.js +0 -181
- package/dist/eval/rubric-loader.d.ts +0 -20
- package/dist/eval/rubric-loader.js +0 -143
- package/dist/eval/runner.d.ts +0 -81
- package/dist/eval/runner.js +0 -746
- package/dist/eval/runs.d.ts +0 -41
- package/dist/eval/runs.js +0 -114
- package/dist/eval/sandbox.d.ts +0 -38
- package/dist/eval/sandbox.js +0 -137
- package/dist/eval/tools/glob.d.ts +0 -2
- package/dist/eval/tools/glob.js +0 -163
- package/dist/eval/tools/grep.d.ts +0 -2
- package/dist/eval/tools/grep.js +0 -152
- package/dist/eval/tools/index.d.ts +0 -7
- package/dist/eval/tools/index.js +0 -35
- package/dist/eval/tools/read.d.ts +0 -2
- package/dist/eval/tools/read.js +0 -122
- package/dist/eval/tools/types.d.ts +0 -49
- package/dist/eval/tools/types.js +0 -41
- package/dist/eval/tools/write.d.ts +0 -2
- package/dist/eval/tools/write.js +0 -92
- package/dist/eval/types.d.ts +0 -561
- package/dist/eval/types.js +0 -47
- package/dist/eval/verifiers/judge.d.ts +0 -40
- package/dist/eval/verifiers/judge.js +0 -256
- package/dist/eval/verifiers/rules.d.ts +0 -24
- package/dist/eval/verifiers/rules.js +0 -218
- package/dist/eval/verifiers/structural.d.ts +0 -14
- package/dist/eval/verifiers/structural.js +0 -171
- package/dist/eval/verifiers/traceability.d.ts +0 -23
- package/dist/eval/verifiers/traceability.js +0 -84
- package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
- package/dist/eval/verifiers/workflow-consistency.js +0 -225
- package/dist/eval/workflow-corpus.d.ts +0 -7
- package/dist/eval/workflow-corpus.js +0 -207
- package/dist/feature-system.d.ts +0 -42
- package/dist/feature-system.js +0 -432
- package/dist/internal/knowledge-digest.d.ts +0 -7
- package/dist/internal/knowledge-digest.js +0 -93
|
@@ -13,7 +13,7 @@ interface InternalIo {
|
|
|
13
13
|
*/
|
|
14
14
|
export declare function countArchivedRunsSafely(projectRoot: string): Promise<number | undefined>;
|
|
15
15
|
/**
|
|
16
|
-
* Compact one-liner for
|
|
16
|
+
* Compact one-liner for bootstrap surfaces.
|
|
17
17
|
*
|
|
18
18
|
* Example: `Compound readiness: clusters=12, ready=2 (critical=1)`.
|
|
19
19
|
* When `ready === 0`, emit `Compound readiness: no candidates`.
|
|
@@ -65,7 +65,7 @@ export async function countArchivedRunsSafely(projectRoot) {
|
|
|
65
65
|
}
|
|
66
66
|
}
|
|
67
67
|
/**
|
|
68
|
-
* Compact one-liner for
|
|
68
|
+
* Compact one-liner for bootstrap surfaces.
|
|
69
69
|
*
|
|
70
70
|
* Example: `Compound readiness: clusters=12, ready=2 (critical=1)`.
|
|
71
71
|
* When `ready === 0`, emit `Compound readiness: no candidates`.
|
|
@@ -6,7 +6,7 @@ interface InternalIo {
|
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
8
|
* Produces a one-line "Ralph Loop: iter=X, slices=Y, acClosed=Z, redOpen=..."
|
|
9
|
-
* summary — suitable for
|
|
9
|
+
* summary — suitable for bootstrap surfaces where the user
|
|
10
10
|
* just needs a progress indicator, not the full slice breakdown.
|
|
11
11
|
*/
|
|
12
12
|
export declare function formatRalphLoopStatusLine(status: RalphLoopStatus): string;
|
|
@@ -36,7 +36,7 @@ async function readCycleLog(projectRoot) {
|
|
|
36
36
|
}
|
|
37
37
|
/**
|
|
38
38
|
* Produces a one-line "Ralph Loop: iter=X, slices=Y, acClosed=Z, redOpen=..."
|
|
39
|
-
* summary — suitable for
|
|
39
|
+
* summary — suitable for bootstrap surfaces where the user
|
|
40
40
|
* just needs a progress indicator, not the full slice breakdown.
|
|
41
41
|
*/
|
|
42
42
|
export function formatRalphLoopStatusLine(status) {
|
|
@@ -14,7 +14,7 @@ export interface KnowledgeEntry {
|
|
|
14
14
|
domain: string | null;
|
|
15
15
|
stage: FlowStage | null;
|
|
16
16
|
origin_stage: FlowStage | null;
|
|
17
|
-
|
|
17
|
+
origin_run: string | null;
|
|
18
18
|
frequency: number;
|
|
19
19
|
universality: KnowledgeEntryUniversality;
|
|
20
20
|
maturity: KnowledgeEntryMaturity;
|
|
@@ -23,6 +23,8 @@ export interface KnowledgeEntry {
|
|
|
23
23
|
last_seen_ts: string;
|
|
24
24
|
project: string | null;
|
|
25
25
|
source?: KnowledgeEntrySource | null;
|
|
26
|
+
supersedes?: string[];
|
|
27
|
+
superseded_by?: string;
|
|
26
28
|
}
|
|
27
29
|
export interface KnowledgeSeedEntry {
|
|
28
30
|
type: KnowledgeEntryType;
|
|
@@ -33,6 +35,8 @@ export interface KnowledgeSeedEntry {
|
|
|
33
35
|
domain?: string | null;
|
|
34
36
|
stage?: FlowStage | null;
|
|
35
37
|
origin_stage?: FlowStage | null;
|
|
38
|
+
origin_run?: string | null;
|
|
39
|
+
/** @deprecated Use `origin_run`. Accepted only for legacy JSONL/backfill inputs. */
|
|
36
40
|
origin_feature?: string | null;
|
|
37
41
|
frequency?: number;
|
|
38
42
|
universality?: KnowledgeEntryUniversality;
|
|
@@ -42,11 +46,13 @@ export interface KnowledgeSeedEntry {
|
|
|
42
46
|
last_seen_ts?: string;
|
|
43
47
|
project?: string | null;
|
|
44
48
|
source?: KnowledgeEntrySource | null;
|
|
49
|
+
supersedes?: string[];
|
|
50
|
+
superseded_by?: string;
|
|
45
51
|
}
|
|
46
52
|
export interface AppendKnowledgeDefaults {
|
|
47
53
|
stage?: FlowStage | null;
|
|
48
54
|
originStage?: FlowStage | null;
|
|
49
|
-
|
|
55
|
+
originRun?: string | null;
|
|
50
56
|
project?: string | null;
|
|
51
57
|
source?: KnowledgeEntrySource | null;
|
|
52
58
|
nowIso?: string;
|
|
@@ -84,7 +90,7 @@ export interface CompoundReadinessCluster {
|
|
|
84
90
|
action: string;
|
|
85
91
|
/**
|
|
86
92
|
* Sum of `frequency` across entries in the cluster — matches the
|
|
87
|
-
* recurrence count used by
|
|
93
|
+
* recurrence count used by compound readiness analysis.
|
|
88
94
|
*/
|
|
89
95
|
recurrence: number;
|
|
90
96
|
/** Distinct entry lines contributing to this cluster. */
|
|
@@ -140,16 +146,15 @@ export interface ComputeCompoundReadinessOptions {
|
|
|
140
146
|
* Count of archived runs under `.cclaw/runs/`. When supplied and
|
|
141
147
|
* `< SMALL_PROJECT_ARCHIVE_RUNS_THRESHOLD`, the effective threshold
|
|
142
148
|
* is lowered to `min(threshold, SMALL_PROJECT_RECURRENCE_THRESHOLD)`.
|
|
143
|
-
* Matches the rule documented in `
|
|
144
|
-
* and `docs/config.md`.
|
|
149
|
+
* Matches the rule documented in `docs/config.md`.
|
|
145
150
|
*/
|
|
146
151
|
archivedRunsCount?: number;
|
|
147
152
|
}
|
|
148
153
|
/**
|
|
149
154
|
* Single source of truth for the small-project relaxation rule.
|
|
150
155
|
*
|
|
151
|
-
* Kept exported so the inline hook mirror
|
|
152
|
-
* the
|
|
156
|
+
* Kept exported so the inline hook mirror and CLI/runtime paths all agree on
|
|
157
|
+
* the same numbers.
|
|
153
158
|
*/
|
|
154
159
|
export declare const SMALL_PROJECT_ARCHIVE_RUNS_THRESHOLD = 5;
|
|
155
160
|
export declare const SMALL_PROJECT_RECURRENCE_THRESHOLD = 2;
|
|
@@ -163,9 +168,10 @@ export declare function effectiveCompoundThreshold(baseThreshold: number, archiv
|
|
|
163
168
|
* for persisting to `.cclaw/state/compound-readiness.json`.
|
|
164
169
|
*
|
|
165
170
|
* Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
|
|
166
|
-
* which mirrors the
|
|
167
|
-
* Entries with `maturity === "lifted-to-enforcement"`
|
|
168
|
-
* they were already promoted and should not re-appear
|
|
171
|
+
* which mirrors the compound readiness clustering in runtime state.
|
|
172
|
+
* Entries with `maturity === "lifted-to-enforcement"` or `superseded_by`
|
|
173
|
+
* are excluded — they were already promoted/replaced and should not re-appear
|
|
174
|
+
* as ready.
|
|
169
175
|
*/
|
|
170
176
|
export declare function computeCompoundReadiness(entries: KnowledgeEntry[], options?: ComputeCompoundReadinessOptions): CompoundReadiness;
|
|
171
177
|
export declare function validateKnowledgeEntry(entry: unknown): {
|
package/dist/knowledge-store.js
CHANGED
|
@@ -8,8 +8,8 @@ const DEFAULT_COMPOUND_READINESS_MAX_READY = 10;
|
|
|
8
8
|
/**
|
|
9
9
|
* Single source of truth for the small-project relaxation rule.
|
|
10
10
|
*
|
|
11
|
-
* Kept exported so the inline hook mirror
|
|
12
|
-
* the
|
|
11
|
+
* Kept exported so the inline hook mirror and CLI/runtime paths all agree on
|
|
12
|
+
* the same numbers.
|
|
13
13
|
*/
|
|
14
14
|
export const SMALL_PROJECT_ARCHIVE_RUNS_THRESHOLD = 5;
|
|
15
15
|
export const SMALL_PROJECT_RECURRENCE_THRESHOLD = 2;
|
|
@@ -31,9 +31,10 @@ export function effectiveCompoundThreshold(baseThreshold, archivedRunsCount) {
|
|
|
31
31
|
* for persisting to `.cclaw/state/compound-readiness.json`.
|
|
32
32
|
*
|
|
33
33
|
* Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
|
|
34
|
-
* which mirrors the
|
|
35
|
-
* Entries with `maturity === "lifted-to-enforcement"`
|
|
36
|
-
* they were already promoted and should not re-appear
|
|
34
|
+
* which mirrors the compound readiness clustering in runtime state.
|
|
35
|
+
* Entries with `maturity === "lifted-to-enforcement"` or `superseded_by`
|
|
36
|
+
* are excluded — they were already promoted/replaced and should not re-appear
|
|
37
|
+
* as ready.
|
|
37
38
|
*/
|
|
38
39
|
export function computeCompoundReadiness(entries, options = {}) {
|
|
39
40
|
const thresholdRaw = options.threshold ?? DEFAULT_COMPOUND_RECURRENCE_THRESHOLD;
|
|
@@ -53,7 +54,7 @@ export function computeCompoundReadiness(entries, options = {}) {
|
|
|
53
54
|
const { threshold, relaxationApplied } = effectiveCompoundThreshold(baseThreshold, archivedRunsCount);
|
|
54
55
|
const buckets = new Map();
|
|
55
56
|
for (const entry of entries) {
|
|
56
|
-
if (entry.maturity === "lifted-to-enforcement")
|
|
57
|
+
if (entry.maturity === "lifted-to-enforcement" || entry.superseded_by !== undefined)
|
|
57
58
|
continue;
|
|
58
59
|
const key = [
|
|
59
60
|
entry.type,
|
|
@@ -160,7 +161,7 @@ const KNOWLEDGE_REQUIRED_KEYS = [
|
|
|
160
161
|
"domain",
|
|
161
162
|
"stage",
|
|
162
163
|
"origin_stage",
|
|
163
|
-
"
|
|
164
|
+
"origin_run",
|
|
164
165
|
"frequency",
|
|
165
166
|
"universality",
|
|
166
167
|
"maturity",
|
|
@@ -170,8 +171,11 @@ const KNOWLEDGE_REQUIRED_KEYS = [
|
|
|
170
171
|
"project"
|
|
171
172
|
];
|
|
172
173
|
const KNOWLEDGE_ALLOWED_KEYS = new Set(KNOWLEDGE_REQUIRED_KEYS);
|
|
174
|
+
KNOWLEDGE_ALLOWED_KEYS.add("origin_feature");
|
|
173
175
|
KNOWLEDGE_ALLOWED_KEYS.add("source");
|
|
174
176
|
KNOWLEDGE_ALLOWED_KEYS.add("severity");
|
|
177
|
+
KNOWLEDGE_ALLOWED_KEYS.add("supersedes");
|
|
178
|
+
KNOWLEDGE_ALLOWED_KEYS.add("superseded_by");
|
|
175
179
|
function knowledgePath(projectRoot) {
|
|
176
180
|
return path.join(projectRoot, RUNTIME_ROOT, "knowledge.jsonl");
|
|
177
181
|
}
|
|
@@ -195,11 +199,13 @@ function dedupeKey(entry) {
|
|
|
195
199
|
entry.domain === null ? "null" : normalizeText(entry.domain),
|
|
196
200
|
entry.stage ?? "null",
|
|
197
201
|
entry.origin_stage ?? "null",
|
|
198
|
-
entry.
|
|
202
|
+
entry.origin_run === null ? "null" : normalizeText(entry.origin_run),
|
|
199
203
|
entry.universality,
|
|
200
204
|
entry.project === null ? "null" : normalizeText(entry.project),
|
|
201
205
|
entry.source === undefined || entry.source === null ? "null" : entry.source,
|
|
202
|
-
entry.severity === undefined ? "none" : entry.severity
|
|
206
|
+
entry.severity === undefined ? "none" : entry.severity,
|
|
207
|
+
Array.isArray(entry.supersedes) ? entry.supersedes.map(normalizeText).sort().join(",") : "none",
|
|
208
|
+
entry.superseded_by === undefined ? "none" : normalizeText(entry.superseded_by)
|
|
203
209
|
].join("|");
|
|
204
210
|
}
|
|
205
211
|
function emptyKnowledgeSnapshot() {
|
|
@@ -211,6 +217,13 @@ function emptyKnowledgeSnapshot() {
|
|
|
211
217
|
entryByIndex: new Map()
|
|
212
218
|
};
|
|
213
219
|
}
|
|
220
|
+
function normalizeLegacyKnowledgeEntry(entry) {
|
|
221
|
+
const { origin_feature: legacyOriginRun, ...rest } = entry;
|
|
222
|
+
return {
|
|
223
|
+
...rest,
|
|
224
|
+
origin_run: entry.origin_run ?? legacyOriginRun ?? null
|
|
225
|
+
};
|
|
226
|
+
}
|
|
214
227
|
function parseKnowledgeSnapshot(raw) {
|
|
215
228
|
const lines = stripBom(raw).split(/\r?\n/u);
|
|
216
229
|
const entries = [];
|
|
@@ -228,7 +241,7 @@ function parseKnowledgeSnapshot(raw) {
|
|
|
228
241
|
malformedLines += 1;
|
|
229
242
|
continue;
|
|
230
243
|
}
|
|
231
|
-
const entry = parsed;
|
|
244
|
+
const entry = normalizeLegacyKnowledgeEntry(parsed);
|
|
232
245
|
entries.push(entry);
|
|
233
246
|
const key = dedupeKey(entry);
|
|
234
247
|
if (!keyToIndex.has(key)) {
|
|
@@ -294,7 +307,9 @@ export function validateKnowledgeEntry(entry) {
|
|
|
294
307
|
}
|
|
295
308
|
for (const key of KNOWLEDGE_REQUIRED_KEYS) {
|
|
296
309
|
if (!Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
297
|
-
|
|
310
|
+
if (key !== "origin_run" || !Object.prototype.hasOwnProperty.call(obj, "origin_feature")) {
|
|
311
|
+
errors.push(`Missing required key "${key}".`);
|
|
312
|
+
}
|
|
298
313
|
}
|
|
299
314
|
}
|
|
300
315
|
if (!KNOWLEDGE_TYPE_SET.has(obj.type)) {
|
|
@@ -322,8 +337,11 @@ export function validateKnowledgeEntry(entry) {
|
|
|
322
337
|
if (!isNullableStage(obj.origin_stage)) {
|
|
323
338
|
errors.push(`origin_stage must be one of ${FLOW_STAGES.join(", ")} or null.`);
|
|
324
339
|
}
|
|
325
|
-
|
|
326
|
-
|
|
340
|
+
const originRun = Object.prototype.hasOwnProperty.call(obj, "origin_run")
|
|
341
|
+
? obj.origin_run
|
|
342
|
+
: obj.origin_feature;
|
|
343
|
+
if (!isNullableString(originRun)) {
|
|
344
|
+
errors.push("origin_run must be string or null.");
|
|
327
345
|
}
|
|
328
346
|
if (typeof obj.frequency !== "number" ||
|
|
329
347
|
!Number.isInteger(obj.frequency) ||
|
|
@@ -345,6 +363,17 @@ export function validateKnowledgeEntry(entry) {
|
|
|
345
363
|
if (!isNullableString(obj.project)) {
|
|
346
364
|
errors.push("project must be string or null.");
|
|
347
365
|
}
|
|
366
|
+
if (obj.supersedes !== undefined) {
|
|
367
|
+
if (!Array.isArray(obj.supersedes) ||
|
|
368
|
+
obj.supersedes.length === 0 ||
|
|
369
|
+
obj.supersedes.some((value) => typeof value !== "string" || value.trim().length === 0)) {
|
|
370
|
+
errors.push("supersedes must be a non-empty array of strings when present.");
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
if (obj.superseded_by !== undefined &&
|
|
374
|
+
(typeof obj.superseded_by !== "string" || obj.superseded_by.trim().length === 0)) {
|
|
375
|
+
errors.push("superseded_by must be a non-empty string when present.");
|
|
376
|
+
}
|
|
348
377
|
if (obj.source !== undefined &&
|
|
349
378
|
obj.source !== null &&
|
|
350
379
|
(typeof obj.source !== "string" || !KNOWLEDGE_SOURCE_SET.has(obj.source))) {
|
|
@@ -356,6 +385,7 @@ export function materializeKnowledgeEntry(seed, defaults = {}) {
|
|
|
356
385
|
const now = normalizeUtcIso(defaults.nowIso ?? nowUtcIso());
|
|
357
386
|
const stage = seed.stage ?? defaults.stage ?? null;
|
|
358
387
|
const originStage = seed.origin_stage ?? defaults.originStage ?? stage ?? null;
|
|
388
|
+
const originRun = seed.origin_run ?? seed.origin_feature ?? defaults.originRun ?? null;
|
|
359
389
|
const source = seed.source ?? defaults.source ?? null;
|
|
360
390
|
const entry = {
|
|
361
391
|
type: seed.type,
|
|
@@ -365,7 +395,7 @@ export function materializeKnowledgeEntry(seed, defaults = {}) {
|
|
|
365
395
|
domain: seed.domain ?? null,
|
|
366
396
|
stage,
|
|
367
397
|
origin_stage: originStage,
|
|
368
|
-
|
|
398
|
+
origin_run: originRun,
|
|
369
399
|
frequency: seed.frequency ?? 1,
|
|
370
400
|
universality: seed.universality ?? "project",
|
|
371
401
|
maturity: seed.maturity ?? "raw",
|
|
@@ -377,6 +407,12 @@ export function materializeKnowledgeEntry(seed, defaults = {}) {
|
|
|
377
407
|
if (seed.severity !== undefined) {
|
|
378
408
|
entry.severity = seed.severity;
|
|
379
409
|
}
|
|
410
|
+
if (seed.supersedes !== undefined) {
|
|
411
|
+
entry.supersedes = seed.supersedes.map((value) => value.trim());
|
|
412
|
+
}
|
|
413
|
+
if (seed.superseded_by !== undefined) {
|
|
414
|
+
entry.superseded_by = seed.superseded_by.trim();
|
|
415
|
+
}
|
|
380
416
|
if (source !== null) {
|
|
381
417
|
entry.source = source;
|
|
382
418
|
}
|
|
@@ -521,7 +557,7 @@ export async function selectRelevantLearnings(projectRoot, options = {}) {
|
|
|
521
557
|
...tokenizeText(entry.domain),
|
|
522
558
|
...tokenizeText(entry.trigger),
|
|
523
559
|
...tokenizeText(entry.action),
|
|
524
|
-
...tokenizeText(entry.
|
|
560
|
+
...tokenizeText(entry.origin_run),
|
|
525
561
|
...tokenizeText(entry.project)
|
|
526
562
|
];
|
|
527
563
|
const searchSet = new Set(searchable);
|
package/dist/policy.js
CHANGED
|
@@ -2,7 +2,7 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
4
4
|
import { FLOW_STAGES } from "./types.js";
|
|
5
|
-
import { stageSchema
|
|
5
|
+
import { stageSchema } from "./content/stage-schema.js";
|
|
6
6
|
import { stageSkillFolder } from "./content/skills.js";
|
|
7
7
|
import { exists } from "./fs-utils.js";
|
|
8
8
|
const POLICY_RULES = [];
|
|
@@ -14,32 +14,11 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
14
14
|
for (const stage of FLOW_STAGES) {
|
|
15
15
|
const folder = stageSkillFolder(stage);
|
|
16
16
|
const schema = stageSchema(stage);
|
|
17
|
-
const commandFile = `${RUNTIME_ROOT}/commands/${stage}.md`;
|
|
18
17
|
const skillFile = `${RUNTIME_ROOT}/skills/${folder}/SKILL.md`;
|
|
19
|
-
// --- thin command mandatory sections ---
|
|
20
|
-
for (const heading of [
|
|
21
|
-
"## HARD-GATE",
|
|
22
|
-
"## Gates",
|
|
23
|
-
"## Exit",
|
|
24
|
-
"## Anchors",
|
|
25
|
-
"## Context Hydration"
|
|
26
|
-
]) {
|
|
27
|
-
rules.push({
|
|
28
|
-
filePath: commandFile,
|
|
29
|
-
needle: heading,
|
|
30
|
-
name: `command:${stage}:section:${heading.replace(/^## /, "").toLowerCase().replace(/[^a-z0-9]+/g, "_")}`
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
// --- command must reference the skill ---
|
|
34
|
-
rules.push({
|
|
35
|
-
filePath: commandFile,
|
|
36
|
-
needle: `${folder}/SKILL.md`,
|
|
37
|
-
name: `command:${stage}:skill_ref`
|
|
38
|
-
});
|
|
39
18
|
// --- skill mandatory sections ---
|
|
40
19
|
for (const heading of [
|
|
41
20
|
"## Process",
|
|
42
|
-
"##
|
|
21
|
+
"## Exit Criteria",
|
|
43
22
|
"## Interaction Protocol",
|
|
44
23
|
"## Anti-Patterns & Red Flags",
|
|
45
24
|
"## HARD-GATE",
|
|
@@ -73,50 +52,25 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
73
52
|
name: `skill:${stage}:section:verification_before_completion`
|
|
74
53
|
});
|
|
75
54
|
}
|
|
76
|
-
// --- policy needles in commands ---
|
|
77
|
-
for (const needle of stagePolicyNeedles(stage)) {
|
|
78
|
-
rules.push({
|
|
79
|
-
filePath: commandFile,
|
|
80
|
-
needle,
|
|
81
|
-
name: `command:${stage}:anchor:${needle.toLowerCase().replace(/[^a-z0-9]+/g, "_")}`
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
55
|
}
|
|
85
56
|
// --- utility skill checks ---
|
|
86
57
|
const runtimeFile = (relativePath) => `${RUNTIME_ROOT}/${relativePath}`;
|
|
87
58
|
const utilitySkillChecks = [
|
|
88
59
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "strict JSONL schema", name: "utility_skill:learnings:jsonl_schema" },
|
|
89
60
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "knowledge.jsonl", name: "utility_skill:learnings:jsonl_store" },
|
|
90
|
-
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "type, trigger, action, confidence, domain, stage, origin_stage,
|
|
91
|
-
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "##
|
|
61
|
+
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "type, trigger, action, confidence, domain, stage, origin_stage, origin_run, frequency, universality, maturity, created, first_seen_ts, last_seen_ts, project", name: "utility_skill:learnings:field_order" },
|
|
62
|
+
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "## Manual Actions", name: "utility_skill:learnings:manual_actions" },
|
|
92
63
|
{ file: runtimeFile("skills/learnings/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:learnings:hard_gate" },
|
|
93
|
-
{ file: runtimeFile("commands/
|
|
64
|
+
{ file: runtimeFile("commands/start.md"), needle: "## Algorithm", name: "utility_command:start:algorithm" },
|
|
65
|
+
{ file: runtimeFile("commands/next.md"), needle: "## Algorithm", name: "utility_command:next:algorithm" },
|
|
66
|
+
{ file: runtimeFile("commands/next.md"), needle: "closeout.shipSubstate", name: "utility_command:next:closeout_chain" },
|
|
94
67
|
{ file: runtimeFile("commands/ideate.md"), needle: "## Algorithm", name: "utility_command:ideate:algorithm" },
|
|
95
68
|
{ file: runtimeFile("skills/flow-ideate/SKILL.md"), needle: "## Protocol", name: "utility_skill:ideate:protocol" },
|
|
96
69
|
{ file: runtimeFile("skills/flow-ideate/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:ideate:hard_gate" },
|
|
97
|
-
{ file: runtimeFile("commands/
|
|
98
|
-
{ file: runtimeFile("
|
|
99
|
-
{ file: runtimeFile("
|
|
100
|
-
{ file: runtimeFile("skills/flow-
|
|
101
|
-
{ file: runtimeFile("skills/flow-tree/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:tree:hard_gate" },
|
|
102
|
-
{ file: runtimeFile("commands/diff.md"), needle: "## Algorithm", name: "utility_command:diff:algorithm" },
|
|
103
|
-
{ file: runtimeFile("skills/flow-diff/SKILL.md"), needle: "## Protocol", name: "utility_skill:diff:protocol" },
|
|
104
|
-
{ file: runtimeFile("skills/flow-diff/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:diff:hard_gate" },
|
|
105
|
-
{ file: runtimeFile("commands/feature.md"), needle: "## Subcommands", name: "utility_command:feature:subcommands" },
|
|
106
|
-
{ file: runtimeFile("skills/using-git-worktrees/SKILL.md"), needle: "## Protocol", name: "utility_skill:feature:protocol" },
|
|
107
|
-
{ file: runtimeFile("skills/using-git-worktrees/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:feature:hard_gate" },
|
|
108
|
-
{ file: runtimeFile("commands/tdd-log.md"), needle: "## Subcommands", name: "utility_command:tdd_log:subcommands" },
|
|
109
|
-
{ file: runtimeFile("skills/tdd-cycle-log/SKILL.md"), needle: "## Protocol", name: "utility_skill:tdd_log:protocol" },
|
|
110
|
-
{ file: runtimeFile("skills/tdd-cycle-log/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:tdd_log:hard_gate" },
|
|
111
|
-
{ file: runtimeFile("commands/retro.md"), needle: "## Algorithm", name: "utility_command:retro:algorithm" },
|
|
112
|
-
{ file: runtimeFile("skills/flow-retro/SKILL.md"), needle: "## Protocol", name: "utility_skill:retro:protocol" },
|
|
113
|
-
{ file: runtimeFile("skills/flow-retro/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:retro:hard_gate" },
|
|
114
|
-
{ file: runtimeFile("commands/compound.md"), needle: "## Algorithm", name: "utility_command:compound:algorithm" },
|
|
115
|
-
{ file: runtimeFile("skills/flow-compound/SKILL.md"), needle: "## Protocol", name: "utility_skill:compound:protocol" },
|
|
116
|
-
{ file: runtimeFile("skills/flow-compound/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:compound:hard_gate" },
|
|
117
|
-
{ file: runtimeFile("commands/rewind.md"), needle: "## Algorithm", name: "utility_command:rewind:algorithm" },
|
|
118
|
-
{ file: runtimeFile("skills/flow-rewind/SKILL.md"), needle: "## Protocol", name: "utility_skill:rewind:protocol" },
|
|
119
|
-
{ file: runtimeFile("skills/flow-rewind/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:rewind:hard_gate" },
|
|
70
|
+
{ file: runtimeFile("commands/view.md"), needle: "## Routing", name: "utility_command:view:routing" },
|
|
71
|
+
{ file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Status Subcommand", name: "utility_skill:view:status_section" },
|
|
72
|
+
{ file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Tree Subcommand", name: "utility_skill:view:tree_section" },
|
|
73
|
+
{ file: runtimeFile("skills/flow-view/SKILL.md"), needle: "## Diff Subcommand", name: "utility_skill:view:diff_section" },
|
|
120
74
|
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:sdd:hard_gate" },
|
|
121
75
|
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "## Status Contract", name: "utility_skill:sdd:status_contract" },
|
|
122
76
|
{ file: runtimeFile("skills/subagent-dev/SKILL.md"), needle: "Implementer", name: "utility_skill:sdd:implementer_template" },
|
|
@@ -136,57 +90,14 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
136
90
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Routing flow", name: "meta_skill:routing_flow" },
|
|
137
91
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Task classification", name: "meta_skill:task_classification" },
|
|
138
92
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Stage quick map", name: "meta_skill:stage_quick_map" },
|
|
139
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "##
|
|
140
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "
|
|
93
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Whole flow map", name: "meta_skill:whole_flow_map" },
|
|
94
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "retro -> compound -> archive", name: "meta_skill:closeout_chain" },
|
|
95
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Contextual Skill Activation", name: "meta_skill:contextual_skills" },
|
|
96
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Protocol Behavior", name: "meta_skill:protocol_behavior" },
|
|
141
97
|
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Failure guardrails", name: "meta_skill:failure_guardrails" },
|
|
142
|
-
{ file: runtimeFile("references/protocols/decision.md"), needle: "# Decision Protocol", name: "protocol:decision" },
|
|
143
|
-
{ file: runtimeFile("references/protocols/completion.md"), needle: "# Stage Completion Protocol", name: "protocol:completion" },
|
|
144
|
-
{ file: runtimeFile("references/protocols/ethos.md"), needle: "# Engineering Ethos", name: "protocol:ethos" },
|
|
145
|
-
{ file: runtimeFile("references/flow-map.md"), needle: "# cclaw Flow Map", name: "reference:flow_map:header" },
|
|
146
|
-
{ file: runtimeFile("references/flow-map.md"), needle: "## Stages (8)", name: "reference:flow_map:stages" },
|
|
147
|
-
{ file: runtimeFile("references/flow-map.md"), needle: "## Ralph Loop", name: "reference:flow_map:ralph_loop" },
|
|
148
|
-
{ file: runtimeFile("references/flow-map.md"), needle: "## Key state files", name: "reference:flow_map:state_files" },
|
|
149
|
-
{ file: runtimeFile("references/flow-map.md"), needle: "## Compound readiness", name: "reference:flow_map:compound_readiness" },
|
|
150
98
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Resume Protocol", name: "utility_skill:session:resume" },
|
|
151
|
-
{ file: runtimeFile("skills/brainstorming/SKILL.md"), needle: "
|
|
152
|
-
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:security:hard_gate" },
|
|
153
|
-
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## Checklist", name: "utility_skill:security:checklist" },
|
|
154
|
-
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## Severity Classification", name: "utility_skill:security:severity" },
|
|
155
|
-
{ file: runtimeFile("skills/debugging/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:debugging:hard_gate" },
|
|
156
|
-
{ file: runtimeFile("skills/debugging/SKILL.md"), needle: "## The Protocol", name: "utility_skill:debugging:protocol" },
|
|
157
|
-
{ file: runtimeFile("skills/debugging/SKILL.md"), needle: "Step 1 — Reproduce", name: "utility_skill:debugging:reproduce" },
|
|
158
|
-
{ file: runtimeFile("skills/debugging/SKILL.md"), needle: "## Testing-Specific Anti-Patterns", name: "utility_skill:debugging:test_antipatterns" },
|
|
159
|
-
{ file: runtimeFile("skills/performance/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:performance:hard_gate" },
|
|
160
|
-
{ file: runtimeFile("skills/performance/SKILL.md"), needle: "## Workflow", name: "utility_skill:performance:workflow" },
|
|
161
|
-
{ file: runtimeFile("skills/performance/SKILL.md"), needle: "## Core Web Vitals Reference", name: "utility_skill:performance:cwv" },
|
|
162
|
-
{ file: runtimeFile("skills/ci-cd/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:cicd:hard_gate" },
|
|
163
|
-
{ file: runtimeFile("skills/ci-cd/SKILL.md"), needle: "## Quality Gate Pipeline", name: "utility_skill:cicd:pipeline" },
|
|
164
|
-
{ file: runtimeFile("skills/ci-cd/SKILL.md"), needle: "## CI Debugging Protocol", name: "utility_skill:cicd:debugging" },
|
|
165
|
-
{ file: runtimeFile("skills/docs/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:docs:hard_gate" },
|
|
166
|
-
{ file: runtimeFile("skills/docs/SKILL.md"), needle: "## ADR (Architecture Decision Record)", name: "utility_skill:docs:adr" },
|
|
167
|
-
{ file: runtimeFile("skills/docs/SKILL.md"), needle: "## README Guidance", name: "utility_skill:docs:readme" },
|
|
168
|
-
{ file: runtimeFile("skills/executing-plans/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:executing_plans:hard_gate" },
|
|
169
|
-
{ file: runtimeFile("skills/executing-plans/SKILL.md"), needle: "## Execution Protocol", name: "utility_skill:executing_plans:protocol" },
|
|
170
|
-
{ file: runtimeFile("skills/executing-plans/SKILL.md"), needle: "## Batch Checklist", name: "utility_skill:executing_plans:batches" },
|
|
171
|
-
{ file: runtimeFile("skills/verification-before-completion/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:verification_before_completion:hard_gate" },
|
|
172
|
-
{ file: runtimeFile("skills/verification-before-completion/SKILL.md"), needle: "## Protocol", name: "utility_skill:verification_before_completion:protocol" },
|
|
173
|
-
{ file: runtimeFile("skills/finishing-a-development-branch/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:finishing_branch:hard_gate" },
|
|
174
|
-
{ file: runtimeFile("skills/finishing-a-development-branch/SKILL.md"), needle: "## Protocol", name: "utility_skill:finishing_branch:protocol" },
|
|
175
|
-
{ file: runtimeFile("skills/context-engineering/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:context_engineering:hard_gate" },
|
|
176
|
-
{ file: runtimeFile("skills/context-engineering/SKILL.md"), needle: "## Context Modes", name: "utility_skill:context_engineering:modes" },
|
|
177
|
-
{ file: runtimeFile("skills/context-engineering/SKILL.md"), needle: "## Mode Switching Protocol", name: "utility_skill:context_engineering:switch" },
|
|
178
|
-
{ file: runtimeFile("skills/source-driven-development/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:source_driven:hard_gate" },
|
|
179
|
-
{ file: runtimeFile("skills/source-driven-development/SKILL.md"), needle: "## Protocol", name: "utility_skill:source_driven:protocol" },
|
|
180
|
-
{ file: runtimeFile("skills/source-driven-development/SKILL.md"), needle: "## Selection Heuristics", name: "utility_skill:source_driven:heuristics" },
|
|
181
|
-
{ file: runtimeFile("skills/frontend-accessibility/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:frontend_accessibility:hard_gate" },
|
|
182
|
-
{ file: runtimeFile("skills/frontend-accessibility/SKILL.md"), needle: "## Checklist", name: "utility_skill:frontend_accessibility:checklist" },
|
|
183
|
-
{ file: runtimeFile("skills/frontend-accessibility/SKILL.md"), needle: "## Anti-Patterns", name: "utility_skill:frontend_accessibility:anti_patterns" },
|
|
99
|
+
{ file: runtimeFile("skills/brainstorming/SKILL.md"), needle: "## Shared Stage Guidance", name: "stage_skill:shared_guidance_inline" },
|
|
184
100
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "activeRunId", name: "hooks:session_start:active_run" },
|
|
185
|
-
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "checkpoint.json", name: "hooks:session_start:checkpoint_ref" },
|
|
186
|
-
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "stage-activity.jsonl", name: "hooks:session_start:activity_ref" },
|
|
187
|
-
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "suggestion-memory.json", name: "hooks:session_start:suggestion_memory" },
|
|
188
|
-
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "context-warnings.jsonl", name: "hooks:session_start:context_warning_ref" },
|
|
189
|
-
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "checkpoint.json", name: "hooks:stop:checkpoint_write" },
|
|
190
101
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "write_to_cclaw_runtime", name: "hooks:guard:risky_write_advisory" },
|
|
191
102
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "stage_invocation_without_recent_flow_read", name: "hooks:workflow_guard:flow_read_reason" },
|
|
192
103
|
{ file: runtimeFile("hooks/run-hook.mjs"), needle: "stage_jump_", name: "hooks:workflow_guard:stage_jump_reason" },
|
package/dist/run-archive.d.ts
CHANGED
|
@@ -9,8 +9,7 @@ export interface ArchiveRunResult {
|
|
|
9
9
|
archiveId: string;
|
|
10
10
|
archivePath: string;
|
|
11
11
|
archivedAt: string;
|
|
12
|
-
|
|
13
|
-
activeFeature: string;
|
|
12
|
+
runName: string;
|
|
14
13
|
resetState: FlowState;
|
|
15
14
|
snapshottedStateFiles: string[];
|
|
16
15
|
/** Knowledge curation hint: total active entries + soft threshold (50). */
|
|
@@ -29,11 +28,10 @@ export interface ArchiveRunResult {
|
|
|
29
28
|
};
|
|
30
29
|
}
|
|
31
30
|
export interface ArchiveManifest {
|
|
32
|
-
version:
|
|
31
|
+
version: 2;
|
|
33
32
|
archiveId: string;
|
|
34
33
|
archivedAt: string;
|
|
35
|
-
|
|
36
|
-
activeFeature: string;
|
|
34
|
+
runName: string;
|
|
37
35
|
sourceRunId: string;
|
|
38
36
|
sourceCurrentStage: FlowStage;
|
|
39
37
|
sourceCompletedStages: FlowStage[];
|
|
@@ -45,7 +43,7 @@ export interface ArchiveRunOptions {
|
|
|
45
43
|
skipRetroReason?: string;
|
|
46
44
|
}
|
|
47
45
|
export declare function listRuns(projectRoot: string): Promise<CclawRunMeta[]>;
|
|
48
|
-
export declare function archiveRun(projectRoot: string,
|
|
46
|
+
export declare function archiveRun(projectRoot: string, runName?: string, options?: ArchiveRunOptions): Promise<ArchiveRunResult>;
|
|
49
47
|
/**
|
|
50
48
|
* Counts entries in the canonical JSONL knowledge store. An "active" entry is one
|
|
51
49
|
* non-empty line that parses as JSON with the required `type` field belonging to the
|
package/dist/run-archive.js
CHANGED
|
@@ -2,7 +2,6 @@ import fs from "node:fs/promises";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
4
4
|
import { createInitialFlowState } from "./flow-state.js";
|
|
5
|
-
import { readActiveFeature, syncActiveFeatureSnapshot } from "./feature-system.js";
|
|
6
5
|
import { ensureDir, exists, withDirectoryLock, writeFileSafe } from "./fs-utils.js";
|
|
7
6
|
import { readKnowledgeSafely } from "./knowledge-store.js";
|
|
8
7
|
import { evaluateRetroGate } from "./retro-gate.js";
|
|
@@ -90,7 +89,7 @@ function toArchiveDate(date = new Date()) {
|
|
|
90
89
|
const dd = date.getDate().toString().padStart(2, "0");
|
|
91
90
|
return `${yyyy}-${mm}-${dd}`;
|
|
92
91
|
}
|
|
93
|
-
function
|
|
92
|
+
function slugifyRunName(value) {
|
|
94
93
|
const slug = value
|
|
95
94
|
.toLowerCase()
|
|
96
95
|
.trim()
|
|
@@ -98,14 +97,14 @@ function slugifyFeatureName(value) {
|
|
|
98
97
|
.replace(/^-+/u, "")
|
|
99
98
|
.replace(/-+$/u, "");
|
|
100
99
|
if (slug.length === 0) {
|
|
101
|
-
return "
|
|
100
|
+
return "run";
|
|
102
101
|
}
|
|
103
102
|
return slug.slice(0, 64);
|
|
104
103
|
}
|
|
105
|
-
async function
|
|
104
|
+
async function inferRunNameFromArtifacts(projectRoot) {
|
|
106
105
|
const ideaPath = path.join(projectRoot, ACTIVE_ARTIFACTS_REL_PATH, "00-idea.md");
|
|
107
106
|
if (!(await exists(ideaPath))) {
|
|
108
|
-
return "
|
|
107
|
+
return "run";
|
|
109
108
|
}
|
|
110
109
|
try {
|
|
111
110
|
const raw = await fs.readFile(ideaPath, "utf8");
|
|
@@ -114,12 +113,12 @@ async function inferFeatureNameFromArtifacts(projectRoot) {
|
|
|
114
113
|
.map((line) => line.trim())
|
|
115
114
|
.find((line) => line.length > 0);
|
|
116
115
|
if (!firstMeaningful) {
|
|
117
|
-
return "
|
|
116
|
+
return "run";
|
|
118
117
|
}
|
|
119
|
-
return firstMeaningful.replace(/^[-#*\s]+/u, "").trim() || "
|
|
118
|
+
return firstMeaningful.replace(/^[-#*\s]+/u, "").trim() || "run";
|
|
120
119
|
}
|
|
121
120
|
catch {
|
|
122
|
-
return "
|
|
121
|
+
return "run";
|
|
123
122
|
}
|
|
124
123
|
}
|
|
125
124
|
async function uniqueArchiveId(projectRoot, baseId) {
|
|
@@ -159,7 +158,7 @@ export async function listRuns(projectRoot) {
|
|
|
159
158
|
}
|
|
160
159
|
return runs.sort((a, b) => a.createdAt.localeCompare(b.createdAt));
|
|
161
160
|
}
|
|
162
|
-
export async function archiveRun(projectRoot,
|
|
161
|
+
export async function archiveRun(projectRoot, runName, options = {}) {
|
|
163
162
|
await ensureRunSystem(projectRoot);
|
|
164
163
|
// Hold BOTH archive.lock and flow-state.lock for the entire archive:
|
|
165
164
|
// the outer archive lock serializes two concurrent archives; the
|
|
@@ -168,15 +167,14 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
168
167
|
// which used to cause lost-update races.
|
|
169
168
|
return withDirectoryLock(archiveLockPath(projectRoot), async () => {
|
|
170
169
|
return withDirectoryLock(flowStateLockPathFor(projectRoot), async () => {
|
|
171
|
-
const activeFeature = await readActiveFeature(projectRoot);
|
|
172
170
|
const artifactsDir = activeArtifactsPath(projectRoot);
|
|
173
171
|
const runsDir = runsRoot(projectRoot);
|
|
174
172
|
await ensureDir(runsDir);
|
|
175
173
|
await ensureDir(artifactsDir);
|
|
176
|
-
const
|
|
177
|
-
?
|
|
178
|
-
: await
|
|
179
|
-
const archiveBaseId = `${toArchiveDate()}-${
|
|
174
|
+
const archiveRunName = (runName?.trim() && runName.trim().length > 0)
|
|
175
|
+
? runName.trim()
|
|
176
|
+
: await inferRunNameFromArtifacts(projectRoot);
|
|
177
|
+
const archiveBaseId = `${toArchiveDate()}-${slugifyRunName(archiveRunName)}`;
|
|
180
178
|
const archiveId = await uniqueArchiveId(projectRoot, archiveBaseId);
|
|
181
179
|
const archivePath = path.join(runsDir, archiveId);
|
|
182
180
|
const archiveArtifactsPath = path.join(archivePath, "artifacts");
|
|
@@ -256,11 +254,10 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
256
254
|
stateReset = true;
|
|
257
255
|
await resetCarryoverStateFiles(projectRoot, resetState.activeRunId);
|
|
258
256
|
const manifest = {
|
|
259
|
-
version:
|
|
257
|
+
version: 2,
|
|
260
258
|
archiveId,
|
|
261
259
|
archivedAt,
|
|
262
|
-
|
|
263
|
-
activeFeature,
|
|
260
|
+
runName: archiveRunName,
|
|
264
261
|
sourceRunId: sourceState.activeRunId,
|
|
265
262
|
sourceCurrentStage: sourceState.currentStage,
|
|
266
263
|
sourceCompletedStages: sourceState.completedStages,
|
|
@@ -271,13 +268,11 @@ export async function archiveRun(projectRoot, featureName, options = {}) {
|
|
|
271
268
|
// Manifest landed — sentinel is no longer needed.
|
|
272
269
|
await fs.unlink(sentinelPath).catch(() => undefined);
|
|
273
270
|
const knowledgeStats = await readKnowledgeStats(projectRoot);
|
|
274
|
-
await syncActiveFeatureSnapshot(projectRoot);
|
|
275
271
|
return {
|
|
276
272
|
archiveId,
|
|
277
273
|
archivePath,
|
|
278
274
|
archivedAt,
|
|
279
|
-
|
|
280
|
-
activeFeature,
|
|
275
|
+
runName: archiveRunName,
|
|
281
276
|
resetState,
|
|
282
277
|
snapshottedStateFiles,
|
|
283
278
|
knowledge: knowledgeStats,
|
|
@@ -21,8 +21,8 @@ export interface WriteFlowStateOptions {
|
|
|
21
21
|
}
|
|
22
22
|
export interface ReadFlowStateOptions {
|
|
23
23
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
24
|
+
* Reserved compatibility switch from older runtimes. The repair layer was removed,
|
|
25
|
+
* so this flag is now a no-op and only preserved for API stability.
|
|
26
26
|
*/
|
|
27
27
|
repairFeatureSystem?: boolean;
|
|
28
28
|
}
|