cclaw-cli 0.55.2 → 1.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.
Files changed (72) hide show
  1. package/README.md +3 -3
  2. package/dist/artifact-linter/brainstorm.js +45 -1
  3. package/dist/artifact-linter/design.js +32 -1
  4. package/dist/artifact-linter/plan.js +22 -1
  5. package/dist/artifact-linter/review.js +35 -1
  6. package/dist/artifact-linter/scope.js +19 -9
  7. package/dist/artifact-linter/shared.d.ts +11 -10
  8. package/dist/artifact-linter/shared.js +70 -41
  9. package/dist/artifact-linter/ship.js +36 -0
  10. package/dist/artifact-linter/spec.js +23 -1
  11. package/dist/artifact-linter/tdd.js +74 -0
  12. package/dist/artifact-linter.d.ts +1 -1
  13. package/dist/constants.d.ts +1 -1
  14. package/dist/constants.js +1 -0
  15. package/dist/content/closeout-guidance.d.ts +1 -1
  16. package/dist/content/closeout-guidance.js +10 -11
  17. package/dist/content/core-agents.d.ts +35 -36
  18. package/dist/content/core-agents.js +189 -99
  19. package/dist/content/diff-command.js +1 -1
  20. package/dist/content/examples.d.ts +0 -3
  21. package/dist/content/examples.js +197 -752
  22. package/dist/content/idea.d.ts +60 -0
  23. package/dist/content/idea.js +404 -0
  24. package/dist/content/learnings.d.ts +2 -4
  25. package/dist/content/learnings.js +10 -26
  26. package/dist/content/node-hooks.js +131 -97
  27. package/dist/content/opencode-plugin.js +12 -26
  28. package/dist/content/reference-patterns.js +2 -2
  29. package/dist/content/runtime-shared-snippets.d.ts +8 -0
  30. package/dist/content/runtime-shared-snippets.js +80 -0
  31. package/dist/content/session-hooks.js +1 -1
  32. package/dist/content/skills.d.ts +1 -0
  33. package/dist/content/skills.js +50 -0
  34. package/dist/content/stage-schema.js +107 -63
  35. package/dist/content/stages/review.js +8 -8
  36. package/dist/content/stages/schema-types.d.ts +2 -2
  37. package/dist/content/stages/scope.js +1 -1
  38. package/dist/content/stages/ship.js +1 -1
  39. package/dist/content/status-command.js +3 -3
  40. package/dist/content/subagent-context-skills.js +156 -1
  41. package/dist/content/subagents.d.ts +0 -5
  42. package/dist/content/subagents.js +12 -82
  43. package/dist/content/templates.js +87 -6
  44. package/dist/content/utility-skills.js +26 -97
  45. package/dist/flow-state.d.ts +5 -6
  46. package/dist/flow-state.js +4 -6
  47. package/dist/gate-evidence.d.ts +0 -31
  48. package/dist/gate-evidence.js +3 -181
  49. package/dist/harness-adapters.js +1 -1
  50. package/dist/install.js +38 -4
  51. package/dist/internal/advance-stage/advance.js +0 -1
  52. package/dist/internal/advance-stage/review-loop.js +1 -10
  53. package/dist/knowledge-store.d.ts +2 -20
  54. package/dist/knowledge-store.js +43 -57
  55. package/dist/policy.js +3 -3
  56. package/dist/retro-gate.js +8 -90
  57. package/dist/run-archive.js +1 -4
  58. package/dist/run-persistence.js +14 -109
  59. package/dist/runtime/run-hook.entry.d.ts +3 -0
  60. package/dist/runtime/run-hook.entry.js +5 -0
  61. package/dist/runtime/run-hook.mjs +9477 -0
  62. package/package.json +4 -2
  63. package/dist/content/hook-inline-snippets.d.ts +0 -96
  64. package/dist/content/hook-inline-snippets.js +0 -515
  65. package/dist/content/idea-command.d.ts +0 -8
  66. package/dist/content/idea-command.js +0 -322
  67. package/dist/content/idea-frames.d.ts +0 -31
  68. package/dist/content/idea-frames.js +0 -140
  69. package/dist/content/idea-ranking.d.ts +0 -25
  70. package/dist/content/idea-ranking.js +0 -65
  71. package/dist/trace-matrix.d.ts +0 -27
  72. package/dist/trace-matrix.js +0 -226
@@ -0,0 +1,60 @@
1
+ export type IdeaFrameId = "pain-friction" | "assumption-break" | "cross-domain-analogy";
2
+ export interface IdeaFrame {
3
+ id: IdeaFrameId;
4
+ label: string;
5
+ prompt: string;
6
+ examplePatterns: string[];
7
+ }
8
+ export interface IdeaFrameDispatchInput {
9
+ focus: string;
10
+ mode: "repo-grounded" | "elsewhere-software" | "elsewhere-non-software";
11
+ signalSummary: string[];
12
+ }
13
+ export interface IdeaFrameDispatchPlanEntry {
14
+ frameId: IdeaFrameId;
15
+ label: string;
16
+ prompt: string;
17
+ }
18
+ export interface IdeaCandidateDraft {
19
+ title: string;
20
+ evidencePath: string;
21
+ summary: string;
22
+ frameId: IdeaFrameId;
23
+ }
24
+ export interface IdeaCandidateMerged extends Omit<IdeaCandidateDraft, "frameId"> {
25
+ frameIds: IdeaFrameId[];
26
+ }
27
+ export declare const DEFAULT_IDEA_FRAME_IDS: readonly IdeaFrameId[];
28
+ export declare const IDEA_FRAMES: readonly IdeaFrame[];
29
+ export declare function resolveIdeaFrames(frameIds?: readonly IdeaFrameId[]): IdeaFrame[];
30
+ export declare function buildIdeaFrameDispatchPlan(input: IdeaFrameDispatchInput, frameIds?: readonly IdeaFrameId[]): IdeaFrameDispatchPlanEntry[];
31
+ export declare function dedupeIdeaCandidates(drafts: readonly IdeaCandidateDraft[]): IdeaCandidateMerged[];
32
+ export type IdeaImpact = "high" | "medium" | "low";
33
+ export type IdeaEffort = "s" | "m" | "l";
34
+ export type IdeaConfidence = "high" | "medium" | "low";
35
+ export interface IdeaCandidateEvaluationInput {
36
+ id: string;
37
+ title: string;
38
+ impact: IdeaImpact;
39
+ effort: IdeaEffort;
40
+ confidence: IdeaConfidence;
41
+ }
42
+ export interface IdeaCandidateEvaluation extends IdeaCandidateEvaluationInput {
43
+ disposition: "survivor" | "rejected";
44
+ rankingScore: number;
45
+ }
46
+ export interface IdeaRankingResult {
47
+ survivors: IdeaCandidateEvaluation[];
48
+ rejected: IdeaCandidateEvaluation[];
49
+ recommendationId: string | null;
50
+ }
51
+ export declare function scoreIdeaCandidate(impact: IdeaImpact, effort: IdeaEffort, confidence: IdeaConfidence): number;
52
+ export declare function evaluateIdeaCandidate(input: IdeaCandidateEvaluationInput): IdeaCandidateEvaluation;
53
+ export declare function rankIdeaCandidates(inputs: readonly IdeaCandidateEvaluationInput[], maxSurvivors?: number): IdeaRankingResult;
54
+ export interface IdeaCommandOptions {
55
+ frameIds?: readonly IdeaFrameId[];
56
+ mode?: "repo-grounded" | "elsewhere-software" | "elsewhere-non-software" | "narrow";
57
+ }
58
+ export declare function minimumDistinctIdeaFrames(frameCount: number, mode?: IdeaCommandOptions["mode"]): number;
59
+ export declare function ideaCommandContract(options?: IdeaCommandOptions): string;
60
+ export declare function ideaCommandSkillMarkdown(options?: IdeaCommandOptions): string;
@@ -0,0 +1,404 @@
1
+ import { RUNTIME_ROOT } from "../constants.js";
2
+ import { ideaStructuredAskToolsWithFallback } from "./decision-protocol.js";
3
+ import { conversationLanguagePolicyMarkdown } from "./language-policy.js";
4
+ const FRAME_REGISTRY = {
5
+ "pain-friction": {
6
+ id: "pain-friction",
7
+ label: "pain/friction",
8
+ prompt: "Find repeated friction in the repo workflow. Prioritize changes that eliminate recurring toil, fragile handoffs, or repeated manual recovery.",
9
+ examplePatterns: [
10
+ "Repeated TODO/FIXME hotspots in one subsystem",
11
+ "Flows that require manual retries or ad-hoc scripts",
12
+ "Developer loops slowed by avoidable boilerplate"
13
+ ]
14
+ },
15
+ "assumption-break": {
16
+ id: "assumption-break",
17
+ label: "assumption-break",
18
+ prompt: "List the top assumptions behind the current approach and break one: what if that assumption is wrong, incomplete, or expensive to maintain?",
19
+ examplePatterns: [
20
+ "An approval gate currently assumed to be always manual can be automated safely",
21
+ "A broad required section can be optional with measurable trigger conditions",
22
+ "A long checklist step can be replaced by a deterministic verifier"
23
+ ]
24
+ },
25
+ "cross-domain-analogy": {
26
+ id: "cross-domain-analogy",
27
+ label: "cross-domain-analogy",
28
+ prompt: "Borrow one proven pattern from an adjacent domain and map it to this repository's constraints.",
29
+ examplePatterns: [
30
+ "Treat post-ship closeout like incident-response runbooks with explicit ownership",
31
+ "Apply API compatibility techniques to artifact schema evolution",
32
+ "Use observability SLO-style thresholds for process-quality gates"
33
+ ]
34
+ }
35
+ };
36
+ export const DEFAULT_IDEA_FRAME_IDS = Object.freeze([
37
+ "pain-friction",
38
+ "assumption-break",
39
+ "cross-domain-analogy"
40
+ ]);
41
+ export const IDEA_FRAMES = Object.freeze(DEFAULT_IDEA_FRAME_IDS.map((id) => FRAME_REGISTRY[id]));
42
+ export function resolveIdeaFrames(frameIds) {
43
+ if (!frameIds || frameIds.length === 0) {
44
+ return [...IDEA_FRAMES];
45
+ }
46
+ const seen = new Set();
47
+ const resolved = [];
48
+ for (const rawId of frameIds) {
49
+ if (!DEFAULT_IDEA_FRAME_IDS.includes(rawId)) {
50
+ throw new Error(`Unknown idea frame id: ${rawId}`);
51
+ }
52
+ if (seen.has(rawId))
53
+ continue;
54
+ seen.add(rawId);
55
+ resolved.push(FRAME_REGISTRY[rawId]);
56
+ }
57
+ return resolved;
58
+ }
59
+ export function buildIdeaFrameDispatchPlan(input, frameIds) {
60
+ const signalBlock = input.signalSummary.length > 0
61
+ ? input.signalSummary.map((line) => `- ${line}`).join("\n")
62
+ : "- no pre-scan signals captured yet";
63
+ return resolveIdeaFrames(frameIds).map((frame) => ({
64
+ frameId: frame.id,
65
+ label: frame.label,
66
+ prompt: [
67
+ `Frame: ${frame.label} (${frame.id})`,
68
+ `Mode: ${input.mode}`,
69
+ `Focus: ${input.focus || "open-ended scan"}`,
70
+ "",
71
+ "Signal summary:",
72
+ signalBlock,
73
+ "",
74
+ `Frame prompt: ${frame.prompt}`,
75
+ "",
76
+ "Generate 3-5 concrete candidates with repo-grounded evidence."
77
+ ].join("\n")
78
+ }));
79
+ }
80
+ function normalizeCandidateKey(title, evidencePath) {
81
+ const normalizedTitle = title.trim().toLowerCase().replace(/[^a-z0-9]+/gu, " ").trim();
82
+ const normalizedEvidence = evidencePath
83
+ .trim()
84
+ .toLowerCase()
85
+ .replace(/\\/gu, "/");
86
+ return `${normalizedTitle}::${normalizedEvidence}`;
87
+ }
88
+ export function dedupeIdeaCandidates(drafts) {
89
+ const merged = new Map();
90
+ for (const draft of drafts) {
91
+ const key = normalizeCandidateKey(draft.title, draft.evidencePath);
92
+ const existing = merged.get(key);
93
+ if (!existing) {
94
+ merged.set(key, {
95
+ title: draft.title,
96
+ evidencePath: draft.evidencePath,
97
+ summary: draft.summary,
98
+ frameIds: [draft.frameId]
99
+ });
100
+ continue;
101
+ }
102
+ if (!existing.frameIds.includes(draft.frameId)) {
103
+ existing.frameIds.push(draft.frameId);
104
+ }
105
+ if (draft.summary.length > existing.summary.length) {
106
+ existing.summary = draft.summary;
107
+ }
108
+ }
109
+ return [...merged.values()];
110
+ }
111
+ const IMPACT_PRIORITY = {
112
+ high: 3,
113
+ medium: 2,
114
+ low: 1
115
+ };
116
+ const EFFORT_PRIORITY = {
117
+ s: 0,
118
+ m: 1,
119
+ l: 2
120
+ };
121
+ const CONFIDENCE_PRIORITY = {
122
+ high: 2,
123
+ medium: 1,
124
+ low: 0
125
+ };
126
+ function isRejectedIdea(input) {
127
+ if (input.confidence === "low")
128
+ return true;
129
+ if (input.impact === "low" && input.effort === "l")
130
+ return true;
131
+ return false;
132
+ }
133
+ export function scoreIdeaCandidate(impact, effort, confidence) {
134
+ // Keep the scoring intentionally simple and monotonic.
135
+ return IMPACT_PRIORITY[impact] + CONFIDENCE_PRIORITY[confidence] - EFFORT_PRIORITY[effort];
136
+ }
137
+ export function evaluateIdeaCandidate(input) {
138
+ const disposition = isRejectedIdea(input) ? "rejected" : "survivor";
139
+ return {
140
+ ...input,
141
+ disposition,
142
+ rankingScore: scoreIdeaCandidate(input.impact, input.effort, input.confidence)
143
+ };
144
+ }
145
+ export function rankIdeaCandidates(inputs, maxSurvivors = 10) {
146
+ const evaluated = inputs.map(evaluateIdeaCandidate);
147
+ const survivors = evaluated
148
+ .filter((candidate) => candidate.disposition === "survivor")
149
+ .sort((left, right) => {
150
+ // Deterministic ordering: impact > effort > confidence > id.
151
+ const impactDelta = IMPACT_PRIORITY[right.impact] - IMPACT_PRIORITY[left.impact];
152
+ if (impactDelta !== 0)
153
+ return impactDelta;
154
+ const effortDelta = EFFORT_PRIORITY[left.effort] - EFFORT_PRIORITY[right.effort];
155
+ if (effortDelta !== 0)
156
+ return effortDelta;
157
+ const confidenceDelta = CONFIDENCE_PRIORITY[right.confidence] - CONFIDENCE_PRIORITY[left.confidence];
158
+ if (confidenceDelta !== 0)
159
+ return confidenceDelta;
160
+ return left.id.localeCompare(right.id);
161
+ })
162
+ .slice(0, Math.max(0, maxSurvivors));
163
+ const survivorIds = new Set(survivors.map((candidate) => candidate.id));
164
+ const rejected = evaluated
165
+ .filter((candidate) => candidate.disposition === "rejected" || !survivorIds.has(candidate.id))
166
+ .sort((left, right) => left.id.localeCompare(right.id));
167
+ return {
168
+ survivors,
169
+ rejected,
170
+ recommendationId: survivors[0]?.id ?? null
171
+ };
172
+ }
173
+ const IDEA_SKILL_FOLDER = "flow-idea";
174
+ const IDEA_SKILL_NAME = "flow-idea";
175
+ const IDEA_ARTIFACT_GLOB = ".cclaw/artifacts/idea-*.md";
176
+ const IDEA_ARTIFACT_PATTERN = ".cclaw/artifacts/idea-<YYYY-MM-DD-slug>.md";
177
+ const IDEA_RESUME_WINDOW_DAYS = 30;
178
+ const STRUCTURED_ASK_TOOLS = ideaStructuredAskToolsWithFallback();
179
+ export function minimumDistinctIdeaFrames(frameCount, mode = "repo-grounded") {
180
+ if (frameCount <= 0)
181
+ return 0;
182
+ const cap = mode === "repo-grounded" ? 3 : 2;
183
+ return Math.min(cap, frameCount);
184
+ }
185
+ function renderFrameBullets(frameIds) {
186
+ return resolveIdeaFrames(frameIds)
187
+ .map((frame) => ` - ${frame.label} (\`${frame.id}\`)`)
188
+ .join("\n");
189
+ }
190
+ function renderFrameNames(frameIds) {
191
+ return resolveIdeaFrames(frameIds)
192
+ .map((frame) => frame.label)
193
+ .join(", ");
194
+ }
195
+ export function ideaCommandContract(options = {}) {
196
+ const frames = resolveIdeaFrames(options.frameIds);
197
+ const frameBullets = renderFrameBullets(options.frameIds);
198
+ const minimumDistinctFrames = minimumDistinctIdeaFrames(frames.length, options.mode);
199
+ return `# /cc-idea
200
+
201
+ ## Purpose
202
+
203
+ Repository-improvement idea mode. Generate a ranked backlog of
204
+ high-value improvements, persist it as an artifact on disk, and end with
205
+ an explicit handoff - either launch \`/cc\` on a chosen candidate in the
206
+ same session, or save/discard the backlog.
207
+
208
+ ## HARD-GATE
209
+
210
+ ${conversationLanguagePolicyMarkdown()}
211
+ - Idea mode only. Never mutate \`.cclaw/state/flow-state.json\`.
212
+ - Every recommendation cites evidence from the current repository
213
+ (file path, command output, or knowledge-store entry id).
214
+ - Whenever you produce ideation output, persist it to
215
+ \`${IDEA_ARTIFACT_PATTERN}\`. Chat-only output is not acceptable.
216
+ The only exception is an explicit user-cancel from the resume prompt -
217
+ in that case, write nothing and exit silently.
218
+ - Always end with a structured handoff prompt, not an open question
219
+ (skipped on explicit cancel).
220
+
221
+ ## Algorithm
222
+
223
+ 1. **Resume check.** Glob \`${IDEA_ARTIFACT_GLOB}\`. If any artifact
224
+ has been modified within the last ${IDEA_RESUME_WINDOW_DAYS} days,
225
+ offer the user: continue that backlog, start fresh, or cancel.
226
+ 2. **Mode classification.** Explicitly classify subject:
227
+ \`repo-grounded\` / \`elsewhere-software\` / \`elsewhere-non-software\` / \`narrow\`.
228
+ 3. **Mode-aware grounding (parallel).**
229
+ - Repo-grounded: repo signal scan + \`${RUNTIME_ROOT}/knowledge.jsonl\`
230
+ repetition scan.
231
+ - Elsewhere-software: docs-first grounding (Context7 and official docs).
232
+ - Elsewhere-non-software: constraints and objective grounding.
233
+ 4. **Divergent ideation frames (parallel).** Generate candidates with
234
+ configured frames (${frames.length} total):
235
+ ${frameBullets}
236
+ Keep at least ${minimumDistinctFrames} distinct frame outputs in this rendered mode.
237
+ Deterministic minimum: repo-grounded = 3, narrow/non-repo = 2, always capped
238
+ by configured frame count.
239
+ 5. **Adversarial critique pass.** For each candidate, write the strongest
240
+ counter-argument, kill weak ideas, and keep survivors only.
241
+ 6. **Produce 5-10 survivors** with impact (High/Medium/Low),
242
+ effort (S/M/L), confidence (High/Medium/Low), **why now**, expected user impact, risk, and one evidence path per
243
+ survivor.
244
+ 7. **Rank by simple triage order**: impact first, then effort, then confidence.
245
+ Reject low-confidence ideas and obvious low-impact/high-effort outliers.
246
+ Recommend the top survivor.
247
+ 8. **Write the artifact** at
248
+ \`${IDEA_ARTIFACT_PATTERN}\` using the schema in the skill.
249
+ 9. **Present the handoff prompt** with four concrete options - not A/B/C
250
+ letters. Default = "Start /cc on the top recommendation".
251
+
252
+ ## Headless mode (CI/automation only)
253
+
254
+ Headless envelopes are a machine-mode exception for CI/automation orchestration.
255
+ In normal interactive ideation, respond with natural language plus the artifact path.
256
+ For skill-to-skill invocation, emit exactly one JSON envelope:
257
+
258
+ \`\`\`json
259
+ {"version":"1","kind":"stage-output","stage":"non-flow","payload":{"command":"/cc-idea","artifact":".cclaw/artifacts/idea-<date>-<slug>.md","recommendation":"I-1"},"emittedAt":"<ISO-8601>"}
260
+ \`\`\`
261
+
262
+ Validate envelopes with:
263
+ \`npx cclaw-cli internal envelope-validate --stdin\`
264
+
265
+ ## Primary skill
266
+
267
+ **${RUNTIME_ROOT}/skills/${IDEA_SKILL_FOLDER}/SKILL.md**
268
+ `;
269
+ }
270
+ export function ideaCommandSkillMarkdown(options = {}) {
271
+ const frames = resolveIdeaFrames(options.frameIds);
272
+ const frameBullets = renderFrameBullets(options.frameIds);
273
+ const minimumDistinctFrames = minimumDistinctIdeaFrames(frames.length, options.mode);
274
+ const frameNames = renderFrameNames(options.frameIds);
275
+ return `---
276
+ name: ${IDEA_SKILL_NAME}
277
+ description: "Repository idea mode: detect and rank high-leverage improvements, persist a backlog artifact, and hand off to /cc or save/discard."
278
+ ---
279
+
280
+ # /cc-idea
281
+
282
+ ## Announce at start
283
+
284
+ "Using flow-idea to identify highest-leverage improvements in this
285
+ repository. Will persist a ranked backlog to
286
+ \`${IDEA_ARTIFACT_PATTERN}\` and end with an explicit handoff."
287
+
288
+ ## HARD-GATE
289
+
290
+ ${conversationLanguagePolicyMarkdown()}
291
+ - Do not start coding in idea mode.
292
+ - Do not mutate \`.cclaw/state/flow-state.json\` - idea mode sits outside
293
+ the critical-path flow.
294
+ - Whenever ideation output is produced, persist the artifact file on disk
295
+ before presenting the handoff. The only exception is an explicit user-cancel
296
+ from the resume prompt - in that case, write nothing and exit silently.
297
+ - Always end with a structured handoff that names the concrete follow-up
298
+ command for each option (skipped on explicit cancel). No A/B/C letters
299
+ without command context.
300
+
301
+ ## Protocol
302
+
303
+ ### Phase 0 - Resume and classify
304
+
305
+ 1. Use the harness's file-glob tool (\`Glob\` pattern
306
+ \`${IDEA_ARTIFACT_GLOB}\` or equivalent \`ls\`).
307
+ 2. Filter to files modified within the last ${IDEA_RESUME_WINDOW_DAYS} days.
308
+ 3. If one or more match, present **one** structured ask using the
309
+ harness's native tool (${STRUCTURED_ASK_TOOLS}) with options:
310
+ - **Continue the existing backlog**.
311
+ - **Start a fresh scan**.
312
+ - **Cancel**.
313
+ 4. If no recent artifact exists, proceed to Phase 1 silently.
314
+ 5. Classify the ideation mode before grounding:
315
+ - \`repo-grounded\`
316
+ - \`elsewhere-software\`
317
+ - \`elsewhere-non-software\`
318
+ - \`narrow\`
319
+ 6. Record the chosen mode in the artifact.
320
+
321
+ ### Phase 1 - Mode-aware grounding
322
+
323
+ Run grounding in parallel where available:
324
+
325
+ - For \`repo-grounded\`:
326
+ - \`rg -n 'TODO|FIXME|XXX|HACK|TBD'\` grouped by file.
327
+ - Test-runner output (\`npm test\`, \`pytest\`, \`go test ./...\`) - note
328
+ failures, timeouts, deprecation warnings.
329
+ - Module size outliers with weak direct test coverage.
330
+ - Docs drift checks for stale references.
331
+ - \`${RUNTIME_ROOT}/knowledge.jsonl\` entries where \`type\` is
332
+ \`rule | pattern | lesson | compound\`, with repeated \`trigger/action\`
333
+ pairs and stable high-confidence patterns.
334
+ - For \`elsewhere-software\`:
335
+ - Gather current framework/library docs first.
336
+ - Add one comparison scan for established solutions.
337
+ - For \`elsewhere-non-software\`:
338
+ - Capture objective, constraints, and measured friction before proposing fixes.
339
+
340
+ Record each finding with exact evidence (path, command, or doc source).
341
+
342
+ ### Phase 2 - Divergent ideation
343
+
344
+ Generate candidate ideas by frame, in parallel when possible:
345
+
346
+ ${frameBullets}
347
+
348
+ Require at least ${minimumDistinctFrames} distinct frames in this rendered mode. The
349
+ runtime rule is deterministic: repo-grounded scans require 3 distinct frames;
350
+ narrow, elsewhere-software, and elsewhere-non-software runs require 2; all modes
351
+ are capped by the configured frame count.
352
+
353
+ ### Phase 3 - Critique all, keep survivors
354
+
355
+ For each raw candidate:
356
+ - Write strongest argument **against** this idea.
357
+ - Identify disqualifiers (duplicate, weak evidence, poor ROI, wrong timing).
358
+ - Mark as \`survivor\` or \`rejected\`.
359
+
360
+ Only survivors advance to ranking.
361
+
362
+ ### Phase 4 - Rank and write the artifact
363
+
364
+ 1. Keep 5-10 survivors.
365
+ 2. For each survivor, include:
366
+ - **ID** - \`I-1\`, \`I-2\`, ...
367
+ - **Title**
368
+ - **Impact** - High / Medium / Low
369
+ - **Effort** - S / M / L
370
+ - **Confidence** - High / Medium / Low
371
+ - **Evidence**
372
+ - **Why now**
373
+ - **Expected impact**
374
+ - **Risk**
375
+ - **Counter-argument**
376
+ - **Next /cc prompt**
377
+ 3. Sort survivors by impact, then effort, then confidence.
378
+ 4. Write \`.cclaw/artifacts/idea-<date>-<slug>.md\`.
379
+ 5. Confirm in chat: "Wrote <path>".
380
+
381
+ ### Phase 5 - Handoff prompt
382
+
383
+ Present one structured ask with exactly these options (no bare A/B/C):
384
+ Required options, in this order:
385
+ 1. **Start /cc on the top recommendation** (default)
386
+ 2. **Pick a different candidate**
387
+ 3. **Save and close**
388
+ 4. **Discard**
389
+
390
+ ### Phase 6 - Execute the choice
391
+
392
+ - Start /cc: load \`${RUNTIME_ROOT}/skills/using-cclaw/SKILL.md\` and run
393
+ \`/cc <phrase>\`.
394
+ - Save and close: reply with artifact path and stop.
395
+ - Discard: delete the artifact and stop.
396
+
397
+ ## Do not
398
+
399
+ - Do not write into \`.cclaw/artifacts/0X-*.md\` (stage artifacts).
400
+ - Do not mutate \`.cclaw/state/flow-state.json\`.
401
+ - Do not collapse all ideas into one frame; distribute across:
402
+ ${frameNames}.
403
+ `;
404
+ }
@@ -1,8 +1,6 @@
1
1
  /**
2
2
  * Canonical required JSONL field order (matches strict validator keys).
3
- * Optional keys (`source`, `severity`, `supersedes`, `superseded_by`) may
4
- * be appended after these required fields.
5
- * Exported for tests and any programmatic writer that wants a stable base shape.
3
+ * Optional keys (`source`, `severity`) may be appended after these required fields.
6
4
  */
7
- export declare const KNOWLEDGE_JSONL_FIELDS: readonly ["type", "trigger", "action", "confidence", "domain", "stage", "origin_stage", "origin_run", "frequency", "universality", "maturity", "created", "first_seen_ts", "last_seen_ts", "project"];
5
+ export declare const KNOWLEDGE_JSONL_FIELDS: readonly ["type", "trigger", "action", "confidence", "stage", "origin_stage", "frequency", "created", "first_seen_ts", "last_seen_ts", "project"];
8
6
  export declare function learnSkillMarkdown(): string;
@@ -11,22 +11,16 @@ const LEARN_SKILL_NAME = "learnings";
11
11
  const LEARN_SKILL_DESCRIPTION = "Project-scoped knowledge store: append and query rule/pattern/lesson/compound entries in the canonical JSONL file at .cclaw/knowledge.jsonl. Strict schema, append-only, machine-queryable.";
12
12
  /**
13
13
  * Canonical required JSONL field order (matches strict validator keys).
14
- * Optional keys (`source`, `severity`, `supersedes`, `superseded_by`) may
15
- * be appended after these required fields.
16
- * Exported for tests and any programmatic writer that wants a stable base shape.
14
+ * Optional keys (`source`, `severity`) may be appended after these required fields.
17
15
  */
18
16
  export const KNOWLEDGE_JSONL_FIELDS = [
19
17
  "type",
20
18
  "trigger",
21
19
  "action",
22
20
  "confidence",
23
- "domain",
24
21
  "stage",
25
22
  "origin_stage",
26
- "origin_run",
27
23
  "frequency",
28
- "universality",
29
- "maturity",
30
24
  "created",
31
25
  "first_seen_ts",
32
26
  "last_seen_ts",
@@ -74,14 +68,14 @@ During manual knowledge operations, only modify \`${KNOWLEDGE_PATH}\`, \`${KNOWL
74
68
  or an explicitly user-approved summary file. Do not modify application code here.
75
69
  Do not invent alternate stores (no markdown mirror, no SQLite, no per-stage files).
76
70
 
77
- ## Entry format strict JSONL schema
71
+ ## Entry format - strict JSONL schema
78
72
 
79
73
  Exactly one JSON object per line. Required fields must appear in the order:
80
- \`type, trigger, action, confidence, domain, stage, origin_stage, origin_run, frequency, universality, maturity, created, first_seen_ts, last_seen_ts, project\`.
81
- Optional fields \`source\`, \`severity\`, \`supersedes\`, and \`superseded_by\` may be appended after \`project\`.
74
+ \`type, trigger, action, confidence, stage, origin_stage, frequency, created, first_seen_ts, last_seen_ts, project\`.
75
+ Optional fields \`source\` and \`severity\` may be appended after \`project\`.
82
76
 
83
77
  \`\`\`json
84
- {"type":"pattern","trigger":"when reviewing external payloads","action":"parse through zod before touching service layer","confidence":"high","domain":"api","stage":"review","origin_stage":"review","origin_run":"payload-hardening","frequency":1,"universality":"project","maturity":"raw","created":"2026-04-14T12:00:00Z","first_seen_ts":"2026-04-14T12:00:00Z","last_seen_ts":"2026-04-14T12:00:00Z","project":"cclaw"}
78
+ {"type":"pattern","trigger":"when reviewing external payloads","action":"parse through zod before touching service layer","confidence":"high","stage":"review","origin_stage":"review","frequency":1,"created":"2026-04-14T12:00:00Z","first_seen_ts":"2026-04-14T12:00:00Z","last_seen_ts":"2026-04-14T12:00:00Z","project":"cclaw"}
85
79
  \`\`\`
86
80
 
87
81
  | field | type | required | notes |
@@ -90,28 +84,19 @@ Optional fields \`source\`, \`severity\`, \`supersedes\`, and \`superseded_by\`
90
84
  | \`trigger\` | string | yes | The concrete situation that must be recognized. Start with a verb or \`when …\`. |
91
85
  | \`action\` | string | yes | The concrete move to take when the trigger fires. One sentence. |
92
86
  | \`confidence\` | \`"high" \\| "medium" \\| "low"\` | yes | Write \`medium\` when unsure; do not omit. |
93
- | \`domain\` | string \\| null | yes | Free-form taxonomy (\`api\`, \`infra\`, \`ui\`, \`security\`, \`testing\`, …). Use \`null\` when cross-cutting. |
94
87
  | \`stage\` | \`FlowStage\` \\| null | yes | One of brainstorm / scope / design / spec / plan / tdd / review / ship, or \`null\` when cross-stage. |
95
88
  | \`origin_stage\` | \`FlowStage\` \\| null | yes | Stage where this learning was first observed. |
96
- | \`origin_run\` | string \\| null | yes | Optional run, branch, or topic label where it was observed first. |
97
89
  | \`frequency\` | integer >= 1 | yes | Number of times this same trigger/action pair has been observed. |
98
- | \`universality\` | \`"project" \\| "personal" \\| "universal"\` | yes | Scope of applicability. |
99
- | \`maturity\` | \`"raw" \\| "lifted-to-rule" \\| "lifted-to-enforcement"\` | yes | Lifecycle state of the learning. |
100
90
  | \`created\` | ISO 8601 UTC string | yes | \`date -u +%Y-%m-%dT%H:%M:%SZ\`. |
101
91
  | \`first_seen_ts\` | ISO 8601 UTC string | yes | First observed timestamp (usually equals \`created\`). |
102
92
  | \`last_seen_ts\` | ISO 8601 UTC string | yes | Last re-confirmed timestamp. |
103
93
  | \`project\` | string \\| null | yes | Repo or scope name. Use \`null\` when the entry crosses projects. |
104
94
  | \`source\` | \`"stage" \\| "retro" \\| "compound" \\| "idea" \\| "manual" \\| null\` | no | Origin channel for the entry when known. |
105
95
  | \`severity\` | \`"critical" \\| "important" \\| "suggestion"\` | no | Priority signal for compound lifts; \`critical\` enables single-hit override in compound readiness analysis. |
106
- | \`supersedes\` | string[] | no | Non-empty IDs/slugs of older entries this entry refreshes. Use only for clear replacements discovered during compound closeout or curation. |
107
- | \`superseded_by\` | string | no | Non-empty ID/slug of the newer entry that refreshes this one. Use only when marking stale guidance as replaced. |
108
96
 
109
97
  Rules:
110
98
  - No other fields beyond the table above. Extra keys are forbidden and MUST be rejected by any writer.
111
- - Every required-null field must be emitted explicitly as \`null\` (not omitted). This keeps the file grep-friendly.
112
- - Append-only: never rewrite or delete a historical line. Corrections are new
113
- entries whose \`trigger\` clearly supersedes the earlier one; add \`supersedes\` /
114
- \`superseded_by\` only when the replacement relationship is clear.
99
+ - Append-only: never rewrite or delete a historical line.
115
100
  - Keep each entry one line. No pretty-printing. No trailing commas.
116
101
 
117
102
  ## Curation policy (target: ≤ 50 active entries)
@@ -133,17 +118,16 @@ Rules:
133
118
 
134
119
  ### Search \`<query>\`
135
120
  - Stream \`${KNOWLEDGE_PATH}\`, JSON.parse each line, filter where any of
136
- \`trigger\`, \`action\`, \`domain\`, \`project\` contains \`<query>\` (case-insensitive).
121
+ \`trigger\`, \`action\`, \`project\` contains \`<query>\` (case-insensitive).
137
122
  - Return the matched lines pretty-printed (do not mutate the file).
138
123
 
139
124
  ### Add
140
- - Ask for required user-facing fields in order: \`type\`, \`trigger\`, \`action\`, \`confidence\`, \`domain\`, \`stage\`, \`universality\`, \`project\`.
125
+ - Ask for required user-facing fields in order: \`type\`, \`trigger\`, \`action\`, \`confidence\`, \`stage\`, \`project\`.
141
126
  - \`confidence\` must be one of \`high\`, \`medium\`, \`low\`. Default to \`medium\` if the user declines to set it.
142
- - \`domain\`, \`stage\`, and \`project\` may be explicitly \`null\`.
127
+ - \`stage\` and \`project\` may be explicitly \`null\`.
143
128
  - Prefer stage-native \`## Learnings\` capture for new flow work; use \`add\` mainly for backfilling historical lessons or ad-hoc entries outside a stage closeout.
144
- - \`origin_stage\` defaults to \`stage\`; \`origin_run\` defaults to the current run, branch, or topic label (or \`null\` if unknown).
129
+ - \`origin_stage\` defaults to \`stage\`.
145
130
  - \`frequency\` starts at \`1\`.
146
- - \`maturity\` starts at \`raw\`.
147
131
  - \`created\`, \`first_seen_ts\`, and \`last_seen_ts\` are set automatically to current UTC ISO timestamp.
148
132
  - Append exactly one JSON line to \`${KNOWLEDGE_PATH}\` with the field order from the schema table above.
149
133
  - Re-read the file tail to confirm the new line is valid JSON and parses back to the same object.