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.
Files changed (42) hide show
  1. package/dist/artifact-linter.js +154 -0
  2. package/dist/cli.js +2 -1
  3. package/dist/constants.d.ts +2 -2
  4. package/dist/constants.js +4 -3
  5. package/dist/content/compound-command.d.ts +2 -0
  6. package/dist/content/compound-command.js +72 -0
  7. package/dist/content/contracts.js +1 -1
  8. package/dist/content/doctor-references.js +7 -6
  9. package/dist/content/feature-command.js +54 -51
  10. package/dist/content/harnesses-doc.js +5 -3
  11. package/dist/content/hooks.js +2 -2
  12. package/dist/content/ideate-command.d.ts +2 -0
  13. package/dist/content/ideate-command.js +73 -0
  14. package/dist/content/learnings.d.ts +1 -1
  15. package/dist/content/learnings.js +22 -5
  16. package/dist/content/meta-skill.js +6 -3
  17. package/dist/content/next-command.js +5 -5
  18. package/dist/content/observe.js +3 -2
  19. package/dist/content/ops-command.js +4 -4
  20. package/dist/content/protocols.js +27 -38
  21. package/dist/content/retro-command.js +2 -1
  22. package/dist/content/rewind-command.d.ts +0 -1
  23. package/dist/content/rewind-command.js +19 -33
  24. package/dist/content/skills.js +14 -8
  25. package/dist/content/stage-schema.js +3 -38
  26. package/dist/content/stages/plan.js +16 -5
  27. package/dist/content/stages/review.js +20 -0
  28. package/dist/content/stages/scope.js +9 -3
  29. package/dist/content/stages/ship.js +1 -0
  30. package/dist/content/stages/tdd.js +5 -4
  31. package/dist/content/templates.js +105 -9
  32. package/dist/content/utility-skills.d.ts +3 -1
  33. package/dist/content/utility-skills.js +91 -1
  34. package/dist/delegation.d.ts +33 -3
  35. package/dist/delegation.js +56 -3
  36. package/dist/doctor.js +269 -88
  37. package/dist/feature-system.d.ts +22 -5
  38. package/dist/feature-system.js +267 -126
  39. package/dist/harness-adapters.js +17 -1
  40. package/dist/install.js +10 -8
  41. package/dist/policy.js +13 -4
  42. package/package.json +1 -1
@@ -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/features/**",
125
+ ".cclaw/worktrees/**",
126
+ ".cclaw/features/** (legacy snapshots, read-only migration)",
126
127
  ".cclaw/runs/**",
127
128
  ".cclaw/artifacts/**",
128
129
  ".cclaw/knowledge.jsonl",
@@ -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/features", ".cclaw/state", ".cclaw/runs", ".cclaw/rules", ".cclaw/adapters", ".cclaw/agents", ".cclaw/hooks", ".cclaw/custom-skills"];
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", "archive", "rewind", "rewind-ack"];
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}/features`,
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,2 @@
1
+ export declare function compoundCommandContract(): string;
2
+ export declare function compoundCommandSkillMarkdown(): string;
@@ -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 preamble budget controls
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
- ## Preamble budget contract
89
+ ## Announce discipline contract
89
90
 
90
- - preamble events are logged to \`.cclaw/state/preamble-log.jsonl\`
91
- - repeated entries inside cooldown windows indicate noisy preamble behavior
92
- - in TDD wave mode, emit once per wave unless plan materially changes
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. Reduce repeated preamble emissions and keep logs structured (\`ts/stage/trigger/hash\`).
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 = "feature-workspaces";
3
- const FEATURE_SKILL_NAME = "feature-workspaces";
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 featuresRoot() {
8
- return `${RUNTIME_ROOT}/features`;
7
+ function worktreeRegistryPath() {
8
+ return `${RUNTIME_ROOT}/state/worktrees.json`;
9
9
  }
10
- function runtimeArtifactsPath() {
11
- return `${RUNTIME_ROOT}/artifacts`;
10
+ function managedWorktreesRoot() {
11
+ return `${RUNTIME_ROOT}/worktrees`;
12
12
  }
13
- function runtimeStatePath() {
14
- return `${RUNTIME_ROOT}/state`;
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 multi-feature workspaces without flow-state/artifact collisions.
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
- Feature snapshots live under \`${featuresRoot()}/<feature-id>/\`.
23
+ Runtime state/artifacts are **never** copied between features anymore. Isolation is branch/worktree-level.
28
24
 
29
25
  ## HARD-GATE
30
26
 
31
- - Never overwrite another feature snapshot silently.
32
- - Before switching feature, snapshot the current active runtime first.
33
- - Keep \`${activeFeaturePath()}\` as the single source of "current feature".
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 active feature id and snapshot location.
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 all feature ids in \`${featuresRoot()}/\` (directory names).
41
+ List registered feature worktrees from \`${worktreeRegistryPath()}\` and mark active entry.
42
42
 
43
43
  ### \`/cc-ops feature new <feature-id>\`
44
- Create \`${featuresRoot()}/<feature-id>/artifacts\` and \`${featuresRoot()}/<feature-id>/state\`.
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 flag:
47
- - \`--clone-active\`: clone current active runtime into the new feature snapshot.
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. Snapshot current active runtime into \`${featuresRoot()}/<active>/\`.
51
- 2. Restore target snapshot from \`${featuresRoot()}/<feature-id>/\` into active runtime:
52
- - \`${runtimeArtifactsPath()}\`
53
- - \`${runtimeStatePath()}\` (preserve \`active-feature.json\`)
54
- 3. Update \`${activeFeaturePath()}\` with \`activeFeature=<feature-id>\`.
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
- If the target snapshot is empty, initialize runtime as a fresh flow.
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
- - whether snapshot/restore changed files
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 multi-feature workspaces (status/list/new/switch) while preserving active flow runtime."
78
+ description: "Manage cclaw feature isolation using git worktrees (status/list/new/switch)."
74
79
  ---
75
80
 
76
- # /cc-ops feature — Feature Workspace Manager
81
+ # /cc-ops feature — Git Worktree Manager
77
82
 
78
83
  ## HARD-GATE
79
84
 
80
- Do not switch feature by editing only \`active-feature.json\`. A valid switch must snapshot current runtime and restore target runtime.
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
- - Feature snapshots: \`${featuresRoot()}/<feature-id>/\`
86
- - Active runtime artifacts: \`${runtimeArtifactsPath()}\`
87
- - Active runtime state: \`${runtimeStatePath()}\`
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. Print active feature id and its snapshot folder.
98
+ 2. Resolve active entry in \`${worktreeRegistryPath()}\`.
99
+ 3. Print active id + workspace path + source.
94
100
 
95
101
  ### list
96
- 1. Enumerate directories in \`${featuresRoot()}/\`.
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\` (lowercase slug, letters/numbers/dashes).
101
- 2. Create snapshot dirs:
102
- - \`${featuresRoot()}/<feature-id>/artifacts\`
103
- - \`${featuresRoot()}/<feature-id>/state\`
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. Read current active feature id.
109
- 2. Snapshot current runtime into current feature snapshot.
110
- 3. Restore target snapshot into active runtime.
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 snapshot copy fails: abort switch, keep current active feature unchanged.
118
- - Preserve global pointer file \`active-feature.json\` when restoring state.
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 ...\` - multi-feature workspace management
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-ack ...\` - clear stale stage markers after redo
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\`
@@ -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 snapshots: ${RUNTIME_ROOT}/features/$ACTIVE_FEATURE/. Learnings: $LEARNINGS_COUNT entries."
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-ack <stage> after redo)."
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,2 @@
1
+ export declare function ideateCommandContract(): string;
2
+ export declare function ideateCommandSkillMarkdown(): string;
@@ -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
+ }