cclaw-cli 0.15.1 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/artifact-linter.js +154 -0
- package/dist/cli.js +2 -1
- package/dist/constants.d.ts +2 -2
- package/dist/constants.js +4 -3
- package/dist/content/compound-command.d.ts +2 -0
- package/dist/content/compound-command.js +72 -0
- package/dist/content/contracts.js +1 -1
- package/dist/content/doctor-references.js +7 -6
- package/dist/content/feature-command.js +54 -51
- package/dist/content/harnesses-doc.js +5 -3
- package/dist/content/hooks.js +2 -2
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +73 -0
- package/dist/content/learnings.d.ts +1 -1
- package/dist/content/learnings.js +22 -5
- package/dist/content/meta-skill.js +6 -3
- package/dist/content/next-command.js +5 -5
- package/dist/content/observe.js +3 -2
- package/dist/content/ops-command.js +4 -4
- package/dist/content/protocols.js +27 -38
- package/dist/content/retro-command.js +2 -1
- package/dist/content/rewind-command.d.ts +0 -1
- package/dist/content/rewind-command.js +19 -33
- package/dist/content/skills.js +14 -8
- package/dist/content/stage-schema.js +3 -38
- package/dist/content/stages/plan.js +16 -5
- package/dist/content/stages/review.js +20 -0
- package/dist/content/stages/scope.js +9 -3
- package/dist/content/stages/ship.js +1 -0
- package/dist/content/stages/tdd.js +5 -4
- package/dist/content/templates.js +105 -9
- package/dist/content/utility-skills.d.ts +3 -1
- package/dist/content/utility-skills.js +91 -1
- package/dist/delegation.d.ts +33 -3
- package/dist/delegation.js +56 -3
- package/dist/doctor.js +269 -88
- package/dist/feature-system.d.ts +22 -5
- package/dist/feature-system.js +267 -126
- package/dist/harness-adapters.js +17 -1
- package/dist/install.js +10 -8
- package/dist/policy.js +13 -4
- package/package.json +1 -1
package/dist/artifact-linter.js
CHANGED
|
@@ -188,6 +188,66 @@ function lineContainsVagueAdjective(text) {
|
|
|
188
188
|
}
|
|
189
189
|
return null;
|
|
190
190
|
}
|
|
191
|
+
const FRONTMATTER_REQUIRED_KEYS = [
|
|
192
|
+
"stage",
|
|
193
|
+
"schema_version",
|
|
194
|
+
"version",
|
|
195
|
+
"feature",
|
|
196
|
+
"locked_decisions",
|
|
197
|
+
"inputs_hash"
|
|
198
|
+
];
|
|
199
|
+
const PLACEHOLDER_PATTERNS = [
|
|
200
|
+
{ label: "TODO", regex: /\bTODO\b/iu },
|
|
201
|
+
{ label: "TBD", regex: /\bTBD\b/iu },
|
|
202
|
+
{ label: "FIXME", regex: /\bFIXME\b/iu },
|
|
203
|
+
{ label: "<fill-in>", regex: /<fill-in>/iu },
|
|
204
|
+
{ label: "<your-*-here>", regex: /<your-[^>]*-here>/iu },
|
|
205
|
+
{ label: "xxx", regex: /\bxxx\b/iu },
|
|
206
|
+
{ label: "ellipsis", regex: /\.{3}/u }
|
|
207
|
+
];
|
|
208
|
+
const SCOPE_REDUCTION_PATTERNS = [
|
|
209
|
+
{ label: "v1", regex: /\bv1\b/iu },
|
|
210
|
+
{ label: "for now", regex: /\bfor now\b/iu },
|
|
211
|
+
{ label: "later", regex: /\blater\b/iu },
|
|
212
|
+
{ label: "temporary", regex: /\btemporary\b/iu },
|
|
213
|
+
{ label: "placeholder", regex: /\bplaceholder\b/iu },
|
|
214
|
+
{ label: "mock for now", regex: /\bmock for now\b/iu },
|
|
215
|
+
{ label: "hardcoded for now", regex: /\bhardcoded for now\b/iu },
|
|
216
|
+
{ label: "will improve later", regex: /\bwill improve later\b/iu }
|
|
217
|
+
];
|
|
218
|
+
function parseFrontmatter(markdown) {
|
|
219
|
+
const lines = markdown.split(/\r?\n/);
|
|
220
|
+
if (lines[0]?.trim() !== "---") {
|
|
221
|
+
return { hasFrontmatter: false, values: {} };
|
|
222
|
+
}
|
|
223
|
+
const endIndex = lines.findIndex((line, index) => index > 0 && line.trim() === "---");
|
|
224
|
+
if (endIndex < 0) {
|
|
225
|
+
return { hasFrontmatter: false, values: {} };
|
|
226
|
+
}
|
|
227
|
+
const values = {};
|
|
228
|
+
for (const line of lines.slice(1, endIndex)) {
|
|
229
|
+
const match = /^([A-Za-z0-9_-]+)\s*:\s*(.*)$/u.exec(line.trim());
|
|
230
|
+
if (!match)
|
|
231
|
+
continue;
|
|
232
|
+
const key = match[1];
|
|
233
|
+
const value = match[2].trim();
|
|
234
|
+
values[key] = value;
|
|
235
|
+
}
|
|
236
|
+
return { hasFrontmatter: true, values };
|
|
237
|
+
}
|
|
238
|
+
function extractDecisionIds(text) {
|
|
239
|
+
const ids = text.match(/\bD-\d+\b/gu) ?? [];
|
|
240
|
+
return [...new Set(ids)];
|
|
241
|
+
}
|
|
242
|
+
function collectPatternHits(text, patterns) {
|
|
243
|
+
const hits = [];
|
|
244
|
+
for (const pattern of patterns) {
|
|
245
|
+
if (pattern.regex.test(text)) {
|
|
246
|
+
hits.push(pattern.label);
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return hits;
|
|
250
|
+
}
|
|
191
251
|
function validateSectionBody(sectionBody, rule, sectionName) {
|
|
192
252
|
const bodyLines = sectionBody.split(/\r?\n/).map((line) => line.trim());
|
|
193
253
|
const meaningful = meaningfulLineCount(sectionBody);
|
|
@@ -336,6 +396,37 @@ export async function lintArtifact(projectRoot, stage) {
|
|
|
336
396
|
}
|
|
337
397
|
const raw = await fs.readFile(absFile, "utf8");
|
|
338
398
|
const sections = extractH2Sections(raw);
|
|
399
|
+
const parsedFrontmatter = parseFrontmatter(raw);
|
|
400
|
+
const frontmatterMissingKeys = FRONTMATTER_REQUIRED_KEYS.filter((key) => {
|
|
401
|
+
const value = parsedFrontmatter.values[key];
|
|
402
|
+
return typeof value !== "string" || value.trim().length === 0;
|
|
403
|
+
});
|
|
404
|
+
const frontmatterStage = parsedFrontmatter.values.stage?.replace(/^['"]|['"]$/gu, "");
|
|
405
|
+
const frontmatterSchemaVersion = parsedFrontmatter.values.schema_version?.replace(/^['"]|['"]$/gu, "");
|
|
406
|
+
const frontmatterInputsHash = parsedFrontmatter.values.inputs_hash?.replace(/^['"]|['"]$/gu, "");
|
|
407
|
+
const frontmatterValid = parsedFrontmatter.hasFrontmatter &&
|
|
408
|
+
frontmatterMissingKeys.length === 0 &&
|
|
409
|
+
frontmatterStage === stage &&
|
|
410
|
+
frontmatterSchemaVersion === "1" &&
|
|
411
|
+
/^sha256:(?:pending|[a-f0-9]{64})$/iu.test(frontmatterInputsHash ?? "");
|
|
412
|
+
const requireFrontmatter = parsedFrontmatter.hasFrontmatter;
|
|
413
|
+
findings.push({
|
|
414
|
+
section: "Frontmatter",
|
|
415
|
+
required: requireFrontmatter,
|
|
416
|
+
rule: "Artifact must include frontmatter keys (stage, schema_version=1, version, feature, locked_decisions, inputs_hash=sha256:pending|sha256:<64hex>).",
|
|
417
|
+
found: parsedFrontmatter.hasFrontmatter ? frontmatterValid : true,
|
|
418
|
+
details: !parsedFrontmatter.hasFrontmatter
|
|
419
|
+
? "Legacy artifact without YAML frontmatter (allowed for backward compatibility)."
|
|
420
|
+
: frontmatterMissingKeys.length > 0
|
|
421
|
+
? `Frontmatter missing required key(s): ${frontmatterMissingKeys.join(", ")}.`
|
|
422
|
+
: frontmatterStage !== stage
|
|
423
|
+
? `Frontmatter stage must be "${stage}" (found "${frontmatterStage ?? "(missing)"}").`
|
|
424
|
+
: frontmatterSchemaVersion !== "1"
|
|
425
|
+
? `Frontmatter schema_version must be "1" (found "${frontmatterSchemaVersion ?? "(missing)"}").`
|
|
426
|
+
: !/^sha256:(?:pending|[a-f0-9]{64})$/iu.test(frontmatterInputsHash ?? "")
|
|
427
|
+
? "Frontmatter inputs_hash must be sha256:pending or sha256:<64 hex chars>."
|
|
428
|
+
: "Frontmatter integrity checks passed."
|
|
429
|
+
});
|
|
339
430
|
const isTrivialOverride = schema.trivialOverrideSections &&
|
|
340
431
|
schema.trivialOverrideSections.length > 0 &&
|
|
341
432
|
/trivial.change|mini.design|escape.hatch/iu.test(raw);
|
|
@@ -362,6 +453,69 @@ export async function lintArtifact(projectRoot, stage) {
|
|
|
362
453
|
: validation.details
|
|
363
454
|
});
|
|
364
455
|
}
|
|
456
|
+
if (stage === "plan") {
|
|
457
|
+
const strictPlanGuards = parsedFrontmatter.hasFrontmatter ||
|
|
458
|
+
headingPresent(sections, "No-Placeholder Scan") ||
|
|
459
|
+
headingPresent(sections, "No Scope Reduction Language Scan") ||
|
|
460
|
+
headingPresent(sections, "Locked Decision Coverage");
|
|
461
|
+
const taskListBody = sectionBodyByName(sections, "Task List") ?? raw;
|
|
462
|
+
const placeholderHits = collectPatternHits(taskListBody, PLACEHOLDER_PATTERNS);
|
|
463
|
+
findings.push({
|
|
464
|
+
section: "No Placeholder Enforcement",
|
|
465
|
+
required: strictPlanGuards,
|
|
466
|
+
rule: "Task List must not contain placeholders (TODO/TBD/FIXME/<fill-in>/<your-*-here>/xxx/ellipsis).",
|
|
467
|
+
found: placeholderHits.length === 0,
|
|
468
|
+
details: placeholderHits.length === 0
|
|
469
|
+
? "No placeholder tokens detected in Task List."
|
|
470
|
+
: `Detected placeholder token(s) in Task List: ${placeholderHits.join(", ")}.`
|
|
471
|
+
});
|
|
472
|
+
const scopePath = path.join(projectRoot, RUNTIME_ROOT, "artifacts", "02-scope.md");
|
|
473
|
+
const scopeRaw = (await exists(scopePath)) ? await fs.readFile(scopePath, "utf8") : "";
|
|
474
|
+
const scopeDecisionIds = extractDecisionIds(scopeRaw);
|
|
475
|
+
const missingDecisionRefs = scopeDecisionIds.filter((id) => !raw.includes(id));
|
|
476
|
+
findings.push({
|
|
477
|
+
section: "Locked Decision Traceability",
|
|
478
|
+
required: strictPlanGuards && scopeDecisionIds.length > 0,
|
|
479
|
+
rule: "Every locked decision ID (D-XX) in scope must be referenced in plan.",
|
|
480
|
+
found: missingDecisionRefs.length === 0,
|
|
481
|
+
details: scopeDecisionIds.length === 0
|
|
482
|
+
? "No D-XX IDs found in scope artifact; traceability check skipped."
|
|
483
|
+
: missingDecisionRefs.length === 0
|
|
484
|
+
? `All ${scopeDecisionIds.length} scope decision IDs are referenced in plan.`
|
|
485
|
+
: `Missing scope decision reference(s) in plan: ${missingDecisionRefs.join(", ")}.`
|
|
486
|
+
});
|
|
487
|
+
const reductionHits = collectPatternHits(taskListBody, SCOPE_REDUCTION_PATTERNS);
|
|
488
|
+
findings.push({
|
|
489
|
+
section: "No Scope Reduction Language",
|
|
490
|
+
required: strictPlanGuards && scopeDecisionIds.length > 0,
|
|
491
|
+
rule: "Task List must not include scope-reduction language when locked decisions exist.",
|
|
492
|
+
found: reductionHits.length === 0,
|
|
493
|
+
details: scopeDecisionIds.length === 0
|
|
494
|
+
? "No locked decisions found in scope artifact; scope-reduction scan is advisory."
|
|
495
|
+
: reductionHits.length === 0
|
|
496
|
+
? "No scope-reduction phrases detected in Task List."
|
|
497
|
+
: `Detected scope-reduction phrase(s) in Task List: ${reductionHits.join(", ")}.`
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
if (stage === "scope") {
|
|
501
|
+
const strictScopeGuards = parsedFrontmatter.hasFrontmatter ||
|
|
502
|
+
headingPresent(sections, "Locked Decisions (D-XX)");
|
|
503
|
+
const scopeSections = [
|
|
504
|
+
sectionBodyByName(sections, "In Scope / Out of Scope") ?? "",
|
|
505
|
+
sectionBodyByName(sections, "Scope Summary") ?? "",
|
|
506
|
+
sectionBodyByName(sections, "Locked Decisions (D-XX)") ?? ""
|
|
507
|
+
].join("\n");
|
|
508
|
+
const reductionHits = collectPatternHits(scopeSections, SCOPE_REDUCTION_PATTERNS);
|
|
509
|
+
findings.push({
|
|
510
|
+
section: "No Scope Reduction Language",
|
|
511
|
+
required: strictScopeGuards,
|
|
512
|
+
rule: "Scope boundary sections must not use reduction placeholders (`v1`, `for now`, `later`, `temporary`, `placeholder`).",
|
|
513
|
+
found: reductionHits.length === 0,
|
|
514
|
+
details: reductionHits.length === 0
|
|
515
|
+
? "No scope-reduction phrases detected in scope boundary sections."
|
|
516
|
+
: `Detected scope-reduction phrase(s): ${reductionHits.join(", ")}.`
|
|
517
|
+
});
|
|
518
|
+
}
|
|
365
519
|
const passed = findings.every((f) => !f.required || f.found);
|
|
366
520
|
return { stage, file: relFile, passed, findings };
|
|
367
521
|
}
|
package/dist/cli.js
CHANGED
|
@@ -122,7 +122,8 @@ function buildInitSurfacePreview(harnesses) {
|
|
|
122
122
|
".cclaw/rules/**",
|
|
123
123
|
".cclaw/adapters/*.md",
|
|
124
124
|
".cclaw/custom-skills/README.md",
|
|
125
|
-
".cclaw/
|
|
125
|
+
".cclaw/worktrees/**",
|
|
126
|
+
".cclaw/features/** (legacy snapshots, read-only migration)",
|
|
126
127
|
".cclaw/runs/**",
|
|
127
128
|
".cclaw/artifacts/**",
|
|
128
129
|
".cclaw/knowledge.jsonl",
|
package/dist/constants.d.ts
CHANGED
|
@@ -4,9 +4,9 @@ export declare const RUNTIME_ROOT = ".cclaw";
|
|
|
4
4
|
export declare const CCLAW_VERSION = "0.1.1";
|
|
5
5
|
export declare const FLOW_VERSION = "1.0.0";
|
|
6
6
|
export declare const DEFAULT_HARNESSES: HarnessId[];
|
|
7
|
-
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/
|
|
7
|
+
export declare const REQUIRED_DIRS: readonly [".cclaw", ".cclaw/commands", ".cclaw/skills", ".cclaw/contexts", ".cclaw/templates", ".cclaw/artifacts", ".cclaw/worktrees", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills"];
|
|
8
8
|
export declare const REQUIRED_GITIGNORE_PATTERNS: readonly ["# cclaw generated artifacts", ".cclaw/", ".claude/commands/cc-*.md", ".claude/commands/cc.md", ".cursor/commands/cc-*.md", ".cursor/commands/cc.md", ".opencode/commands/cc-*.md", ".opencode/commands/cc.md", ".codex/commands/cc-*.md", ".codex/commands/cc.md", ".claude/hooks/hooks.json", ".cursor/hooks.json", ".codex/hooks.json", ".opencode/plugins/cclaw-plugin.mjs", ".cursor/rules/cclaw-workflow.mdc"];
|
|
9
9
|
export declare const COMMAND_FILE_ORDER: FlowStage[];
|
|
10
|
-
export declare const UTILITY_COMMANDS: readonly ["learn", "next", "view", "status", "tree", "diff", "ops", "feature", "tdd-log", "retro", "
|
|
10
|
+
export declare const UTILITY_COMMANDS: readonly ["learn", "next", "ideate", "view", "status", "tree", "diff", "ops", "feature", "tdd-log", "retro", "compound", "archive", "rewind"];
|
|
11
11
|
export declare const SUBAGENT_SKILL_FOLDERS: readonly ["subagent-dev", "parallel-dispatch"];
|
|
12
12
|
export type UtilityCommand = (typeof UTILITY_COMMANDS)[number];
|
package/dist/constants.js
CHANGED
|
@@ -15,7 +15,7 @@ export const REQUIRED_DIRS = [
|
|
|
15
15
|
`${RUNTIME_ROOT}/contexts`,
|
|
16
16
|
`${RUNTIME_ROOT}/templates`,
|
|
17
17
|
`${RUNTIME_ROOT}/artifacts`,
|
|
18
|
-
`${RUNTIME_ROOT}/
|
|
18
|
+
`${RUNTIME_ROOT}/worktrees`,
|
|
19
19
|
`${RUNTIME_ROOT}/state`,
|
|
20
20
|
`${RUNTIME_ROOT}/runs`,
|
|
21
21
|
`${RUNTIME_ROOT}/rules`,
|
|
@@ -54,6 +54,7 @@ export const COMMAND_FILE_ORDER = [
|
|
|
54
54
|
export const UTILITY_COMMANDS = [
|
|
55
55
|
"learn",
|
|
56
56
|
"next",
|
|
57
|
+
"ideate",
|
|
57
58
|
"view",
|
|
58
59
|
"status",
|
|
59
60
|
"tree",
|
|
@@ -62,9 +63,9 @@ export const UTILITY_COMMANDS = [
|
|
|
62
63
|
"feature",
|
|
63
64
|
"tdd-log",
|
|
64
65
|
"retro",
|
|
66
|
+
"compound",
|
|
65
67
|
"archive",
|
|
66
|
-
"rewind"
|
|
67
|
-
"rewind-ack"
|
|
68
|
+
"rewind"
|
|
68
69
|
];
|
|
69
70
|
export const SUBAGENT_SKILL_FOLDERS = [
|
|
70
71
|
"subagent-dev",
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const COMPOUND_SKILL_FOLDER = "flow-compound";
|
|
3
|
+
const COMPOUND_SKILL_NAME = "flow-compound";
|
|
4
|
+
export function compoundCommandContract() {
|
|
5
|
+
return `# /cc-ops compound
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Lift repeated lessons into durable project assets (rules, protocols, skills)
|
|
10
|
+
so the next run is easier and safer.
|
|
11
|
+
|
|
12
|
+
## HARD-GATE
|
|
13
|
+
|
|
14
|
+
- Do not mutate rules/skills without explicit user approval.
|
|
15
|
+
- Every proposal must cite concrete knowledge evidence (line references or IDs).
|
|
16
|
+
- Keep scope focused: one compound change set per run.
|
|
17
|
+
|
|
18
|
+
## Algorithm
|
|
19
|
+
|
|
20
|
+
1. Read \`${RUNTIME_ROOT}/knowledge.jsonl\`.
|
|
21
|
+
2. Cluster repeated trigger/action pairs.
|
|
22
|
+
3. For clusters with frequency >= 3, propose one lift action:
|
|
23
|
+
- rule update
|
|
24
|
+
- protocol update
|
|
25
|
+
- utility skill update
|
|
26
|
+
4. For each proposal include:
|
|
27
|
+
- why now
|
|
28
|
+
- target file(s)
|
|
29
|
+
- expected risk reduction
|
|
30
|
+
5. Ask user approval for each proposal before writing.
|
|
31
|
+
6. Apply approved lifts and record completion in retro artifact.
|
|
32
|
+
|
|
33
|
+
## Primary skill
|
|
34
|
+
|
|
35
|
+
**${RUNTIME_ROOT}/skills/${COMPOUND_SKILL_FOLDER}/SKILL.md**
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
export function compoundCommandSkillMarkdown() {
|
|
39
|
+
return `---
|
|
40
|
+
name: ${COMPOUND_SKILL_NAME}
|
|
41
|
+
description: "Compound mode: convert repeated learnings into durable rules/protocols/skills."
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
# /cc-ops compound
|
|
45
|
+
|
|
46
|
+
## Announce at start
|
|
47
|
+
|
|
48
|
+
"Using flow-compound to lift repeated learnings into durable workflow assets."
|
|
49
|
+
|
|
50
|
+
## HARD-GATE
|
|
51
|
+
|
|
52
|
+
No silent codification. Every lift requires explicit user approval.
|
|
53
|
+
|
|
54
|
+
## Protocol
|
|
55
|
+
|
|
56
|
+
1. Parse \`.cclaw/knowledge.jsonl\` and group repeated lessons.
|
|
57
|
+
2. Keep only candidates with clear recurrence and actionable lift path.
|
|
58
|
+
3. Propose each candidate using this template:
|
|
59
|
+
|
|
60
|
+
\`\`\`
|
|
61
|
+
Candidate: <short title>
|
|
62
|
+
Evidence: <knowledge refs>
|
|
63
|
+
Lift target: <rule/protocol/skill file>
|
|
64
|
+
Change type: <add/update/remove>
|
|
65
|
+
Expected benefit: <what regressions this prevents>
|
|
66
|
+
\`\`\`
|
|
67
|
+
|
|
68
|
+
4. Ask user to approve/reject per candidate.
|
|
69
|
+
5. Apply only approved candidates.
|
|
70
|
+
6. Append a \`compound\` learning entry summarizing what was lifted.
|
|
71
|
+
`;
|
|
72
|
+
}
|
|
@@ -34,7 +34,7 @@ ${schema.hardGate}
|
|
|
34
34
|
2. Resolve active artifact root: \`.cclaw/artifacts/\`.
|
|
35
35
|
3. Load required upstream artifacts for this stage:
|
|
36
36
|
${hydrationLines}
|
|
37
|
-
4. Stream \`.cclaw/knowledge.jsonl\` and apply relevant JSON-line entries (strict schema: type, trigger, action, confidence, domain, stage, created, project).
|
|
37
|
+
4. Stream \`.cclaw/knowledge.jsonl\` and apply relevant JSON-line entries (strict schema: type, trigger, action, confidence, domain, stage, origin_stage, origin_feature, frequency, universality, maturity, created, first_seen_ts, last_seen_ts, project).
|
|
38
38
|
5. Write stage output to ${writeStepPaths}.
|
|
39
39
|
6. Do NOT copy artifacts into \`.cclaw/runs/\`; archival is handled by \`/cc-ops archive\` (agent-facing wrapper over archive runtime).
|
|
40
40
|
|
|
@@ -11,7 +11,7 @@ Reference docs for \`cclaw doctor\` checks.
|
|
|
11
11
|
- \`hooks-and-lifecycle.md\` - hook wiring and harness lifecycle integration
|
|
12
12
|
- \`harness-and-routing.md\` - harness shims, AGENTS/CLAUDE routing blocks, cursor rule
|
|
13
13
|
- \`state-and-gates.md\` - flow-state integrity and gate evidence contracts
|
|
14
|
-
- \`delegation-and-preamble.md\` - mandatory delegations and
|
|
14
|
+
- \`delegation-and-preamble.md\` - mandatory delegations and lightweight announce discipline
|
|
15
15
|
- \`traceability.md\` - spec/plan/tdd trace matrix expectations
|
|
16
16
|
- \`tooling-capabilities.md\` - local runtime prerequisites (bash/node/python/jq)
|
|
17
17
|
- \`config-and-policy.md\` - config schema, rules policy, and validation references
|
|
@@ -84,18 +84,19 @@ Reference docs for \`cclaw doctor\` checks.
|
|
|
84
84
|
- mandatory delegations for the current stage must be completed or waived
|
|
85
85
|
- waivers should include an explicit reason
|
|
86
86
|
- stale entries from previous runs are ignored by current-run checks
|
|
87
|
+
- delegation entries use span-compatible fields (\`spanId\`, \`startTs\`, \`endTs\`, \`retryCount\`, \`evidenceRefs\`)
|
|
87
88
|
|
|
88
|
-
##
|
|
89
|
+
## Announce discipline contract
|
|
89
90
|
|
|
90
|
-
- preamble
|
|
91
|
-
-
|
|
92
|
-
-
|
|
91
|
+
- no dedicated preamble runtime log is required
|
|
92
|
+
- substantial turns should still start with a concise announce (stage + goal + next action)
|
|
93
|
+
- do not spam repeated announces when intent did not change
|
|
93
94
|
|
|
94
95
|
## Typical fixes
|
|
95
96
|
|
|
96
97
|
1. Append missing delegation records with \`completed\` or \`waived\` status.
|
|
97
98
|
2. Record harness-limitation waivers when native delegation is unavailable.
|
|
98
|
-
3.
|
|
99
|
+
3. Keep announces concise and only refresh when plan/risk materially changes.
|
|
99
100
|
`,
|
|
100
101
|
"traceability.md": `# Traceability
|
|
101
102
|
|
|
@@ -1,66 +1,71 @@
|
|
|
1
1
|
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
-
const FEATURE_SKILL_FOLDER = "
|
|
3
|
-
const FEATURE_SKILL_NAME = "
|
|
2
|
+
const FEATURE_SKILL_FOLDER = "using-git-worktrees";
|
|
3
|
+
const FEATURE_SKILL_NAME = "using-git-worktrees";
|
|
4
4
|
function activeFeaturePath() {
|
|
5
5
|
return `${RUNTIME_ROOT}/state/active-feature.json`;
|
|
6
6
|
}
|
|
7
|
-
function
|
|
8
|
-
return `${RUNTIME_ROOT}/
|
|
7
|
+
function worktreeRegistryPath() {
|
|
8
|
+
return `${RUNTIME_ROOT}/state/worktrees.json`;
|
|
9
9
|
}
|
|
10
|
-
function
|
|
11
|
-
return `${RUNTIME_ROOT}/
|
|
10
|
+
function managedWorktreesRoot() {
|
|
11
|
+
return `${RUNTIME_ROOT}/worktrees`;
|
|
12
12
|
}
|
|
13
|
-
function
|
|
14
|
-
return `${RUNTIME_ROOT}/
|
|
13
|
+
function legacyFeaturesRoot() {
|
|
14
|
+
return `${RUNTIME_ROOT}/features`;
|
|
15
15
|
}
|
|
16
16
|
export function featureCommandContract() {
|
|
17
17
|
return `# /cc-ops feature
|
|
18
18
|
|
|
19
19
|
## Purpose
|
|
20
20
|
|
|
21
|
-
Manage
|
|
22
|
-
|
|
23
|
-
The active runtime remains:
|
|
24
|
-
- \`${runtimeArtifactsPath()}\` (active artifacts)
|
|
25
|
-
- \`${runtimeStatePath()}\` (active state)
|
|
21
|
+
Manage parallel feature execution using git worktrees (git-native isolation).
|
|
26
22
|
|
|
27
|
-
|
|
23
|
+
Runtime state/artifacts are **never** copied between features anymore. Isolation is branch/worktree-level.
|
|
28
24
|
|
|
29
25
|
## HARD-GATE
|
|
30
26
|
|
|
31
|
-
-
|
|
32
|
-
-
|
|
33
|
-
- Keep \`${activeFeaturePath()}\` as the
|
|
27
|
+
- Do not mutate feature context by copying \`${RUNTIME_ROOT}/artifacts\` or \`${RUNTIME_ROOT}/state\` between feature IDs.
|
|
28
|
+
- Use \`git worktree add\` for new feature execution paths.
|
|
29
|
+
- Keep \`${activeFeaturePath()}\` + \`${worktreeRegistryPath()}\` as the feature routing source of truth.
|
|
30
|
+
- Treat \`${legacyFeaturesRoot()}/\` as read-only migration data.
|
|
34
31
|
|
|
35
32
|
## Subcommands
|
|
36
33
|
|
|
37
34
|
### \`/cc-ops feature status\`
|
|
38
|
-
Show
|
|
35
|
+
Show:
|
|
36
|
+
- active feature id from \`${activeFeaturePath()}\`
|
|
37
|
+
- resolved worktree entry from \`${worktreeRegistryPath()}\`
|
|
38
|
+
- active workspace path
|
|
39
39
|
|
|
40
40
|
### \`/cc-ops feature list\`
|
|
41
|
-
List
|
|
41
|
+
List registered feature worktrees from \`${worktreeRegistryPath()}\` and mark active entry.
|
|
42
42
|
|
|
43
43
|
### \`/cc-ops feature new <feature-id>\`
|
|
44
|
-
|
|
44
|
+
1. Validate \`feature-id\` (lowercase slug, letters/numbers/dashes).
|
|
45
|
+
2. Create worktree under \`${managedWorktreesRoot()}/<feature-id>\`.
|
|
46
|
+
3. Create/switch branch using \`git worktree add\` (prefer \`feature/<feature-id>\` naming).
|
|
47
|
+
4. Register entry in \`${worktreeRegistryPath()}\`.
|
|
45
48
|
|
|
46
|
-
Optional
|
|
47
|
-
- \`--clone-active\`:
|
|
49
|
+
Optional flags:
|
|
50
|
+
- \`--clone-active\`: seed from active branch HEAD (default behavior).
|
|
51
|
+
- \`--switch\`: mark new feature as active after registration.
|
|
48
52
|
|
|
49
53
|
### \`/cc-ops feature switch <feature-id>\`
|
|
50
|
-
1.
|
|
51
|
-
2.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
54
|
+
1. Validate that \`<feature-id>\` exists in \`${worktreeRegistryPath()}\`.
|
|
55
|
+
2. Update \`${activeFeaturePath()}\`.
|
|
56
|
+
3. Print target worktree path and instruct the operator/agent to continue from that workspace root.
|
|
57
|
+
|
|
58
|
+
## Migration note
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
Legacy snapshot folders under \`${legacyFeaturesRoot()}/\` are supported as read-only references during migration and should not be used for new execution.
|
|
57
61
|
|
|
58
62
|
## Output
|
|
59
63
|
|
|
60
64
|
Always print:
|
|
61
65
|
- active feature before
|
|
62
66
|
- active feature after
|
|
63
|
-
-
|
|
67
|
+
- target workspace path
|
|
68
|
+
- workspace source (\`git-worktree\` | \`workspace\` | \`legacy-snapshot\`)
|
|
64
69
|
|
|
65
70
|
## Primary skill
|
|
66
71
|
|
|
@@ -70,51 +75,49 @@ Always print:
|
|
|
70
75
|
export function featureCommandSkillMarkdown() {
|
|
71
76
|
return `---
|
|
72
77
|
name: ${FEATURE_SKILL_NAME}
|
|
73
|
-
description: "Manage cclaw
|
|
78
|
+
description: "Manage cclaw feature isolation using git worktrees (status/list/new/switch)."
|
|
74
79
|
---
|
|
75
80
|
|
|
76
|
-
# /cc-ops feature —
|
|
81
|
+
# /cc-ops feature — Git Worktree Manager
|
|
77
82
|
|
|
78
83
|
## HARD-GATE
|
|
79
84
|
|
|
80
|
-
Do not
|
|
85
|
+
Do not implement feature switching by copying runtime files between feature IDs. Use git worktrees and registry updates only.
|
|
81
86
|
|
|
82
87
|
## Paths
|
|
83
88
|
|
|
84
89
|
- Active pointer: \`${activeFeaturePath()}\`
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
90
|
+
- Worktree registry: \`${worktreeRegistryPath()}\`
|
|
91
|
+
- Managed worktree root: \`${managedWorktreesRoot()}\`
|
|
92
|
+
- Legacy snapshots (read-only): \`${legacyFeaturesRoot()}\`
|
|
88
93
|
|
|
89
94
|
## Protocol
|
|
90
95
|
|
|
91
96
|
### status
|
|
92
97
|
1. Read \`${activeFeaturePath()}\`.
|
|
93
|
-
2.
|
|
98
|
+
2. Resolve active entry in \`${worktreeRegistryPath()}\`.
|
|
99
|
+
3. Print active id + workspace path + source.
|
|
94
100
|
|
|
95
101
|
### list
|
|
96
|
-
1. Enumerate
|
|
102
|
+
1. Enumerate entries in \`${worktreeRegistryPath()}\`.
|
|
97
103
|
2. Mark the active one.
|
|
104
|
+
3. Highlight any \`legacy-snapshot\` entries as migration-only.
|
|
98
105
|
|
|
99
|
-
### new <feature-id> [--clone-active]
|
|
100
|
-
1. Validate \`feature-id\`
|
|
101
|
-
2.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
3. If \`--clone-active\`: copy active runtime artifacts/state into the new snapshot.
|
|
105
|
-
4. Do not change active feature unless the user explicitly requests switch.
|
|
106
|
+
### new <feature-id> [--clone-active] [--switch]
|
|
107
|
+
1. Validate \`feature-id\` and ensure not already registered.
|
|
108
|
+
2. Run \`git worktree add\` to create \`${managedWorktreesRoot()}/<feature-id>\`.
|
|
109
|
+
3. Register entry in \`${worktreeRegistryPath()}\` with branch + path + source.
|
|
110
|
+
4. If \`--switch\`, update \`${activeFeaturePath()}\`.
|
|
106
111
|
|
|
107
112
|
### switch <feature-id>
|
|
108
|
-
1.
|
|
109
|
-
2.
|
|
110
|
-
3.
|
|
111
|
-
4. Update \`${activeFeaturePath()}\`.
|
|
112
|
-
5. Report stage/run after restore (\`flow-state.json\`).
|
|
113
|
+
1. Validate target exists in \`${worktreeRegistryPath()}\`.
|
|
114
|
+
2. Update \`${activeFeaturePath()}\`.
|
|
115
|
+
3. Report target path and require continuation from that workspace root.
|
|
113
116
|
|
|
114
117
|
## Safety checks
|
|
115
118
|
|
|
116
119
|
- If target feature does not exist: block and suggest \`/cc-ops feature new <id>\`.
|
|
117
|
-
- If
|
|
118
|
-
-
|
|
120
|
+
- If \`git worktree add\` fails: do not write partial registry updates.
|
|
121
|
+
- If active feature maps to \`legacy-snapshot\`, report read-only migration warning.
|
|
119
122
|
`;
|
|
120
123
|
}
|
|
@@ -65,9 +65,10 @@ All harnesses receive the same utility commands:
|
|
|
65
65
|
|
|
66
66
|
- \`/cc\` - flow entry and resume
|
|
67
67
|
- \`/cc-next\` - stage progression
|
|
68
|
+
- \`/cc-ideate\` - discovery mode for ranked repo-improvement backlog
|
|
68
69
|
- \`/cc-view\` - read-only router for status/tree/diff
|
|
69
70
|
- \`/cc-learn\` - knowledge capture/lookup
|
|
70
|
-
- \`/cc-ops\` - operations router for feature/tdd-log/retro/archive/rewind
|
|
71
|
+
- \`/cc-ops\` - operations router for feature/tdd-log/retro/compound/archive/rewind
|
|
71
72
|
|
|
72
73
|
Read-only subcommands:
|
|
73
74
|
- \`/cc-view status\` - visual flow snapshot
|
|
@@ -75,12 +76,13 @@ Read-only subcommands:
|
|
|
75
76
|
- \`/cc-view diff\` - before/after flow-state diff map
|
|
76
77
|
|
|
77
78
|
Operations subcommands:
|
|
78
|
-
- \`/cc-ops feature ...\` -
|
|
79
|
+
- \`/cc-ops feature ...\` - git-worktree feature isolation and routing
|
|
79
80
|
- \`/cc-ops tdd-log ...\` - explicit RED/GREEN/REFACTOR evidence log
|
|
80
81
|
- \`/cc-ops retro\` - mandatory retrospective gate before archive
|
|
82
|
+
- \`/cc-ops compound\` - lift repeated learnings into durable rules/skills
|
|
81
83
|
- \`/cc-ops archive\` - archive active run from harness flow
|
|
82
84
|
- \`/cc-ops rewind ...\` - rewind flow and invalidate downstream stages
|
|
83
|
-
- \`/cc-ops rewind
|
|
85
|
+
- \`/cc-ops rewind --ack ...\` - clear stale stage markers after redo
|
|
84
86
|
|
|
85
87
|
Stage order remains canonical:
|
|
86
88
|
\`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship\`
|
package/dist/content/hooks.js
CHANGED
|
@@ -457,7 +457,7 @@ if [ -n "$ROUTING_MISSING" ]; then
|
|
|
457
457
|
fi
|
|
458
458
|
|
|
459
459
|
# --- Build context message ---
|
|
460
|
-
CTX="cclaw loaded. Flow: stage=$STAGE ($COMPLETED/8 completed, run=$ACTIVE_RUN, feature=$ACTIVE_FEATURE). Active artifacts: ${RUNTIME_ROOT}/artifacts/. Feature
|
|
460
|
+
CTX="cclaw loaded. Flow: stage=$STAGE ($COMPLETED/8 completed, run=$ACTIVE_RUN, feature=$ACTIVE_FEATURE). Active artifacts: ${RUNTIME_ROOT}/artifacts/. Feature registry: ${RUNTIME_ROOT}/state/worktrees.json (managed roots: ${RUNTIME_ROOT}/worktrees/). Learnings: $LEARNINGS_COUNT entries."
|
|
461
461
|
if [ -n "$VERSION_NOTE" ]; then
|
|
462
462
|
CTX="$CTX
|
|
463
463
|
$VERSION_NOTE"
|
|
@@ -496,7 +496,7 @@ To disable suggestions persistently set ${RUNTIME_ROOT}/state/suggestion-memory.
|
|
|
496
496
|
fi
|
|
497
497
|
if [ -n "$STALE_STAGES" ]; then
|
|
498
498
|
CTX="$CTX
|
|
499
|
-
Stale stages pending acknowledgement: $STALE_STAGES (use /cc-ops rewind
|
|
499
|
+
Stale stages pending acknowledgement: $STALE_STAGES (use /cc-ops rewind --ack <stage> after redo)."
|
|
500
500
|
fi
|
|
501
501
|
if [ -n "$KNOWLEDGE_DIGEST" ]; then
|
|
502
502
|
CTX="$CTX
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { RUNTIME_ROOT } from "../constants.js";
|
|
2
|
+
const IDEATE_SKILL_FOLDER = "flow-ideate";
|
|
3
|
+
const IDEATE_SKILL_NAME = "flow-ideate";
|
|
4
|
+
export function ideateCommandContract() {
|
|
5
|
+
return `# /cc-ideate
|
|
6
|
+
|
|
7
|
+
## Purpose
|
|
8
|
+
|
|
9
|
+
Repository-improvement discovery mode. Generate a ranked backlog of high-value
|
|
10
|
+
improvements before committing to a specific feature request.
|
|
11
|
+
|
|
12
|
+
## HARD-GATE
|
|
13
|
+
|
|
14
|
+
- This is discovery mode only. Do not start implementation automatically.
|
|
15
|
+
- Every recommendation must include evidence from the current repository.
|
|
16
|
+
- End with a decision prompt: pick one candidate or cancel.
|
|
17
|
+
|
|
18
|
+
## Algorithm
|
|
19
|
+
|
|
20
|
+
1. Scan repo signals:
|
|
21
|
+
- open TODO/backlog notes
|
|
22
|
+
- flaky or failing tests
|
|
23
|
+
- oversized modules / complexity hotspots
|
|
24
|
+
- docs drift vs changed code
|
|
25
|
+
- repeated learnings from \`.cclaw/knowledge.jsonl\`
|
|
26
|
+
2. Produce 5-10 candidates with:
|
|
27
|
+
- impact (High/Medium/Low)
|
|
28
|
+
- effort (S/M/L)
|
|
29
|
+
- confidence (High/Medium/Low)
|
|
30
|
+
- evidence path(s)
|
|
31
|
+
3. Rank candidates by impact/effort ratio.
|
|
32
|
+
4. Present one recommendation as default.
|
|
33
|
+
5. Ask user to choose:
|
|
34
|
+
- (A) start with recommended item
|
|
35
|
+
- (B) choose another candidate
|
|
36
|
+
- (C) cancel
|
|
37
|
+
|
|
38
|
+
## Primary skill
|
|
39
|
+
|
|
40
|
+
**${RUNTIME_ROOT}/skills/${IDEATE_SKILL_FOLDER}/SKILL.md**
|
|
41
|
+
`;
|
|
42
|
+
}
|
|
43
|
+
export function ideateCommandSkillMarkdown() {
|
|
44
|
+
return `---
|
|
45
|
+
name: ${IDEATE_SKILL_NAME}
|
|
46
|
+
description: "Repository ideation mode: detect and rank high-leverage improvements before implementation."
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
# /cc-ideate
|
|
50
|
+
|
|
51
|
+
## Announce at start
|
|
52
|
+
|
|
53
|
+
"Using flow-ideate to identify highest-leverage improvements in this repository."
|
|
54
|
+
|
|
55
|
+
## HARD-GATE
|
|
56
|
+
|
|
57
|
+
Do not start coding in ideate mode. End with an explicit user choice.
|
|
58
|
+
|
|
59
|
+
## Protocol
|
|
60
|
+
|
|
61
|
+
1. Collect evidence from the current repo state.
|
|
62
|
+
2. Build candidate improvements with impact/effort/confidence.
|
|
63
|
+
3. Rank and recommend one candidate.
|
|
64
|
+
4. Ask for explicit selection.
|
|
65
|
+
5. If user selects a candidate, hand off to \`/cc <selected idea>\`.
|
|
66
|
+
|
|
67
|
+
## Candidate format
|
|
68
|
+
|
|
69
|
+
| ID | Improvement | Impact | Effort | Confidence | Evidence |
|
|
70
|
+
|---|---|---|---|---|---|
|
|
71
|
+
| I-1 | | High/Medium/Low | S/M/L | High/Medium/Low | path or command evidence |
|
|
72
|
+
`;
|
|
73
|
+
}
|