cclaw-cli 0.48.13 → 0.48.15
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/content/flow-map.d.ts +23 -0
- package/dist/content/flow-map.js +129 -0
- package/dist/content/meta-skill.js +3 -0
- package/dist/content/next-command.js +7 -0
- package/dist/content/node-hooks.js +123 -0
- package/dist/content/start-command.js +7 -0
- package/dist/doctor-registry.js +1 -1
- package/dist/install.js +2 -0
- package/dist/internal/advance-stage.js +6 -2
- package/dist/internal/compound-readiness.d.ts +15 -0
- package/dist/internal/compound-readiness.js +76 -0
- package/dist/knowledge-store.d.ts +57 -0
- package/dist/knowledge-store.js +107 -0
- package/dist/policy.js +5 -0
- package/package.json +1 -1
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relative path used by skills/commands to cite the consolidated flow map.
|
|
3
|
+
* Stable contract — changing this value is a breaking change for doctor
|
|
4
|
+
* policies and meta-skill links.
|
|
5
|
+
*/
|
|
6
|
+
export declare const FLOW_MAP_REL_PATH = ".cclaw/references/flow-map.md";
|
|
7
|
+
/**
|
|
8
|
+
* Canonical one-page overview of cclaw's user-facing surface.
|
|
9
|
+
*
|
|
10
|
+
* Purpose: give the model (and any curious human) a single file that
|
|
11
|
+
* answers "what does cclaw expose, where does my current stage fit, and
|
|
12
|
+
* which files drive progress?" without forcing a walk of the 8 stage
|
|
13
|
+
* skills plus the 4 router skills plus the meta-skill.
|
|
14
|
+
*
|
|
15
|
+
* Design rules:
|
|
16
|
+
* - Keep it under ~150 lines; it is a map, not a manual.
|
|
17
|
+
* - Only cite files that already exist under `.cclaw/`.
|
|
18
|
+
* - Do not duplicate protocol text (decision/completion/ethos live in
|
|
19
|
+
* `.cclaw/references/protocols/`). Link, don't inline.
|
|
20
|
+
* - Do not introduce new gates or hard rules here — flow-map is
|
|
21
|
+
* descriptive, not prescriptive.
|
|
22
|
+
*/
|
|
23
|
+
export declare function flowMapMarkdown(): string;
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
/**
|
|
3
|
+
* Relative path used by skills/commands to cite the consolidated flow map.
|
|
4
|
+
* Stable contract — changing this value is a breaking change for doctor
|
|
5
|
+
* policies and meta-skill links.
|
|
6
|
+
*/
|
|
7
|
+
export const FLOW_MAP_REL_PATH = `${RUNTIME_ROOT}/references/flow-map.md`;
|
|
8
|
+
/**
|
|
9
|
+
* Canonical one-page overview of cclaw's user-facing surface.
|
|
10
|
+
*
|
|
11
|
+
* Purpose: give the model (and any curious human) a single file that
|
|
12
|
+
* answers "what does cclaw expose, where does my current stage fit, and
|
|
13
|
+
* which files drive progress?" without forcing a walk of the 8 stage
|
|
14
|
+
* skills plus the 4 router skills plus the meta-skill.
|
|
15
|
+
*
|
|
16
|
+
* Design rules:
|
|
17
|
+
* - Keep it under ~150 lines; it is a map, not a manual.
|
|
18
|
+
* - Only cite files that already exist under `.cclaw/`.
|
|
19
|
+
* - Do not duplicate protocol text (decision/completion/ethos live in
|
|
20
|
+
* `.cclaw/references/protocols/`). Link, don't inline.
|
|
21
|
+
* - Do not introduce new gates or hard rules here — flow-map is
|
|
22
|
+
* descriptive, not prescriptive.
|
|
23
|
+
*/
|
|
24
|
+
export function flowMapMarkdown() {
|
|
25
|
+
return `# cclaw Flow Map
|
|
26
|
+
|
|
27
|
+
One-page surface reference. Use this when you need the shape of cclaw
|
|
28
|
+
without reading every skill — the stage quick-map, the user-facing
|
|
29
|
+
slash commands, the Ralph Loop signal, and the key state files.
|
|
30
|
+
|
|
31
|
+
For enforcement details, load the matching stage skill or command
|
|
32
|
+
contract. For protocols (decision/completion/ethos) see
|
|
33
|
+
\`${RUNTIME_ROOT}/references/protocols/\`.
|
|
34
|
+
|
|
35
|
+
## Stages (8)
|
|
36
|
+
|
|
37
|
+
| # | Stage | Goal | Primary artifact |
|
|
38
|
+
|---|---|---|---|
|
|
39
|
+
| 1 | brainstorm | Explore options and constraints | \`.cclaw/artifacts/00-idea.md\` (+ \`01-brainstorm.md\`) |
|
|
40
|
+
| 2 | scope | Freeze scope (in/out, assumptions) | \`.cclaw/artifacts/02-scope.md\` |
|
|
41
|
+
| 3 | design | Pick the shape of the change | \`.cclaw/artifacts/03-design.md\` |
|
|
42
|
+
| 4 | spec | Turn design into testable acceptance criteria | \`.cclaw/artifacts/04-spec.md\` |
|
|
43
|
+
| 5 | plan | Decompose spec into executable slices | \`.cclaw/artifacts/05-plan.md\` |
|
|
44
|
+
| 6 | tdd | Drive each slice through RED → GREEN → REFACTOR (Ralph Loop) | \`.cclaw/artifacts/06-tdd.md\` + \`.cclaw/state/tdd-cycle-log.jsonl\` |
|
|
45
|
+
| 7 | review | Cross-check correctness, spec coverage, and ethos | \`.cclaw/artifacts/07-review.md\` |
|
|
46
|
+
| 8 | ship | Close out: retro, compound, archive | \`.cclaw/artifacts/08-ship.md\` (+ \`09-retro.md\`) |
|
|
47
|
+
|
|
48
|
+
Track shortcuts (set in \`.cclaw/state/flow-state.json\`):
|
|
49
|
+
|
|
50
|
+
- \`quick\` — spec → tdd → review → ship
|
|
51
|
+
- \`medium\` — brainstorm → spec → plan → tdd → review → ship
|
|
52
|
+
- \`standard\` — all 8 stages (default)
|
|
53
|
+
|
|
54
|
+
## User-facing slash commands
|
|
55
|
+
|
|
56
|
+
| Command | Role | Notes |
|
|
57
|
+
|---|---|---|
|
|
58
|
+
| \`/cc\` | Entry point. No args = resume. With prompt = classify + start. | Writes \`00-idea.md\` and picks the track. |
|
|
59
|
+
| \`/cc-next\` | Advance or resume the current stage based on gates. | Soft nudge from Ralph Loop during \`tdd\`. |
|
|
60
|
+
| \`/cc-ideate\` | Repo-improvement discovery, separate from product flow. | Produces ideas, not stage artifacts. |
|
|
61
|
+
| \`/cc-view [status\\|tree\\|diff]\` | Read-only router. Never mutates flow state. | \`diff\` refreshes the snapshot baseline by design. |
|
|
62
|
+
| \`/cc-ops [feature\\|tdd-log\\|retro\\|compound\\|archive\\|rewind]\` | Operations router for post-flow and side-channel actions. | Mutations are scoped to each subcommand. |
|
|
63
|
+
|
|
64
|
+
Subcommand dispatch lives in \`${RUNTIME_ROOT}/commands/\` and the
|
|
65
|
+
matching \`${RUNTIME_ROOT}/skills/flow-*/SKILL.md\`. The meta-skill
|
|
66
|
+
(\`${RUNTIME_ROOT}/skills/using-cclaw/SKILL.md\`) decides which router to
|
|
67
|
+
use before any substantive work.
|
|
68
|
+
|
|
69
|
+
## Ralph Loop (TDD progress signal)
|
|
70
|
+
|
|
71
|
+
When \`currentStage === "tdd"\`, SessionStart writes
|
|
72
|
+
\`${RUNTIME_ROOT}/state/ralph-loop.json\` from the TDD cycle log. Fields
|
|
73
|
+
worth acting on:
|
|
74
|
+
|
|
75
|
+
- \`loopIteration\` — how many RED → GREEN cycles already landed.
|
|
76
|
+
- \`redOpenSlices\` — slices with an unsatisfied RED. Non-empty means do
|
|
77
|
+
**not** advance to review.
|
|
78
|
+
- \`acClosed\` — distinct acceptance-criterion IDs closed by a GREEN row
|
|
79
|
+
(requires \`acIds\` on the green log entry via \`/cc-ops tdd-log\`).
|
|
80
|
+
- \`sliceCount\` — total distinct plan slices ever touched.
|
|
81
|
+
|
|
82
|
+
Ralph Loop is a signal, not a gate. Stage advancement still runs
|
|
83
|
+
through the normal \`flow-state.json\` gate catalog.
|
|
84
|
+
|
|
85
|
+
## Compound readiness (auto-promotion signal)
|
|
86
|
+
|
|
87
|
+
SessionStart also refreshes
|
|
88
|
+
\`${RUNTIME_ROOT}/state/compound-readiness.json\` from \`knowledge.jsonl\`.
|
|
89
|
+
The file lists clusters whose summed \`frequency\` reaches
|
|
90
|
+
\`compound.recurrenceThreshold\` (default 3) or whose severity is
|
|
91
|
+
\`critical\` (override). It surfaces a one-line nudge in the session
|
|
92
|
+
digest only during \`review\` and \`ship\`, where lift-to-rule is in
|
|
93
|
+
scope; earlier stages refresh the file silently. Promotion itself stays
|
|
94
|
+
manual via \`/cc-ops compound\` so the signal never blocks flow.
|
|
95
|
+
|
|
96
|
+
## Key state files
|
|
97
|
+
|
|
98
|
+
| Path | What it holds |
|
|
99
|
+
|---|---|
|
|
100
|
+
| \`${RUNTIME_ROOT}/state/flow-state.json\` | Track, currentStage, completedStages, gate catalog, closeout substate. |
|
|
101
|
+
| \`${RUNTIME_ROOT}/state/delegation-log.json\` | Per-stage mandatory agent status + fulfillmentMode + evidenceRefs. |
|
|
102
|
+
| \`${RUNTIME_ROOT}/state/tdd-cycle-log.jsonl\` | Append-only RED/GREEN/REFACTOR entries (source of Ralph Loop). |
|
|
103
|
+
| \`${RUNTIME_ROOT}/state/ralph-loop.json\` | Derived Ralph Loop status (TDD-only). |
|
|
104
|
+
| \`${RUNTIME_ROOT}/state/compound-readiness.json\` | Derived compound-promotion readiness (refreshed each SessionStart). |
|
|
105
|
+
| \`${RUNTIME_ROOT}/state/stage-activity.jsonl\` | Append-only stage-enter/exit and gate-pass signals. |
|
|
106
|
+
| \`${RUNTIME_ROOT}/state/checkpoint.json\` | Latest session checkpoint (stage + timestamp). |
|
|
107
|
+
| \`${RUNTIME_ROOT}/state/context-mode.json\` | Active context mode (\`default\`, \`headless\`, ...). |
|
|
108
|
+
| \`${RUNTIME_ROOT}/state/harness-gaps.json\` | Per-harness tier, subagent fallback, playbook path (schemaVersion 2). |
|
|
109
|
+
| \`${RUNTIME_ROOT}/knowledge.jsonl\` | Append-only learnings; surfaced to sessions via digest. |
|
|
110
|
+
|
|
111
|
+
## Strictness and hooks
|
|
112
|
+
|
|
113
|
+
Hook-driven guards respect the \`strictness\` field in
|
|
114
|
+
\`${RUNTIME_ROOT}/config.yaml\`:
|
|
115
|
+
|
|
116
|
+
- \`advisory\` (default) — hooks warn but never block tool calls.
|
|
117
|
+
- \`strict\` — hooks block tool calls that violate their scope.
|
|
118
|
+
|
|
119
|
+
Override per-session with \`CCLAW_STRICTNESS=advisory|strict\`.
|
|
120
|
+
|
|
121
|
+
## When in doubt
|
|
122
|
+
|
|
123
|
+
1. Read \`${RUNTIME_ROOT}/state/flow-state.json\` to know where you are.
|
|
124
|
+
2. Load the matching stage skill only if you are about to do
|
|
125
|
+
substantive work (see \`using-cclaw\` meta-skill).
|
|
126
|
+
3. Prefer \`/cc-next\` for progression. \`/cc-view\` for visibility.
|
|
127
|
+
\`/cc-ops\` for side-channel operations.
|
|
128
|
+
`;
|
|
129
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { FLOW_MAP_REL_PATH } from "./flow-map.js";
|
|
1
2
|
import { COMPLETION_PROTOCOL_REL_PATH, DECISION_PROTOCOL_REL_PATH, ETHOS_PROTOCOL_REL_PATH } from "./protocols.js";
|
|
2
3
|
export const META_SKILL_NAME = "using-cclaw";
|
|
3
4
|
export function usingCclawSkillMarkdown() {
|
|
@@ -76,6 +77,8 @@ Before stage work:
|
|
|
76
77
|
brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship
|
|
77
78
|
|
|
78
79
|
Tracks may skip stages via \`flow-state.track\` + \`skippedStages\`.
|
|
80
|
+
For the full surface (stages, routers, Ralph Loop, state files) load
|
|
81
|
+
\`${FLOW_MAP_REL_PATH}\` — it is the single-page overview of cclaw.
|
|
79
82
|
|
|
80
83
|
## Contextual skill activation
|
|
81
84
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
import { FLOW_MAP_REL_PATH } from "./flow-map.js";
|
|
2
3
|
import { stageSchema } from "./stage-schema.js";
|
|
3
4
|
import { stageSkillFolder } from "./skills.js";
|
|
4
5
|
const NEXT_SKILL_FOLDER = "flow-next-step";
|
|
@@ -128,6 +129,12 @@ Validate envelopes with:
|
|
|
128
129
|
## Primary skill
|
|
129
130
|
|
|
130
131
|
**${skillRel}** — full protocol and stage table.
|
|
132
|
+
|
|
133
|
+
## Surface reference
|
|
134
|
+
|
|
135
|
+
When you need the full shape of cclaw (stages, routers, Ralph Loop,
|
|
136
|
+
state files) load \`${FLOW_MAP_REL_PATH}\`. It is the single-page
|
|
137
|
+
overview and is safe to read at any time.
|
|
131
138
|
`;
|
|
132
139
|
}
|
|
133
140
|
/**
|
|
@@ -725,6 +725,28 @@ async function handleSessionStart(runtime) {
|
|
|
725
725
|
}
|
|
726
726
|
}
|
|
727
727
|
|
|
728
|
+
// Keep compound-readiness.json fresh on every session-start (cheap derived
|
|
729
|
+
// summary). Surface a one-line nudge only from review and ship stages
|
|
730
|
+
// where lifting becomes relevant; earlier stages update the file silently.
|
|
731
|
+
let compoundReadinessLine = "";
|
|
732
|
+
try {
|
|
733
|
+
const readiness = await computeCompoundReadinessInline(runtime.root, {});
|
|
734
|
+
await writeJsonFile(path.join(stateDir, "compound-readiness.json"), readiness);
|
|
735
|
+
if (state.currentStage === "review" || state.currentStage === "ship") {
|
|
736
|
+
if (readiness.readyCount === 0) {
|
|
737
|
+
compoundReadinessLine = "Compound readiness: no candidates (clusters=" +
|
|
738
|
+
String(readiness.clusterCount) + ", threshold=" + String(readiness.threshold) + ")";
|
|
739
|
+
} else {
|
|
740
|
+
const critical = readiness.ready.filter((entry) => entry.severity === "critical").length;
|
|
741
|
+
const criticalSuffix = critical > 0 ? " (critical=" + String(critical) + ")" : "";
|
|
742
|
+
compoundReadinessLine = "Compound readiness: clusters=" + String(readiness.clusterCount) +
|
|
743
|
+
", ready=" + String(readiness.readyCount) + criticalSuffix;
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
} catch (_err) {
|
|
747
|
+
// best-effort — a malformed knowledge.jsonl must never break session-start.
|
|
748
|
+
}
|
|
749
|
+
|
|
728
750
|
const suggestionMemory = toObject(await readJsonFile(suggestionMemoryFile, {})) || {};
|
|
729
751
|
const suggestionsEnabled = suggestionMemory.enabled !== false;
|
|
730
752
|
const mutedStages = Array.isArray(suggestionMemory.mutedStages)
|
|
@@ -792,6 +814,9 @@ async function handleSessionStart(runtime) {
|
|
|
792
814
|
if (ralphLoopLine.length > 0) {
|
|
793
815
|
parts.push(ralphLoopLine);
|
|
794
816
|
}
|
|
817
|
+
if (compoundReadinessLine.length > 0) {
|
|
818
|
+
parts.push(compoundReadinessLine);
|
|
819
|
+
}
|
|
795
820
|
if (contextWarning.length > 0) {
|
|
796
821
|
parts.push("Latest context warning:\\n" + contextWarning);
|
|
797
822
|
}
|
|
@@ -1112,6 +1137,104 @@ async function tddCycleCounts(stateDir, runId) {
|
|
|
1112
1137
|
return { red, green };
|
|
1113
1138
|
}
|
|
1114
1139
|
|
|
1140
|
+
// Mirrors src/knowledge-store.ts::computeCompoundReadiness — kept inline so
|
|
1141
|
+
// SessionStart can refresh compound-readiness.json without the CLI binary.
|
|
1142
|
+
// Any schema change must update src/knowledge-store.ts::computeCompoundReadiness
|
|
1143
|
+
// and src/internal/compound-readiness.ts in lockstep.
|
|
1144
|
+
async function computeCompoundReadinessInline(root, options) {
|
|
1145
|
+
const filePath = path.join(root, RUNTIME_ROOT, "knowledge.jsonl");
|
|
1146
|
+
const raw = await readTextFile(filePath, "");
|
|
1147
|
+
const threshold = Number.isInteger(options && options.threshold) && options.threshold >= 1
|
|
1148
|
+
? options.threshold
|
|
1149
|
+
: 3;
|
|
1150
|
+
const maxReady = Number.isInteger(options && options.maxReady) && options.maxReady >= 1
|
|
1151
|
+
? options.maxReady
|
|
1152
|
+
: 10;
|
|
1153
|
+
const normalize = (value) => String(value == null ? "" : value).trim().replace(/\\s+/gu, " ").toLowerCase();
|
|
1154
|
+
const severityWeight = (sev) => {
|
|
1155
|
+
if (sev === "critical") return 3;
|
|
1156
|
+
if (sev === "important") return 2;
|
|
1157
|
+
if (sev === "suggestion") return 1;
|
|
1158
|
+
return 0;
|
|
1159
|
+
};
|
|
1160
|
+
const buckets = new Map();
|
|
1161
|
+
for (const rawLine of raw.split(/\\r?\\n/gu)) {
|
|
1162
|
+
const line = rawLine.trim();
|
|
1163
|
+
if (line.length === 0) continue;
|
|
1164
|
+
let row;
|
|
1165
|
+
try { row = JSON.parse(line); } catch { continue; }
|
|
1166
|
+
if (!row || typeof row !== "object" || Array.isArray(row)) continue;
|
|
1167
|
+
if (row.maturity === "lifted-to-enforcement") continue;
|
|
1168
|
+
const type = typeof row.type === "string" ? row.type : "";
|
|
1169
|
+
const trigger = typeof row.trigger === "string" ? row.trigger : "";
|
|
1170
|
+
const action = typeof row.action === "string" ? row.action : "";
|
|
1171
|
+
if (type.length === 0 || trigger.length === 0 || action.length === 0) continue;
|
|
1172
|
+
const key = type + "||" + normalize(trigger) + "||" + normalize(action);
|
|
1173
|
+
const frequency = Number.isInteger(row.frequency) && row.frequency > 0 ? Math.floor(row.frequency) : 1;
|
|
1174
|
+
const lastSeen = typeof row.last_seen_ts === "string" ? row.last_seen_ts : "";
|
|
1175
|
+
let bucket = buckets.get(key);
|
|
1176
|
+
if (!bucket) {
|
|
1177
|
+
bucket = {
|
|
1178
|
+
trigger,
|
|
1179
|
+
action,
|
|
1180
|
+
recurrence: frequency,
|
|
1181
|
+
entryCount: 1,
|
|
1182
|
+
severity: typeof row.severity === "string" ? row.severity : undefined,
|
|
1183
|
+
lastSeenTs: lastSeen,
|
|
1184
|
+
types: new Set([type]),
|
|
1185
|
+
maturity: new Set([typeof row.maturity === "string" ? row.maturity : "raw"])
|
|
1186
|
+
};
|
|
1187
|
+
buckets.set(key, bucket);
|
|
1188
|
+
continue;
|
|
1189
|
+
}
|
|
1190
|
+
bucket.recurrence += frequency;
|
|
1191
|
+
bucket.entryCount += 1;
|
|
1192
|
+
bucket.types.add(type);
|
|
1193
|
+
bucket.maturity.add(typeof row.maturity === "string" ? row.maturity : "raw");
|
|
1194
|
+
if (row.severity === "critical") {
|
|
1195
|
+
bucket.severity = "critical";
|
|
1196
|
+
} else if (row.severity === "important" && bucket.severity !== "critical") {
|
|
1197
|
+
bucket.severity = "important";
|
|
1198
|
+
}
|
|
1199
|
+
if (lastSeen && Date.parse(lastSeen) > Date.parse(bucket.lastSeenTs || "0")) {
|
|
1200
|
+
bucket.lastSeenTs = lastSeen;
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
const ready = [];
|
|
1204
|
+
for (const bucket of buckets.values()) {
|
|
1205
|
+
const criticalOverride = bucket.severity === "critical";
|
|
1206
|
+
const meetsRecurrence = bucket.recurrence >= threshold;
|
|
1207
|
+
if (!criticalOverride && !meetsRecurrence) continue;
|
|
1208
|
+
ready.push({
|
|
1209
|
+
trigger: bucket.trigger,
|
|
1210
|
+
action: bucket.action,
|
|
1211
|
+
recurrence: bucket.recurrence,
|
|
1212
|
+
entryCount: bucket.entryCount,
|
|
1213
|
+
qualification: criticalOverride && !meetsRecurrence ? "critical_override" : "recurrence",
|
|
1214
|
+
...(bucket.severity ? { severity: bucket.severity } : {}),
|
|
1215
|
+
lastSeenTs: bucket.lastSeenTs,
|
|
1216
|
+
types: Array.from(bucket.types).sort(),
|
|
1217
|
+
maturity: Array.from(bucket.maturity).sort()
|
|
1218
|
+
});
|
|
1219
|
+
}
|
|
1220
|
+
ready.sort((a, b) => {
|
|
1221
|
+
const sevDiff = severityWeight(b.severity) - severityWeight(a.severity);
|
|
1222
|
+
if (sevDiff !== 0) return sevDiff;
|
|
1223
|
+
if (b.recurrence !== a.recurrence) return b.recurrence - a.recurrence;
|
|
1224
|
+
const recencyDiff = Date.parse(b.lastSeenTs || "0") - Date.parse(a.lastSeenTs || "0");
|
|
1225
|
+
if (!Number.isNaN(recencyDiff) && recencyDiff !== 0) return recencyDiff;
|
|
1226
|
+
return String(a.trigger).localeCompare(String(b.trigger));
|
|
1227
|
+
});
|
|
1228
|
+
return {
|
|
1229
|
+
schemaVersion: 1,
|
|
1230
|
+
threshold,
|
|
1231
|
+
clusterCount: buckets.size,
|
|
1232
|
+
readyCount: ready.length,
|
|
1233
|
+
ready: ready.slice(0, maxReady),
|
|
1234
|
+
lastUpdatedAt: new Date().toISOString()
|
|
1235
|
+
};
|
|
1236
|
+
}
|
|
1237
|
+
|
|
1115
1238
|
// Mirrors src/tdd-cycle.ts::computeRalphLoopStatus — kept inline so the
|
|
1116
1239
|
// SessionStart hook can write ralph-loop.json without depending on the CLI
|
|
1117
1240
|
// binary being installed globally. Any schema change must update both copies.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
import { FLOW_MAP_REL_PATH } from "./flow-map.js";
|
|
2
3
|
const START_SKILL_FOLDER = "flow-start";
|
|
3
4
|
const START_SKILL_NAME = "flow-start";
|
|
4
5
|
function flowStatePath() {
|
|
@@ -115,6 +116,12 @@ Validate envelopes with:
|
|
|
115
116
|
## Primary skill
|
|
116
117
|
|
|
117
118
|
**${RUNTIME_ROOT}/skills/${START_SKILL_FOLDER}/SKILL.md**
|
|
119
|
+
|
|
120
|
+
## Surface reference
|
|
121
|
+
|
|
122
|
+
For the 1-page overview of cclaw (stages, routers, Ralph Loop, state
|
|
123
|
+
files) load \`${FLOW_MAP_REL_PATH}\` — useful when a fresh session
|
|
124
|
+
needs orientation before \`/cc <prompt>\` runs.
|
|
118
125
|
`;
|
|
119
126
|
}
|
|
120
127
|
/**
|
package/dist/doctor-registry.js
CHANGED
|
@@ -85,7 +85,7 @@ const RULES = [
|
|
|
85
85
|
}
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
|
-
test: /^(meta_skill:|protocol:|stage_skill:|context_mode:)/,
|
|
88
|
+
test: /^(meta_skill:|protocol:|stage_skill:|context_mode:|reference:)/,
|
|
89
89
|
metadata: {
|
|
90
90
|
severity: "error",
|
|
91
91
|
summary: "Routing skill and protocol integrity check.",
|
package/dist/install.js
CHANGED
|
@@ -28,6 +28,7 @@ import { stageCompleteScript, opencodePluginJs, claudeHooksJson, codexHooksJson,
|
|
|
28
28
|
import { nodeHookRuntimeScript } from "./content/node-hooks.js";
|
|
29
29
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
30
30
|
import { decisionProtocolMarkdown, completionProtocolMarkdown, ethosProtocolMarkdown } from "./content/protocols.js";
|
|
31
|
+
import { flowMapMarkdown } from "./content/flow-map.js";
|
|
31
32
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
32
33
|
import { EVAL_BASELINES_README, EVAL_CONFIG_YAML, EVAL_CORPUS_README, EVAL_REPORTS_README, EVAL_RUBRIC_FILES, EVAL_RUBRICS_README } from "./content/eval-scaffold.js";
|
|
33
34
|
import { TDD_BATCH_WALKTHROUGH_MARKDOWN, stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
|
|
@@ -355,6 +356,7 @@ async function writeSkills(projectRoot, config) {
|
|
|
355
356
|
await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "decision.md"), decisionProtocolMarkdown());
|
|
356
357
|
await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "completion.md"), completionProtocolMarkdown());
|
|
357
358
|
await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "ethos.md"), ethosProtocolMarkdown());
|
|
359
|
+
await writeFileSafe(runtimePath(projectRoot, "references", "flow-map.md"), flowMapMarkdown());
|
|
358
360
|
for (const folder of UTILITY_SKILL_FOLDERS) {
|
|
359
361
|
const generator = UTILITY_SKILL_MAP[folder];
|
|
360
362
|
await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
|
|
@@ -12,6 +12,7 @@ import { getAvailableTransitions, getTransitionGuards, isFlowTrack } from "../fl
|
|
|
12
12
|
import { appendKnowledge } from "../knowledge-store.js";
|
|
13
13
|
import { readFlowState, writeFlowState } from "../runs.js";
|
|
14
14
|
import { FLOW_STAGES } from "../types.js";
|
|
15
|
+
import { runCompoundReadinessCommand } from "./compound-readiness.js";
|
|
15
16
|
import { runEnvelopeValidateCommand } from "./envelope-validate.js";
|
|
16
17
|
import { runKnowledgeDigestCommand } from "./knowledge-digest.js";
|
|
17
18
|
import { runTddLoopStatusCommand } from "./tdd-loop-status.js";
|
|
@@ -673,7 +674,7 @@ async function runHookCommand(projectRoot, args, io) {
|
|
|
673
674
|
export async function runInternalCommand(projectRoot, argv, io) {
|
|
674
675
|
const [subcommand, ...tokens] = argv;
|
|
675
676
|
if (!subcommand) {
|
|
676
|
-
io.stderr.write("cclaw internal requires a subcommand: advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | tdd-loop-status | hook\n");
|
|
677
|
+
io.stderr.write("cclaw internal requires a subcommand: advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | tdd-loop-status | compound-readiness | hook\n");
|
|
677
678
|
return 1;
|
|
678
679
|
}
|
|
679
680
|
try {
|
|
@@ -698,10 +699,13 @@ export async function runInternalCommand(projectRoot, argv, io) {
|
|
|
698
699
|
if (subcommand === "tdd-loop-status") {
|
|
699
700
|
return await runTddLoopStatusCommand(projectRoot, tokens, io);
|
|
700
701
|
}
|
|
702
|
+
if (subcommand === "compound-readiness") {
|
|
703
|
+
return await runCompoundReadinessCommand(projectRoot, tokens, io);
|
|
704
|
+
}
|
|
701
705
|
if (subcommand === "hook") {
|
|
702
706
|
return await runHookCommand(projectRoot, parseHookArgs(tokens), io);
|
|
703
707
|
}
|
|
704
|
-
io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | tdd-loop-status | hook\n`);
|
|
708
|
+
io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence | tdd-loop-status | compound-readiness | hook\n`);
|
|
705
709
|
return 1;
|
|
706
710
|
}
|
|
707
711
|
catch (err) {
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { Writable } from "node:stream";
|
|
2
|
+
import { type CompoundReadiness } from "../knowledge-store.js";
|
|
3
|
+
interface InternalIo {
|
|
4
|
+
stdout: Writable;
|
|
5
|
+
stderr: Writable;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Compact one-liner for session-digest / bootstrap surfaces.
|
|
9
|
+
*
|
|
10
|
+
* Example: `Compound readiness: clusters=12, ready=2 (critical=1)`.
|
|
11
|
+
* When `ready === 0`, emit `Compound readiness: no candidates`.
|
|
12
|
+
*/
|
|
13
|
+
export declare function formatCompoundReadinessLine(status: CompoundReadiness): string;
|
|
14
|
+
export declare function runCompoundReadinessCommand(projectRoot: string, argv: string[], io: InternalIo): Promise<number>;
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
3
|
+
import { readConfig } from "../config.js";
|
|
4
|
+
import { writeFileSafe } from "../fs-utils.js";
|
|
5
|
+
import { computeCompoundReadiness, readKnowledgeSafely } from "../knowledge-store.js";
|
|
6
|
+
function parseArgs(tokens) {
|
|
7
|
+
const args = { json: false, quiet: false, write: true };
|
|
8
|
+
for (let i = 0; i < tokens.length; i += 1) {
|
|
9
|
+
const token = tokens[i];
|
|
10
|
+
if (token === "--json")
|
|
11
|
+
args.json = true;
|
|
12
|
+
else if (token === "--quiet")
|
|
13
|
+
args.quiet = true;
|
|
14
|
+
else if (token === "--no-write")
|
|
15
|
+
args.write = false;
|
|
16
|
+
else if (token === "--write")
|
|
17
|
+
args.write = true;
|
|
18
|
+
else if (token === "--threshold") {
|
|
19
|
+
const value = tokens[i + 1];
|
|
20
|
+
if (!value)
|
|
21
|
+
throw new Error("--threshold requires a numeric value");
|
|
22
|
+
const parsed = Number.parseInt(value, 10);
|
|
23
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
24
|
+
throw new Error(`--threshold must be a positive integer, got ${value}`);
|
|
25
|
+
}
|
|
26
|
+
args.threshold = parsed;
|
|
27
|
+
i += 1;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
throw new Error(`Unknown compound-readiness flag: ${token}`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return args;
|
|
34
|
+
}
|
|
35
|
+
function stateDir(projectRoot) {
|
|
36
|
+
return path.join(projectRoot, RUNTIME_ROOT, "state");
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Compact one-liner for session-digest / bootstrap surfaces.
|
|
40
|
+
*
|
|
41
|
+
* Example: `Compound readiness: clusters=12, ready=2 (critical=1)`.
|
|
42
|
+
* When `ready === 0`, emit `Compound readiness: no candidates`.
|
|
43
|
+
*/
|
|
44
|
+
export function formatCompoundReadinessLine(status) {
|
|
45
|
+
if (status.readyCount === 0) {
|
|
46
|
+
return `Compound readiness: no candidates (clusters=${status.clusterCount}, threshold=${status.threshold})`;
|
|
47
|
+
}
|
|
48
|
+
const critical = status.ready.filter((cluster) => cluster.severity === "critical").length;
|
|
49
|
+
const criticalSuffix = critical > 0 ? ` (critical=${critical})` : "";
|
|
50
|
+
return `Compound readiness: clusters=${status.clusterCount}, ready=${status.readyCount}${criticalSuffix}`;
|
|
51
|
+
}
|
|
52
|
+
export async function runCompoundReadinessCommand(projectRoot, argv, io) {
|
|
53
|
+
const args = parseArgs(argv);
|
|
54
|
+
const config = await readConfig(projectRoot).catch(() => null);
|
|
55
|
+
const threshold = args.threshold ??
|
|
56
|
+
(typeof config?.compound?.recurrenceThreshold === "number"
|
|
57
|
+
? config.compound.recurrenceThreshold
|
|
58
|
+
: undefined);
|
|
59
|
+
const { entries } = await readKnowledgeSafely(projectRoot, { lockAware: false });
|
|
60
|
+
const status = computeCompoundReadiness(entries, {
|
|
61
|
+
...(typeof threshold === "number" ? { threshold } : {})
|
|
62
|
+
});
|
|
63
|
+
if (args.write) {
|
|
64
|
+
const target = path.join(stateDir(projectRoot), "compound-readiness.json");
|
|
65
|
+
await writeFileSafe(target, `${JSON.stringify(status, null, 2)}\n`);
|
|
66
|
+
}
|
|
67
|
+
if (!args.quiet) {
|
|
68
|
+
if (args.json) {
|
|
69
|
+
io.stdout.write(`${JSON.stringify(status, null, 2)}\n`);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
io.stdout.write(`${formatCompoundReadinessLine(status)}\n`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return 0;
|
|
76
|
+
}
|
|
@@ -72,6 +72,63 @@ export interface SelectRelevantLearningsOptions {
|
|
|
72
72
|
openGates?: string[];
|
|
73
73
|
limit?: number;
|
|
74
74
|
}
|
|
75
|
+
/**
|
|
76
|
+
* One clustered (trigger, action) group ready for compound lift.
|
|
77
|
+
*
|
|
78
|
+
* A cluster "qualifies" when its recurrence count meets the configured
|
|
79
|
+
* threshold **or** any contributing entry is marked `severity: "critical"`.
|
|
80
|
+
* The skill surface exposes this for nudging — it is not a gate.
|
|
81
|
+
*/
|
|
82
|
+
export interface CompoundReadinessCluster {
|
|
83
|
+
trigger: string;
|
|
84
|
+
action: string;
|
|
85
|
+
/**
|
|
86
|
+
* Sum of `frequency` across entries in the cluster — matches the
|
|
87
|
+
* recurrence count used by `/cc-ops compound`.
|
|
88
|
+
*/
|
|
89
|
+
recurrence: number;
|
|
90
|
+
/** Distinct entry lines contributing to this cluster. */
|
|
91
|
+
entryCount: number;
|
|
92
|
+
qualification: "recurrence" | "critical_override";
|
|
93
|
+
severity?: KnowledgeEntrySeverity;
|
|
94
|
+
lastSeenTs: string;
|
|
95
|
+
/** Entry types observed (rule/pattern/lesson/compound). */
|
|
96
|
+
types: KnowledgeEntryType[];
|
|
97
|
+
/** Distinct maturity values observed across the cluster. */
|
|
98
|
+
maturity: KnowledgeEntryMaturity[];
|
|
99
|
+
}
|
|
100
|
+
export interface CompoundReadiness {
|
|
101
|
+
schemaVersion: 1;
|
|
102
|
+
/** Effective recurrence threshold applied to this computation. */
|
|
103
|
+
threshold: number;
|
|
104
|
+
/** Total number of (trigger, action) clusters seen, regardless of threshold. */
|
|
105
|
+
clusterCount: number;
|
|
106
|
+
/** Number of clusters that passed the threshold or critical override. */
|
|
107
|
+
readyCount: number;
|
|
108
|
+
/**
|
|
109
|
+
* Top ready clusters (sorted by qualification severity / recurrence /
|
|
110
|
+
* recency). Capped by `maxReady` to keep the artifact small.
|
|
111
|
+
*/
|
|
112
|
+
ready: CompoundReadinessCluster[];
|
|
113
|
+
lastUpdatedAt: string;
|
|
114
|
+
}
|
|
115
|
+
export interface ComputeCompoundReadinessOptions {
|
|
116
|
+
threshold?: number;
|
|
117
|
+
/** Hard cap on `ready[]` to keep the surface digest concise. Default 10. */
|
|
118
|
+
maxReady?: number;
|
|
119
|
+
now?: Date;
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Pure function — no filesystem side effects. Callers pass entries from
|
|
123
|
+
* `readKnowledgeSafely` and get a derived readiness snapshot suitable
|
|
124
|
+
* for persisting to `.cclaw/state/compound-readiness.json`.
|
|
125
|
+
*
|
|
126
|
+
* Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
|
|
127
|
+
* which mirrors the clustering used by the `/cc-ops compound` skill.
|
|
128
|
+
* Entries with `maturity === "lifted-to-enforcement"` are excluded —
|
|
129
|
+
* they were already promoted and should not re-appear as ready.
|
|
130
|
+
*/
|
|
131
|
+
export declare function computeCompoundReadiness(entries: KnowledgeEntry[], options?: ComputeCompoundReadinessOptions): CompoundReadiness;
|
|
75
132
|
export declare function validateKnowledgeEntry(entry: unknown): {
|
|
76
133
|
ok: boolean;
|
|
77
134
|
errors: string[];
|
package/dist/knowledge-store.js
CHANGED
|
@@ -1,8 +1,115 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { DEFAULT_COMPOUND_RECURRENCE_THRESHOLD } from "./config.js";
|
|
3
4
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
4
5
|
import { stripBom, withDirectoryLock } from "./fs-utils.js";
|
|
5
6
|
import { FLOW_STAGES } from "./types.js";
|
|
7
|
+
const DEFAULT_COMPOUND_READINESS_MAX_READY = 10;
|
|
8
|
+
/**
|
|
9
|
+
* Pure function — no filesystem side effects. Callers pass entries from
|
|
10
|
+
* `readKnowledgeSafely` and get a derived readiness snapshot suitable
|
|
11
|
+
* for persisting to `.cclaw/state/compound-readiness.json`.
|
|
12
|
+
*
|
|
13
|
+
* Clustering key: `(type, normalizeText(trigger), normalizeText(action))`
|
|
14
|
+
* which mirrors the clustering used by the `/cc-ops compound` skill.
|
|
15
|
+
* Entries with `maturity === "lifted-to-enforcement"` are excluded —
|
|
16
|
+
* they were already promoted and should not re-appear as ready.
|
|
17
|
+
*/
|
|
18
|
+
export function computeCompoundReadiness(entries, options = {}) {
|
|
19
|
+
const thresholdRaw = options.threshold ?? DEFAULT_COMPOUND_RECURRENCE_THRESHOLD;
|
|
20
|
+
const threshold = Number.isInteger(thresholdRaw) && thresholdRaw >= 1
|
|
21
|
+
? thresholdRaw
|
|
22
|
+
: DEFAULT_COMPOUND_RECURRENCE_THRESHOLD;
|
|
23
|
+
const maxReadyRaw = options.maxReady ?? DEFAULT_COMPOUND_READINESS_MAX_READY;
|
|
24
|
+
const maxReady = Number.isInteger(maxReadyRaw) && maxReadyRaw >= 1
|
|
25
|
+
? maxReadyRaw
|
|
26
|
+
: DEFAULT_COMPOUND_READINESS_MAX_READY;
|
|
27
|
+
const now = options.now ?? new Date();
|
|
28
|
+
const buckets = new Map();
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
if (entry.maturity === "lifted-to-enforcement")
|
|
31
|
+
continue;
|
|
32
|
+
const key = [
|
|
33
|
+
entry.type,
|
|
34
|
+
normalizeText(entry.trigger),
|
|
35
|
+
normalizeText(entry.action)
|
|
36
|
+
].join("||");
|
|
37
|
+
const frequency = Math.max(1, Math.floor(entry.frequency));
|
|
38
|
+
const bucket = buckets.get(key);
|
|
39
|
+
if (!bucket) {
|
|
40
|
+
buckets.set(key, {
|
|
41
|
+
trigger: entry.trigger,
|
|
42
|
+
action: entry.action,
|
|
43
|
+
recurrence: frequency,
|
|
44
|
+
entryCount: 1,
|
|
45
|
+
severity: entry.severity,
|
|
46
|
+
lastSeenTs: entry.last_seen_ts,
|
|
47
|
+
types: new Set([entry.type]),
|
|
48
|
+
maturity: new Set([entry.maturity])
|
|
49
|
+
});
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
bucket.recurrence += frequency;
|
|
53
|
+
bucket.entryCount += 1;
|
|
54
|
+
bucket.types.add(entry.type);
|
|
55
|
+
bucket.maturity.add(entry.maturity);
|
|
56
|
+
if (entry.severity === "critical") {
|
|
57
|
+
bucket.severity = "critical";
|
|
58
|
+
}
|
|
59
|
+
else if (entry.severity === "important" && bucket.severity !== "critical") {
|
|
60
|
+
bucket.severity = "important";
|
|
61
|
+
}
|
|
62
|
+
if (Date.parse(entry.last_seen_ts) > Date.parse(bucket.lastSeenTs)) {
|
|
63
|
+
bucket.lastSeenTs = entry.last_seen_ts;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
const ready = [];
|
|
67
|
+
for (const bucket of buckets.values()) {
|
|
68
|
+
const criticalOverride = bucket.severity === "critical";
|
|
69
|
+
const meetsRecurrence = bucket.recurrence >= threshold;
|
|
70
|
+
if (!criticalOverride && !meetsRecurrence)
|
|
71
|
+
continue;
|
|
72
|
+
ready.push({
|
|
73
|
+
trigger: bucket.trigger,
|
|
74
|
+
action: bucket.action,
|
|
75
|
+
recurrence: bucket.recurrence,
|
|
76
|
+
entryCount: bucket.entryCount,
|
|
77
|
+
qualification: criticalOverride && !meetsRecurrence ? "critical_override" : "recurrence",
|
|
78
|
+
...(bucket.severity ? { severity: bucket.severity } : {}),
|
|
79
|
+
lastSeenTs: bucket.lastSeenTs,
|
|
80
|
+
types: Array.from(bucket.types).sort(),
|
|
81
|
+
maturity: Array.from(bucket.maturity).sort()
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
ready.sort((a, b) => {
|
|
85
|
+
const severityWeight = (sev) => {
|
|
86
|
+
if (sev === "critical")
|
|
87
|
+
return 3;
|
|
88
|
+
if (sev === "important")
|
|
89
|
+
return 2;
|
|
90
|
+
if (sev === "suggestion")
|
|
91
|
+
return 1;
|
|
92
|
+
return 0;
|
|
93
|
+
};
|
|
94
|
+
const severityDiff = severityWeight(b.severity) - severityWeight(a.severity);
|
|
95
|
+
if (severityDiff !== 0)
|
|
96
|
+
return severityDiff;
|
|
97
|
+
if (b.recurrence !== a.recurrence)
|
|
98
|
+
return b.recurrence - a.recurrence;
|
|
99
|
+
const recencyDiff = Date.parse(b.lastSeenTs) - Date.parse(a.lastSeenTs);
|
|
100
|
+
if (!Number.isNaN(recencyDiff) && recencyDiff !== 0)
|
|
101
|
+
return recencyDiff;
|
|
102
|
+
return a.trigger.localeCompare(b.trigger);
|
|
103
|
+
});
|
|
104
|
+
return {
|
|
105
|
+
schemaVersion: 1,
|
|
106
|
+
threshold,
|
|
107
|
+
clusterCount: buckets.size,
|
|
108
|
+
readyCount: ready.length,
|
|
109
|
+
ready: ready.slice(0, maxReady),
|
|
110
|
+
lastUpdatedAt: normalizeUtcIso(now.toISOString())
|
|
111
|
+
};
|
|
112
|
+
}
|
|
6
113
|
const KNOWLEDGE_TYPE_SET = new Set(["rule", "pattern", "lesson", "compound"]);
|
|
7
114
|
const KNOWLEDGE_CONFIDENCE_SET = new Set(["high", "medium", "low"]);
|
|
8
115
|
const KNOWLEDGE_SEVERITY_SET = new Set(["critical", "important", "suggestion"]);
|
package/dist/policy.js
CHANGED
|
@@ -142,6 +142,11 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
142
142
|
{ file: runtimeFile("references/protocols/decision.md"), needle: "# Decision Protocol", name: "protocol:decision" },
|
|
143
143
|
{ file: runtimeFile("references/protocols/completion.md"), needle: "# Stage Completion Protocol", name: "protocol:completion" },
|
|
144
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" },
|
|
145
150
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Resume Protocol", name: "utility_skill:session:resume" },
|
|
146
151
|
{ file: runtimeFile("skills/brainstorming/SKILL.md"), needle: "common-guidance.md", name: "stage_skill:shared_guidance_reference" },
|
|
147
152
|
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:security:hard_gate" },
|