cclaw-cli 0.48.30 → 0.48.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-linter.js +609 -10
- package/dist/config.d.ts +1 -1
- package/dist/config.js +82 -4
- package/dist/content/examples.js +23 -6
- package/dist/content/ideate-command.d.ts +6 -2
- package/dist/content/ideate-command.js +43 -16
- package/dist/content/ideate-frames.d.ts +31 -0
- package/dist/content/ideate-frames.js +140 -0
- package/dist/content/ideate-ranking.d.ts +25 -0
- package/dist/content/ideate-ranking.js +65 -0
- package/dist/content/review-loop.d.ts +192 -0
- package/dist/content/review-loop.js +689 -0
- package/dist/content/seed-shelf.d.ts +36 -0
- package/dist/content/seed-shelf.js +236 -0
- package/dist/content/skills.js +84 -67
- package/dist/content/stage-schema.d.ts +1 -1
- package/dist/content/stage-schema.js +14 -2
- package/dist/content/stages/brainstorm.js +15 -4
- package/dist/content/stages/design.js +31 -8
- package/dist/content/stages/schema-types.d.ts +10 -0
- package/dist/content/stages/scope.js +17 -6
- package/dist/content/start-command.js +24 -18
- package/dist/content/templates.js +108 -4
- package/dist/internal/advance-stage.js +143 -1
- package/dist/trace-matrix.d.ts +14 -0
- package/dist/trace-matrix.js +55 -1
- package/dist/types.d.ts +27 -0
- package/package.json +1 -1
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
export interface SeedShelfEntry {
|
|
2
|
+
fileName: string;
|
|
3
|
+
absPath: string;
|
|
4
|
+
relPath: string;
|
|
5
|
+
createdOn: string;
|
|
6
|
+
title: string;
|
|
7
|
+
triggerWhen: string[];
|
|
8
|
+
sourceStage: string | null;
|
|
9
|
+
sourceArtifact: string | null;
|
|
10
|
+
hypothesis: string | null;
|
|
11
|
+
action: string | null;
|
|
12
|
+
summary: string;
|
|
13
|
+
raw: string;
|
|
14
|
+
}
|
|
15
|
+
export interface SeedTemplateInput {
|
|
16
|
+
title: string;
|
|
17
|
+
triggerWhen: readonly string[];
|
|
18
|
+
hypothesis: string;
|
|
19
|
+
action: string;
|
|
20
|
+
sourceStage?: string;
|
|
21
|
+
sourceArtifact?: string;
|
|
22
|
+
createdAt?: Date;
|
|
23
|
+
}
|
|
24
|
+
export interface ResolvedSeedPath {
|
|
25
|
+
fileName: string;
|
|
26
|
+
absPath: string;
|
|
27
|
+
relPath: string;
|
|
28
|
+
}
|
|
29
|
+
export declare function seedShelfDir(projectRoot: string): string;
|
|
30
|
+
export declare function seedSlug(title: string): string;
|
|
31
|
+
export declare function seedFileName(title: string, createdAt?: Date): string;
|
|
32
|
+
export declare function resolveSeedPathForWrite(projectRoot: string, title: string, createdAt?: Date): Promise<ResolvedSeedPath>;
|
|
33
|
+
export declare function readSeedShelf(projectRoot: string): Promise<SeedShelfEntry[]>;
|
|
34
|
+
export declare function seedMatchesPrompt(seed: SeedShelfEntry, prompt: string): boolean;
|
|
35
|
+
export declare function findMatchingSeeds(projectRoot: string, prompt: string, maxMatches?: number): Promise<SeedShelfEntry[]>;
|
|
36
|
+
export declare function renderSeedTemplate(input: SeedTemplateInput): string;
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { parse } from "yaml";
|
|
4
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
5
|
+
const SEED_FILE_NAME_PATTERN = /^SEED-(\d{4}-\d{2}-\d{2})-([a-z0-9]+(?:-[a-z0-9]+)*)(?:-(\d+))?\.md$/u;
|
|
6
|
+
const DEFAULT_MAX_MATCHES = 3;
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function normalizeTriggerList(value) {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value
|
|
13
|
+
.map((item) => typeof item === "string" || typeof item === "number" ? String(item).trim() : "")
|
|
14
|
+
.filter((item) => item.length > 0);
|
|
15
|
+
}
|
|
16
|
+
if (typeof value === "string") {
|
|
17
|
+
return value
|
|
18
|
+
.split(/,\s*/u)
|
|
19
|
+
.map((item) => item.trim())
|
|
20
|
+
.filter((item) => item.length > 0);
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
}
|
|
24
|
+
function parseSeedFrontmatter(raw) {
|
|
25
|
+
if (!raw.startsWith("---")) {
|
|
26
|
+
return { values: {}, body: raw };
|
|
27
|
+
}
|
|
28
|
+
const lines = raw.split(/\r?\n/u);
|
|
29
|
+
let closingIndex = -1;
|
|
30
|
+
for (let index = 1; index < lines.length; index += 1) {
|
|
31
|
+
if (lines[index]?.trim() === "---") {
|
|
32
|
+
closingIndex = index;
|
|
33
|
+
break;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
if (closingIndex < 0) {
|
|
37
|
+
return { values: {}, body: raw };
|
|
38
|
+
}
|
|
39
|
+
const frontmatterRaw = lines.slice(1, closingIndex).join("\n");
|
|
40
|
+
const body = lines.slice(closingIndex + 1).join("\n");
|
|
41
|
+
try {
|
|
42
|
+
const parsed = parse(frontmatterRaw);
|
|
43
|
+
if (isRecord(parsed)) {
|
|
44
|
+
return { values: parsed, body };
|
|
45
|
+
}
|
|
46
|
+
return { values: {}, body };
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
return { values: {}, body };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function firstHeading(body) {
|
|
53
|
+
const match = /^#\s+(.+)$/mu.exec(body);
|
|
54
|
+
if (!match)
|
|
55
|
+
return null;
|
|
56
|
+
const title = match[1]?.trim() ?? "";
|
|
57
|
+
return title.length > 0 ? title : null;
|
|
58
|
+
}
|
|
59
|
+
function firstNonEmptyParagraph(body) {
|
|
60
|
+
const lines = body.split(/\r?\n/u);
|
|
61
|
+
for (const line of lines) {
|
|
62
|
+
const trimmed = line.trim();
|
|
63
|
+
if (trimmed.length === 0)
|
|
64
|
+
continue;
|
|
65
|
+
if (/^#\s+/u.test(trimmed))
|
|
66
|
+
continue;
|
|
67
|
+
if (/^[-*]\s+/u.test(trimmed))
|
|
68
|
+
continue;
|
|
69
|
+
return trimmed;
|
|
70
|
+
}
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
function fromFileNameFallbackTitle(fileName) {
|
|
74
|
+
const stem = fileName.replace(/\.md$/u, "");
|
|
75
|
+
const withoutPrefix = stem.replace(/^SEED-\d{4}-\d{2}-\d{2}-/u, "");
|
|
76
|
+
return withoutPrefix
|
|
77
|
+
.split("-")
|
|
78
|
+
.filter((part) => part.length > 0 && !/^\d+$/u.test(part))
|
|
79
|
+
.map((part) => part[0]?.toUpperCase() + part.slice(1))
|
|
80
|
+
.join(" ")
|
|
81
|
+
.trim();
|
|
82
|
+
}
|
|
83
|
+
export function seedShelfDir(projectRoot) {
|
|
84
|
+
return path.join(projectRoot, RUNTIME_ROOT, "seeds");
|
|
85
|
+
}
|
|
86
|
+
export function seedSlug(title) {
|
|
87
|
+
const normalized = title
|
|
88
|
+
.toLowerCase()
|
|
89
|
+
.trim()
|
|
90
|
+
.replace(/[`"'“”‘’()[\]{}<>]/gu, " ")
|
|
91
|
+
.replace(/[^a-z0-9]+/gu, "-")
|
|
92
|
+
.replace(/^-+/u, "")
|
|
93
|
+
.replace(/-+$/u, "");
|
|
94
|
+
if (normalized.length === 0) {
|
|
95
|
+
return "seed";
|
|
96
|
+
}
|
|
97
|
+
return normalized.slice(0, 48);
|
|
98
|
+
}
|
|
99
|
+
function isoDate(value) {
|
|
100
|
+
return value.toISOString().slice(0, 10);
|
|
101
|
+
}
|
|
102
|
+
export function seedFileName(title, createdAt = new Date()) {
|
|
103
|
+
return `SEED-${isoDate(createdAt)}-${seedSlug(title)}.md`;
|
|
104
|
+
}
|
|
105
|
+
async function pathExists(absPath) {
|
|
106
|
+
try {
|
|
107
|
+
await fs.stat(absPath);
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
export async function resolveSeedPathForWrite(projectRoot, title, createdAt = new Date()) {
|
|
115
|
+
const seedsDir = seedShelfDir(projectRoot);
|
|
116
|
+
await fs.mkdir(seedsDir, { recursive: true });
|
|
117
|
+
const baseFile = seedFileName(title, createdAt);
|
|
118
|
+
const baseStem = baseFile.replace(/\.md$/u, "");
|
|
119
|
+
let candidate = baseFile;
|
|
120
|
+
let index = 2;
|
|
121
|
+
while (await pathExists(path.join(seedsDir, candidate))) {
|
|
122
|
+
candidate = `${baseStem}-${index}.md`;
|
|
123
|
+
index += 1;
|
|
124
|
+
}
|
|
125
|
+
return {
|
|
126
|
+
fileName: candidate,
|
|
127
|
+
absPath: path.join(seedsDir, candidate),
|
|
128
|
+
relPath: path.join(RUNTIME_ROOT, "seeds", candidate)
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
export async function readSeedShelf(projectRoot) {
|
|
132
|
+
const dir = seedShelfDir(projectRoot);
|
|
133
|
+
let names = [];
|
|
134
|
+
try {
|
|
135
|
+
names = await fs.readdir(dir);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
const entries = [];
|
|
141
|
+
for (const fileName of names) {
|
|
142
|
+
if (!SEED_FILE_NAME_PATTERN.test(fileName))
|
|
143
|
+
continue;
|
|
144
|
+
const absPath = path.join(dir, fileName);
|
|
145
|
+
let raw = "";
|
|
146
|
+
try {
|
|
147
|
+
raw = await fs.readFile(absPath, "utf8");
|
|
148
|
+
}
|
|
149
|
+
catch {
|
|
150
|
+
continue;
|
|
151
|
+
}
|
|
152
|
+
const frontmatter = parseSeedFrontmatter(raw);
|
|
153
|
+
const title = (typeof frontmatter.values.title === "string" && frontmatter.values.title.trim().length > 0
|
|
154
|
+
? frontmatter.values.title.trim()
|
|
155
|
+
: null) ??
|
|
156
|
+
firstHeading(frontmatter.body) ??
|
|
157
|
+
fromFileNameFallbackTitle(fileName) ??
|
|
158
|
+
"Untitled seed";
|
|
159
|
+
const triggerWhen = normalizeTriggerList(frontmatter.values.trigger_when ?? frontmatter.values.triggerWhen);
|
|
160
|
+
const sourceStage = typeof frontmatter.values.source_stage === "string"
|
|
161
|
+
? frontmatter.values.source_stage
|
|
162
|
+
: typeof frontmatter.values.sourceStage === "string"
|
|
163
|
+
? frontmatter.values.sourceStage
|
|
164
|
+
: null;
|
|
165
|
+
const sourceArtifact = typeof frontmatter.values.source_artifact === "string"
|
|
166
|
+
? frontmatter.values.source_artifact
|
|
167
|
+
: typeof frontmatter.values.sourceArtifact === "string"
|
|
168
|
+
? frontmatter.values.sourceArtifact
|
|
169
|
+
: null;
|
|
170
|
+
const hypothesis = typeof frontmatter.values.hypothesis === "string" ? frontmatter.values.hypothesis : null;
|
|
171
|
+
const action = typeof frontmatter.values.action === "string" ? frontmatter.values.action : null;
|
|
172
|
+
const createdOn = SEED_FILE_NAME_PATTERN.exec(fileName)?.[1] ?? "1970-01-01";
|
|
173
|
+
const summary = firstNonEmptyParagraph(frontmatter.body);
|
|
174
|
+
entries.push({
|
|
175
|
+
fileName,
|
|
176
|
+
absPath,
|
|
177
|
+
relPath: path.join(RUNTIME_ROOT, "seeds", fileName),
|
|
178
|
+
createdOn,
|
|
179
|
+
title,
|
|
180
|
+
triggerWhen,
|
|
181
|
+
sourceStage,
|
|
182
|
+
sourceArtifact,
|
|
183
|
+
hypothesis,
|
|
184
|
+
action,
|
|
185
|
+
summary,
|
|
186
|
+
raw
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
entries.sort((a, b) => b.fileName.localeCompare(a.fileName));
|
|
190
|
+
return entries;
|
|
191
|
+
}
|
|
192
|
+
export function seedMatchesPrompt(seed, prompt) {
|
|
193
|
+
const normalizedPrompt = prompt.toLowerCase().trim();
|
|
194
|
+
if (normalizedPrompt.length === 0)
|
|
195
|
+
return false;
|
|
196
|
+
if (seed.triggerWhen.length === 0)
|
|
197
|
+
return false;
|
|
198
|
+
return seed.triggerWhen.some((trigger) => normalizedPrompt.includes(trigger.toLowerCase()));
|
|
199
|
+
}
|
|
200
|
+
export async function findMatchingSeeds(projectRoot, prompt, maxMatches = DEFAULT_MAX_MATCHES) {
|
|
201
|
+
const seeds = await readSeedShelf(projectRoot);
|
|
202
|
+
const matches = seeds.filter((seed) => seedMatchesPrompt(seed, prompt));
|
|
203
|
+
return matches.slice(0, Math.max(1, maxMatches));
|
|
204
|
+
}
|
|
205
|
+
export function renderSeedTemplate(input) {
|
|
206
|
+
const triggerWhen = [...input.triggerWhen].map((item) => item.trim()).filter((item) => item.length > 0);
|
|
207
|
+
const createdAt = input.createdAt ?? new Date();
|
|
208
|
+
const sourceStage = input.sourceStage?.trim() || "unknown";
|
|
209
|
+
const sourceArtifact = input.sourceArtifact?.trim() || "unknown";
|
|
210
|
+
return `---
|
|
211
|
+
title: ${input.title.trim()}
|
|
212
|
+
created_at: ${createdAt.toISOString()}
|
|
213
|
+
source_stage: ${sourceStage}
|
|
214
|
+
source_artifact: ${sourceArtifact}
|
|
215
|
+
trigger_when:
|
|
216
|
+
${triggerWhen.length > 0 ? triggerWhen.map((trigger) => ` - ${trigger}`).join("\n") : " - <trigger token>"}
|
|
217
|
+
hypothesis: ${input.hypothesis.trim()}
|
|
218
|
+
action: ${input.action.trim()}
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
# ${input.title.trim()}
|
|
222
|
+
|
|
223
|
+
## Why capture this seed
|
|
224
|
+
${input.hypothesis.trim()}
|
|
225
|
+
|
|
226
|
+
## Trigger when
|
|
227
|
+
${triggerWhen.length > 0 ? triggerWhen.map((trigger) => `- ${trigger}`).join("\n") : "- <trigger token>"}
|
|
228
|
+
|
|
229
|
+
## Suggested action
|
|
230
|
+
${input.action.trim()}
|
|
231
|
+
|
|
232
|
+
## Notes
|
|
233
|
+
- Expected payoff:
|
|
234
|
+
- Risks:
|
|
235
|
+
`;
|
|
236
|
+
}
|
package/dist/content/skills.js
CHANGED
|
@@ -1,22 +1,20 @@
|
|
|
1
1
|
import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "../constants.js";
|
|
2
|
-
import { STAGE_EXAMPLES_REFERENCE_DIR,
|
|
2
|
+
import { STAGE_EXAMPLES_REFERENCE_DIR, stageExamples } from "./examples.js";
|
|
3
3
|
import { STAGE_COMMON_GUIDANCE_REL_PATH } from "./stage-common-guidance.js";
|
|
4
4
|
import { stageAutoSubagentDispatch, stageSchema } from "./stage-schema.js";
|
|
5
5
|
const VERIFICATION_STAGES = ["tdd", "review", "ship"];
|
|
6
6
|
const DECISION_PROTOCOL_PATH = `${RUNTIME_ROOT}/references/protocols/decision.md`;
|
|
7
7
|
const COMPLETION_PROTOCOL_PATH = `${RUNTIME_ROOT}/references/protocols/completion.md`;
|
|
8
|
-
function whenNotToUseBlock(
|
|
9
|
-
|
|
10
|
-
if (schema.whenNotToUse.length === 0) {
|
|
8
|
+
function whenNotToUseBlock(items) {
|
|
9
|
+
if (items.length === 0) {
|
|
11
10
|
return "";
|
|
12
11
|
}
|
|
13
12
|
return `## When Not to Use
|
|
14
|
-
${
|
|
13
|
+
${items.map((item) => `- ${item}`).join("\n")}
|
|
15
14
|
|
|
16
15
|
`;
|
|
17
16
|
}
|
|
18
|
-
function contextLoadingBlock(
|
|
19
|
-
const trace = stageSchema(stage, track).crossStageTrace;
|
|
17
|
+
function contextLoadingBlock(trace) {
|
|
20
18
|
const readLines = trace.readsFrom.length > 0
|
|
21
19
|
? trace.readsFrom.map((value) => `- \`${value}\``).join("\n")
|
|
22
20
|
: "- (first stage — no upstream artifacts)";
|
|
@@ -54,8 +52,7 @@ Mandatory delegations for this stage: ${mandatoryList}.
|
|
|
54
52
|
Record completion/waiver in \`${delegationLogRel}\` before stage completion.
|
|
55
53
|
`;
|
|
56
54
|
}
|
|
57
|
-
function researchPlaybooksBlock(
|
|
58
|
-
const playbooks = stageSchema(stage, track).researchPlaybooks ?? [];
|
|
55
|
+
function researchPlaybooksBlock(playbooks) {
|
|
59
56
|
if (playbooks.length === 0)
|
|
60
57
|
return "";
|
|
61
58
|
const rows = playbooks
|
|
@@ -70,11 +67,10 @@ and record outcomes in the stage artifact when relevant.
|
|
|
70
67
|
${rows}
|
|
71
68
|
`;
|
|
72
69
|
}
|
|
73
|
-
function reviewSectionsBlock(
|
|
74
|
-
|
|
75
|
-
if (schema.reviewSections.length === 0)
|
|
70
|
+
function reviewSectionsBlock(sectionsInput) {
|
|
71
|
+
if (sectionsInput.length === 0)
|
|
76
72
|
return "";
|
|
77
|
-
const sections =
|
|
73
|
+
const sections = sectionsInput
|
|
78
74
|
.map((sec) => {
|
|
79
75
|
const points = sec.evaluationPoints.map((p) => `- ${p}`).join("\n");
|
|
80
76
|
const title = sec.stopGate ? `${sec.title} (STOP gate)` : sec.title;
|
|
@@ -86,6 +82,18 @@ function reviewSectionsBlock(stage, track) {
|
|
|
86
82
|
${sections}
|
|
87
83
|
`;
|
|
88
84
|
}
|
|
85
|
+
function reviewLoopBlock(reviewLoop) {
|
|
86
|
+
if (!reviewLoop)
|
|
87
|
+
return "";
|
|
88
|
+
const checklist = reviewLoop.checklist.map((item) => `- \`${item}\``).join("\n");
|
|
89
|
+
return `## Outside Voice Review Loop
|
|
90
|
+
- Stage: \`${reviewLoop.stage}\`
|
|
91
|
+
- Target score: \`${reviewLoop.targetScore}\`
|
|
92
|
+
- Max iterations: \`${reviewLoop.maxIterations}\`
|
|
93
|
+
- Checklist dimensions:
|
|
94
|
+
${checklist}
|
|
95
|
+
`;
|
|
96
|
+
}
|
|
89
97
|
function verificationBlock(stage) {
|
|
90
98
|
if (!VERIFICATION_STAGES.includes(stage))
|
|
91
99
|
return "";
|
|
@@ -116,8 +124,7 @@ Detailed walkthrough:
|
|
|
116
124
|
\`.cclaw/${STAGE_EXAMPLES_REFERENCE_DIR}/tdd-batch-walkthrough.md\`
|
|
117
125
|
`;
|
|
118
126
|
}
|
|
119
|
-
function crossStageTraceBlock(
|
|
120
|
-
const trace = stageSchema(stage, track).crossStageTrace;
|
|
127
|
+
function crossStageTraceBlock(trace) {
|
|
121
128
|
const reads = trace.readsFrom.length > 0
|
|
122
129
|
? trace.readsFrom.map((r) => `- ${r}`).join("\n")
|
|
123
130
|
: "- (first stage — no upstream artifacts)";
|
|
@@ -135,8 +142,7 @@ ${writes}
|
|
|
135
142
|
Rule: ${trace.traceabilityRule}
|
|
136
143
|
`;
|
|
137
144
|
}
|
|
138
|
-
function artifactValidationBlock(
|
|
139
|
-
const validations = stageSchema(stage, track).artifactValidation;
|
|
145
|
+
function artifactValidationBlock(validations) {
|
|
140
146
|
if (validations.length === 0)
|
|
141
147
|
return "";
|
|
142
148
|
const rows = validations
|
|
@@ -152,10 +158,10 @@ function artifactValidationBlock(stage, track) {
|
|
|
152
158
|
${rows}
|
|
153
159
|
`;
|
|
154
160
|
}
|
|
155
|
-
function mergedAntiPatterns(
|
|
161
|
+
function mergedAntiPatterns(philosophy, execution) {
|
|
156
162
|
const merged = [];
|
|
157
163
|
const seen = new Set();
|
|
158
|
-
for (const item of [...
|
|
164
|
+
for (const item of [...philosophy.commonRationalizations, ...execution.blockers]) {
|
|
159
165
|
const key = item.trim().toLowerCase();
|
|
160
166
|
if (seen.has(key))
|
|
161
167
|
continue;
|
|
@@ -205,9 +211,9 @@ function stageSpecificSeeAlso(stage) {
|
|
|
205
211
|
return refs[stage];
|
|
206
212
|
}
|
|
207
213
|
function completionParametersBlock(schema, track) {
|
|
208
|
-
const gateList = schema.requiredGates.map((g) => `\`${g.id}\``).join(", ");
|
|
209
|
-
const mandatory = schema.mandatoryDelegations.length > 0
|
|
210
|
-
? schema.mandatoryDelegations.map((a) => `\`${a}\``).join(", ")
|
|
214
|
+
const gateList = schema.executionModel.requiredGates.map((g) => `\`${g.id}\``).join(", ");
|
|
215
|
+
const mandatory = schema.reviewLens.mandatoryDelegations.length > 0
|
|
216
|
+
? schema.reviewLens.mandatoryDelegations.map((a) => `\`${a}\``).join(", ")
|
|
211
217
|
: "none";
|
|
212
218
|
const nextStage = schema.next === "done" ? "done" : schema.next;
|
|
213
219
|
const nextDescription = schema.next === "done"
|
|
@@ -218,25 +224,24 @@ function completionParametersBlock(schema, track) {
|
|
|
218
224
|
- \`stage\`: \`${schema.stage}\`
|
|
219
225
|
- \`next\`: \`${nextStage}\` (${nextDescription})
|
|
220
226
|
- \`gates\`: ${gateList}
|
|
221
|
-
- \`artifact\`: \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`
|
|
227
|
+
- \`artifact\`: \`${RUNTIME_ROOT}/artifacts/${schema.artifactRules.artifactFile}\`
|
|
222
228
|
- \`mandatory delegations\`: ${mandatory}
|
|
223
229
|
- \`completion helper\`: \`node .cclaw/hooks/stage-complete.mjs ${schema.stage}\`
|
|
224
230
|
- Fill \`## Learnings\` before closeout: either \`- None this stage.\` or JSON bullets with required keys \`type\`, \`trigger\`, \`action\`, \`confidence\` (knowledge-schema compatible).
|
|
225
231
|
- Record mandatory delegation completion/waiver in \`${RUNTIME_ROOT}/state/delegation-log.json\` with rationale as needed.
|
|
226
232
|
- Use the completion helper instead of raw \`flow-state.json\` edits (legacy direct edits trigger workflow-guard warnings or strict-mode blocks).
|
|
227
|
-
|
|
228
|
-
\`${COMPLETION_PROTOCOL_PATH}\`
|
|
233
|
+
- Completion protocol reference: \`${COMPLETION_PROTOCOL_PATH}\`
|
|
229
234
|
`;
|
|
230
235
|
}
|
|
231
236
|
function quickStartBlock(stage, track) {
|
|
232
237
|
const schema = stageSchema(stage, track);
|
|
233
|
-
const gatePreview = schema.requiredGates.slice(0, 3).map((g) => `\`${g.id}\``).join(", ");
|
|
238
|
+
const gatePreview = schema.executionModel.requiredGates.slice(0, 3).map((g) => `\`${g.id}\``).join(", ");
|
|
234
239
|
return `## Quick Start
|
|
235
240
|
|
|
236
|
-
1. Announce at start: "Using \`${schema.skillName}\` to ${schema.purpose}".
|
|
241
|
+
1. Announce at start: "Using \`${schema.skillName}\` to ${schema.philosophy.purpose}".
|
|
237
242
|
2. Obey HARD-GATE and Iron Law.
|
|
238
|
-
3. Execute checklist in order and persist \`${RUNTIME_ROOT}/artifacts/${schema.artifactFile}\`.
|
|
239
|
-
4. Satisfy gates (${gatePreview}${schema.requiredGates.length > 3 ? ` +${schema.requiredGates.length - 3}` : ""}).
|
|
243
|
+
3. Execute checklist in order and persist \`${RUNTIME_ROOT}/artifacts/${schema.artifactRules.artifactFile}\`.
|
|
244
|
+
4. Satisfy gates (${gatePreview}${schema.executionModel.requiredGates.length > 3 ? ` +${schema.executionModel.requiredGates.length - 3}` : ""}).
|
|
240
245
|
`;
|
|
241
246
|
}
|
|
242
247
|
/**
|
|
@@ -325,21 +330,27 @@ function dedupeGuidance(items, blockedBy) {
|
|
|
325
330
|
}
|
|
326
331
|
export function stageSkillMarkdown(stage, track = "standard") {
|
|
327
332
|
const schema = stageSchema(stage, track);
|
|
328
|
-
const
|
|
333
|
+
const philosophy = schema.philosophy;
|
|
334
|
+
const executionModel = schema.executionModel;
|
|
335
|
+
const artifactRules = schema.artifactRules;
|
|
336
|
+
const reviewLens = schema.reviewLens;
|
|
337
|
+
const mandatoryDelegations = reviewLens.mandatoryDelegations;
|
|
338
|
+
const gateList = executionModel.requiredGates
|
|
329
339
|
.map((g) => `- \`${g.id}\` — ${g.description}`)
|
|
330
340
|
.join("\n");
|
|
331
|
-
const evidenceList =
|
|
341
|
+
const evidenceList = executionModel.requiredEvidence
|
|
332
342
|
.map((e) => `- [ ] ${e}`)
|
|
333
343
|
.join("\n");
|
|
334
|
-
const checklistItems =
|
|
344
|
+
const checklistItems = executionModel.checklist
|
|
335
345
|
.map((item, i) => `${i + 1}. ${item}`)
|
|
336
346
|
.join("\n");
|
|
337
|
-
const interactionFocus = dedupeGuidance(
|
|
338
|
-
const processSummary = dedupeGuidance(
|
|
339
|
-
const processNote = schema.process.length > processSummary.length
|
|
340
|
-
? `- Follow the Checklist above for remaining execution detail (+${schema.process.length - processSummary.length} condensed step${schema.process.length - processSummary.length === 1 ? "" : "s"}).`
|
|
341
|
-
: "";
|
|
347
|
+
const interactionFocus = dedupeGuidance(executionModel.interactionProtocol, [...executionModel.checklist, ...executionModel.process]).slice(0, 5);
|
|
348
|
+
const processSummary = dedupeGuidance(executionModel.process, executionModel.checklist).slice(0, 5);
|
|
342
349
|
const stageRefs = stageSpecificSeeAlso(stage);
|
|
350
|
+
const reviewLoopSection = reviewLoopBlock(reviewLens.reviewLoop);
|
|
351
|
+
const mandatoryDelegationSummary = mandatoryDelegations.length > 0
|
|
352
|
+
? mandatoryDelegations.map((name) => `\`${name}\``).join(", ")
|
|
353
|
+
: "none";
|
|
343
354
|
return `---
|
|
344
355
|
name: ${schema.skillName}
|
|
345
356
|
description: "${schema.skillDescription}"
|
|
@@ -349,7 +360,7 @@ description: "${schema.skillDescription}"
|
|
|
349
360
|
|
|
350
361
|
<EXTREMELY-IMPORTANT>
|
|
351
362
|
|
|
352
|
-
**IRON LAW — ${stage.toUpperCase()}:** ${
|
|
363
|
+
**IRON LAW — ${stage.toUpperCase()}:** ${philosophy.ironLaw}
|
|
353
364
|
|
|
354
365
|
If you are about to violate the Iron Law, STOP. No amount of urgency, partial progress, or clever reinterpretation overrides it. Escalate via the Decision Protocol or abandon the stage.
|
|
355
366
|
|
|
@@ -357,28 +368,35 @@ If you are about to violate the Iron Law, STOP. No amount of urgency, partial pr
|
|
|
357
368
|
|
|
358
369
|
${quickStartBlock(stage, track)}
|
|
359
370
|
|
|
360
|
-
##
|
|
361
|
-
${
|
|
371
|
+
## Philosophy
|
|
372
|
+
${philosophy.purpose}
|
|
373
|
+
|
|
374
|
+
## Complexity Tier
|
|
375
|
+
- Active tier: \`${schema.complexityTier}\`
|
|
376
|
+
- Mandatory delegations at this tier: ${mandatoryDelegationSummary}
|
|
362
377
|
|
|
363
378
|
## When to Use
|
|
364
|
-
${
|
|
379
|
+
${philosophy.whenToUse.map((item) => `- ${item}`).join("\n")}
|
|
380
|
+
|
|
381
|
+
${whenNotToUseBlock(philosophy.whenNotToUse)}
|
|
382
|
+
## HARD-GATE
|
|
383
|
+
${philosophy.hardGate}
|
|
384
|
+
|
|
385
|
+
## Anti-Patterns & Red Flags
|
|
386
|
+
${mergedAntiPatterns(philosophy, executionModel)}
|
|
387
|
+
|
|
388
|
+
## Process
|
|
389
|
+
${processSummary.length > 0 ? processSummary.map((item, i) => `${i + 1}. ${item}`).join("\n") : "1. Execute the Checklist in order.\n2. Satisfy every required gate.\n3. Complete verification before stage closeout."}
|
|
365
390
|
|
|
366
|
-
${whenNotToUseBlock(stage, track)}
|
|
367
391
|
## Inputs
|
|
368
|
-
${
|
|
392
|
+
${executionModel.inputs.length > 0 ? executionModel.inputs.map((item) => `- ${item}`).join("\n") : "- (first stage — no required inputs)"}
|
|
369
393
|
|
|
370
394
|
## Required Context
|
|
371
|
-
${
|
|
395
|
+
${executionModel.requiredContext.length > 0 ? executionModel.requiredContext.map((item) => `- ${item}`).join("\n") : "- None beyond this skill"}
|
|
372
396
|
|
|
373
|
-
${contextLoadingBlock(
|
|
397
|
+
${contextLoadingBlock(artifactRules.crossStageTrace)}
|
|
374
398
|
${autoSubagentDispatchBlock(stage, track)}
|
|
375
|
-
${researchPlaybooksBlock(
|
|
376
|
-
|
|
377
|
-
## Outputs
|
|
378
|
-
${schema.outputs.map((item) => `- ${item}`).join("\n")}
|
|
379
|
-
|
|
380
|
-
## HARD-GATE
|
|
381
|
-
${schema.hardGate}
|
|
399
|
+
${researchPlaybooksBlock(executionModel.researchPlaybooks ?? [])}
|
|
382
400
|
|
|
383
401
|
## Checklist
|
|
384
402
|
|
|
@@ -386,15 +404,12 @@ You MUST complete these steps in order:
|
|
|
386
404
|
|
|
387
405
|
${checklistItems}
|
|
388
406
|
|
|
389
|
-
${stageGoodBadExamples(stage)}
|
|
390
|
-
${stageDomainExamples(stage)}
|
|
391
407
|
${stageExamples(stage)}
|
|
392
408
|
|
|
393
409
|
## Interaction Protocol
|
|
394
410
|
${interactionFocus.length > 0 ? interactionFocus.map((item, i) => `${i + 1}. ${item}`).join("\n") : "- Keep communication concise and decision-focused; rely on the Checklist for execution order."}
|
|
395
411
|
|
|
396
|
-
|
|
397
|
-
\`${DECISION_PROTOCOL_PATH}\`
|
|
412
|
+
Decision protocol reference: \`${DECISION_PROTOCOL_PATH}\`
|
|
398
413
|
|
|
399
414
|
${batchExecutionModeBlock(stage, track)}
|
|
400
415
|
## Required Gates
|
|
@@ -403,22 +418,24 @@ ${gateList}
|
|
|
403
418
|
## Required Evidence
|
|
404
419
|
${evidenceList}
|
|
405
420
|
|
|
406
|
-
## Process
|
|
407
|
-
${processSummary.length > 0 ? processSummary.map((item, i) => `${i + 1}. ${item}`).join("\n") : "1. Execute the Checklist in order.\n2. Satisfy every required gate.\n3. Complete verification before stage closeout."}
|
|
408
|
-
${processNote.length > 0 ? `\n${processNote}` : ""}
|
|
409
|
-
|
|
410
|
-
${reviewSectionsBlock(stage, track)}
|
|
411
421
|
${verificationBlock(stage)}
|
|
412
|
-
${crossStageTraceBlock(stage, track)}
|
|
413
|
-
${artifactValidationBlock(stage, track)}
|
|
414
|
-
|
|
415
|
-
## Anti-Patterns & Red Flags
|
|
416
|
-
${mergedAntiPatterns(schema)}
|
|
417
422
|
|
|
418
423
|
## Verification
|
|
419
|
-
${
|
|
424
|
+
${executionModel.exitCriteria.map((item) => `- [ ] ${item}`).join("\n")}
|
|
420
425
|
|
|
421
426
|
${completionParametersBlock(schema, track)}
|
|
427
|
+
## Artifact Rules
|
|
428
|
+
- Artifact target: \`${RUNTIME_ROOT}/artifacts/${artifactRules.artifactFile}\`
|
|
429
|
+
|
|
430
|
+
${crossStageTraceBlock(artifactRules.crossStageTrace)}
|
|
431
|
+
${artifactValidationBlock(artifactRules.artifactValidation)}
|
|
432
|
+
|
|
433
|
+
## Review Lens
|
|
434
|
+
${reviewLoopSection ? `${reviewLoopSection}\n` : ""}## Outputs
|
|
435
|
+
${reviewLens.outputs.map((item) => `- ${item}`).join("\n")}
|
|
436
|
+
|
|
437
|
+
${reviewSectionsBlock(reviewLens.reviewSections)}
|
|
438
|
+
|
|
422
439
|
## Shared Stage Guidance
|
|
423
440
|
See:
|
|
424
441
|
- \`${STAGE_COMMON_GUIDANCE_REL_PATH}\`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { FlowStage, FlowTrack, TransitionRule } from "../types.js";
|
|
2
2
|
import type { StageComplexityTier, StageAutoSubagentDispatch, StageSchema } from "./stages/schema-types.js";
|
|
3
|
-
export type { ArtifactValidation, CrossStageTrace, ReviewSection, StageComplexityTier, StageExecutionModel, StagePhilosophy, StageArtifactRules, StageReviewLens, StageAutoSubagentDispatch, StageGate, StageSchemaLegacyInput, StageSchema, StageSchemaInput, StageSchemaV2Input } from "./stages/schema-types.js";
|
|
3
|
+
export type { ArtifactValidation, CrossStageTrace, ReviewSection, StageComplexityTier, StageExecutionModel, StagePhilosophy, StageArtifactRules, StageReviewLoop, StageReviewLens, StageAutoSubagentDispatch, StageGate, StageSchemaLegacyInput, StageSchema, StageSchemaInput, StageSchemaV2Input } from "./stages/schema-types.js";
|
|
4
4
|
export declare const SKILL_ENVELOPE_KINDS: readonly ["stage-output", "gate-result", "delegation-record"];
|
|
5
5
|
export type SkillEnvelopeKind = (typeof SKILL_ENVELOPE_KINDS)[number];
|
|
6
6
|
export interface SkillEnvelope {
|
|
@@ -145,13 +145,23 @@ const REQUIRED_GATE_IDS = {
|
|
|
145
145
|
]
|
|
146
146
|
};
|
|
147
147
|
const REQUIRED_ARTIFACT_SECTIONS = {
|
|
148
|
-
brainstorm: [
|
|
148
|
+
brainstorm: [
|
|
149
|
+
"Context",
|
|
150
|
+
"Problem",
|
|
151
|
+
"Approach Tier",
|
|
152
|
+
"Approaches",
|
|
153
|
+
"Approach Reaction",
|
|
154
|
+
"Selected Direction"
|
|
155
|
+
],
|
|
149
156
|
scope: ["Scope Mode", "In Scope / Out of Scope", "Completion Dashboard", "Scope Summary"],
|
|
150
157
|
design: [
|
|
151
158
|
"Research Fleet Synthesis",
|
|
152
159
|
"Architecture Boundaries",
|
|
153
160
|
"Architecture Diagram",
|
|
154
161
|
"Failure Mode Table",
|
|
162
|
+
"Security & Threat Model",
|
|
163
|
+
"Observability & Debuggability",
|
|
164
|
+
"Deployment & Rollout",
|
|
155
165
|
"Completion Dashboard"
|
|
156
166
|
],
|
|
157
167
|
spec: ["Acceptance Criteria", "Edge Cases", "Testability Map", "Approval"],
|
|
@@ -227,6 +237,7 @@ function normalizeStageSchemaInput(value) {
|
|
|
227
237
|
next: value.next,
|
|
228
238
|
checklist: value.executionModel.checklist,
|
|
229
239
|
reviewSections: value.reviewLens.reviewSections,
|
|
240
|
+
reviewLoop: value.reviewLens.reviewLoop,
|
|
230
241
|
completionStatus: value.artifactRules.completionStatus,
|
|
231
242
|
crossStageTrace: value.artifactRules.crossStageTrace,
|
|
232
243
|
artifactValidation: value.artifactRules.artifactValidation,
|
|
@@ -453,7 +464,8 @@ export function stageSchema(stage, track = "standard") {
|
|
|
453
464
|
const reviewLens = {
|
|
454
465
|
outputs: base.outputs,
|
|
455
466
|
reviewSections: base.reviewSections,
|
|
456
|
-
mandatoryDelegations
|
|
467
|
+
mandatoryDelegations,
|
|
468
|
+
reviewLoop: base.reviewLoop
|
|
457
469
|
};
|
|
458
470
|
return {
|
|
459
471
|
...base,
|
|
@@ -44,6 +44,7 @@ export const BRAINSTORM = {
|
|
|
44
44
|
"**Propose 2-3 architecturally distinct approaches** — with real trade-offs and no recommendation yet. At least one option must be a higher-upside challenger that raises ambition vs the user's initial ask.",
|
|
45
45
|
"**Collect user reaction** — ask which approach feels closest and what concerns remain before stating your recommendation.",
|
|
46
46
|
"**Recommend only after reaction** — present final recommendation with rationale that explicitly references user feedback.",
|
|
47
|
+
"**Plant-seed shelf (optional)** — when a non-selected approach is still promising, capture it as `.cclaw/seeds/SEED-<YYYY-MM-DD>-<slug>.md` with `trigger_when`, hypothesis, and suggested action instead of losing it.",
|
|
47
48
|
"**Present design by sections** — scale each section to its complexity. Ask after each section whether it looks right so far. Cover: architecture, key components, data flow.",
|
|
48
49
|
"**Optional visual companion** — when architecture/data flow complexity is medium+ offer a compact diagram (ASCII or Mermaid) before artifact write-up.",
|
|
49
50
|
"**Write artifact** to `.cclaw/artifacts/01-brainstorm-<slug>.md`.",
|
|
@@ -74,6 +75,7 @@ export const BRAINSTORM = {
|
|
|
74
75
|
"Propose 2-3 architecturally distinct approaches with trade-offs (one must be higher-upside challenger).",
|
|
75
76
|
"Collect user reaction before giving your recommendation.",
|
|
76
77
|
"Recommend after reaction and explain how feedback changed the recommendation.",
|
|
78
|
+
"Optionally plant promising non-selected approaches into `.cclaw/seeds/SEED-<YYYY-MM-DD>-<slug>.md` with trigger_when/action notes.",
|
|
77
79
|
"Present design sections incrementally, get approval after each.",
|
|
78
80
|
"Write approved direction to `.cclaw/artifacts/01-brainstorm-<slug>.md`.",
|
|
79
81
|
"Run document-quality pass to close contradictions and weak trade-off reasoning.",
|
|
@@ -93,6 +95,7 @@ export const BRAINSTORM = {
|
|
|
93
95
|
"2-3 approaches with trade-offs are recorded, including one higher-upside challenger option.",
|
|
94
96
|
"User reaction to approaches is captured before final recommendation.",
|
|
95
97
|
"Final recommendation explicitly reflects user reaction.",
|
|
98
|
+
"When a promising option is parked, a seed file is created under `.cclaw/seeds/` and referenced in the artifact.",
|
|
96
99
|
"Approved direction and approval marker are present.",
|
|
97
100
|
"Assumptions and open questions are captured (or explicitly marked as none)."
|
|
98
101
|
],
|
|
@@ -130,13 +133,21 @@ export const BRAINSTORM = {
|
|
|
130
133
|
{ section: "Context", required: true, validationRule: "Must reference project state and relevant existing code or patterns." },
|
|
131
134
|
{ section: "Problem", required: true, validationRule: "Must define what we're solving, success criteria, and constraints." },
|
|
132
135
|
{ section: "Clarifying Questions", required: false, validationRule: "Must capture question, answer, and decision impact for each clarifying question." },
|
|
133
|
-
{ section: "Approach Tier", required:
|
|
134
|
-
{ section: "
|
|
135
|
-
{ section: "
|
|
136
|
-
{ section: "
|
|
136
|
+
{ section: "Approach Tier", required: true, validationRule: "Must classify depth as Lightweight/Standard/Deep and explain why." },
|
|
137
|
+
{ section: "Short-Circuit Decision", required: false, validationRule: "Must include Status/Why/Scope handoff lines when short-circuit is discussed." },
|
|
138
|
+
{ section: "Approaches", required: true, validationRule: "Must compare 2-3 architecturally distinct options with real trade-offs and include one row labeled `challenger: higher-upside`." },
|
|
139
|
+
{ section: "Approach Reaction", required: true, validationRule: "Must summarize user reaction before recommendation, including concerns that changed direction." },
|
|
140
|
+
{ section: "Selected Direction", required: true, validationRule: "Must include the selected approach, rationale tied to user reaction/feedback, and explicit approval marker." },
|
|
137
141
|
{ section: "Design", required: false, validationRule: "Must cover architecture, key components, and data flow scaled to complexity." },
|
|
138
142
|
{ section: "Visual Companion", required: false, validationRule: "If architecture/data-flow complexity is medium+, include compact ASCII/Mermaid diagram or explicitly justify omission." },
|
|
139
143
|
{ section: "Assumptions and Open Questions", required: false, validationRule: "Must capture unresolved assumptions/open questions, or explicitly state none." }
|
|
144
|
+
],
|
|
145
|
+
trivialOverrideSections: [
|
|
146
|
+
"Context",
|
|
147
|
+
"Problem",
|
|
148
|
+
"Approach Tier",
|
|
149
|
+
"Short-Circuit Decision",
|
|
150
|
+
"Selected Direction"
|
|
140
151
|
]
|
|
141
152
|
},
|
|
142
153
|
reviewLens: {
|