cclaw-cli 0.48.35 → 0.51.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 (181) hide show
  1. package/README.md +54 -82
  2. package/dist/artifact-linter.d.ts +4 -0
  3. package/dist/artifact-linter.js +24 -3
  4. package/dist/cli.d.ts +1 -19
  5. package/dist/cli.js +49 -495
  6. package/dist/constants.d.ts +2 -13
  7. package/dist/constants.js +1 -46
  8. package/dist/content/closeout-guidance.d.ts +14 -0
  9. package/dist/content/closeout-guidance.js +42 -0
  10. package/dist/content/core-agents.js +51 -9
  11. package/dist/content/decision-protocol.d.ts +12 -0
  12. package/dist/content/decision-protocol.js +20 -0
  13. package/dist/content/diff-command.d.ts +1 -2
  14. package/dist/content/diff-command.js +8 -94
  15. package/dist/content/examples.d.ts +4 -10
  16. package/dist/content/examples.js +10 -20
  17. package/dist/content/hook-events.js +2 -2
  18. package/dist/content/hook-inline-snippets.d.ts +5 -2
  19. package/dist/content/hook-inline-snippets.js +33 -1
  20. package/dist/content/hook-manifest.d.ts +3 -4
  21. package/dist/content/hook-manifest.js +11 -12
  22. package/dist/content/hooks.js +2 -0
  23. package/dist/content/ideate-command.d.ts +2 -0
  24. package/dist/content/ideate-command.js +31 -25
  25. package/dist/content/iron-laws.d.ts +5 -5
  26. package/dist/content/iron-laws.js +5 -5
  27. package/dist/content/learnings.d.ts +3 -4
  28. package/dist/content/learnings.js +24 -50
  29. package/dist/content/meta-skill.js +31 -24
  30. package/dist/content/next-command.js +38 -38
  31. package/dist/content/node-hooks.js +17 -343
  32. package/dist/content/opencode-plugin.js +2 -100
  33. package/dist/content/research-playbooks.js +14 -14
  34. package/dist/content/review-loop.d.ts +2 -0
  35. package/dist/content/review-loop.js +8 -0
  36. package/dist/content/session-hooks.js +14 -46
  37. package/dist/content/skills.d.ts +0 -5
  38. package/dist/content/skills.js +53 -128
  39. package/dist/content/stage-common-guidance.d.ts +0 -1
  40. package/dist/content/stage-common-guidance.js +15 -14
  41. package/dist/content/stage-schema.d.ts +26 -1
  42. package/dist/content/stage-schema.js +121 -40
  43. package/dist/content/stages/_lint-metadata/index.js +9 -15
  44. package/dist/content/stages/brainstorm.js +22 -43
  45. package/dist/content/stages/design.js +37 -57
  46. package/dist/content/stages/plan.js +22 -13
  47. package/dist/content/stages/review.js +24 -27
  48. package/dist/content/stages/scope.js +34 -46
  49. package/dist/content/stages/ship.js +7 -4
  50. package/dist/content/stages/spec.js +20 -9
  51. package/dist/content/stages/tdd.js +64 -44
  52. package/dist/content/start-command.js +10 -12
  53. package/dist/content/status-command.d.ts +2 -7
  54. package/dist/content/status-command.js +19 -146
  55. package/dist/content/subagents.d.ts +0 -5
  56. package/dist/content/subagents.js +47 -28
  57. package/dist/content/templates.d.ts +1 -1
  58. package/dist/content/templates.js +126 -135
  59. package/dist/content/track-render-context.d.ts +17 -0
  60. package/dist/content/track-render-context.js +44 -0
  61. package/dist/content/tree-command.d.ts +1 -2
  62. package/dist/content/tree-command.js +4 -87
  63. package/dist/content/utility-skills.d.ts +2 -29
  64. package/dist/content/utility-skills.js +2 -1533
  65. package/dist/content/view-command.js +29 -11
  66. package/dist/delegation.d.ts +1 -1
  67. package/dist/delegation.js +5 -15
  68. package/dist/doctor-registry.js +20 -21
  69. package/dist/doctor.js +88 -408
  70. package/dist/flow-state.d.ts +3 -0
  71. package/dist/flow-state.js +2 -0
  72. package/dist/harness-adapters.d.ts +1 -1
  73. package/dist/harness-adapters.js +48 -57
  74. package/dist/install.js +128 -520
  75. package/dist/internal/advance-stage.js +3 -9
  76. package/dist/internal/compound-readiness.d.ts +1 -1
  77. package/dist/internal/compound-readiness.js +1 -1
  78. package/dist/internal/tdd-loop-status.d.ts +1 -1
  79. package/dist/internal/tdd-loop-status.js +1 -1
  80. package/dist/knowledge-store.d.ts +16 -10
  81. package/dist/knowledge-store.js +51 -15
  82. package/dist/policy.js +16 -109
  83. package/dist/run-archive.d.ts +4 -6
  84. package/dist/run-archive.js +15 -20
  85. package/dist/run-persistence.d.ts +2 -2
  86. package/dist/run-persistence.js +3 -9
  87. package/package.json +1 -2
  88. package/dist/content/archive-command.d.ts +0 -2
  89. package/dist/content/archive-command.js +0 -124
  90. package/dist/content/compound-command.d.ts +0 -5
  91. package/dist/content/compound-command.js +0 -193
  92. package/dist/content/contexts.d.ts +0 -9
  93. package/dist/content/contexts.js +0 -65
  94. package/dist/content/contracts.d.ts +0 -2
  95. package/dist/content/contracts.js +0 -51
  96. package/dist/content/doctor-references.d.ts +0 -2
  97. package/dist/content/doctor-references.js +0 -150
  98. package/dist/content/eval-scaffold.d.ts +0 -15
  99. package/dist/content/eval-scaffold.js +0 -370
  100. package/dist/content/feature-command.d.ts +0 -2
  101. package/dist/content/feature-command.js +0 -123
  102. package/dist/content/flow-map.d.ts +0 -23
  103. package/dist/content/flow-map.js +0 -134
  104. package/dist/content/harness-doc.d.ts +0 -2
  105. package/dist/content/harness-doc.js +0 -202
  106. package/dist/content/harness-playbooks.d.ts +0 -24
  107. package/dist/content/harness-playbooks.js +0 -393
  108. package/dist/content/harness-tool-refs.d.ts +0 -20
  109. package/dist/content/harness-tool-refs.js +0 -268
  110. package/dist/content/ops-command.d.ts +0 -2
  111. package/dist/content/ops-command.js +0 -71
  112. package/dist/content/protocols.d.ts +0 -7
  113. package/dist/content/protocols.js +0 -215
  114. package/dist/content/retro-command.d.ts +0 -2
  115. package/dist/content/retro-command.js +0 -165
  116. package/dist/content/rewind-command.d.ts +0 -2
  117. package/dist/content/rewind-command.js +0 -106
  118. package/dist/content/tdd-log-command.d.ts +0 -2
  119. package/dist/content/tdd-log-command.js +0 -85
  120. package/dist/eval/agents/single-shot.d.ts +0 -27
  121. package/dist/eval/agents/single-shot.js +0 -79
  122. package/dist/eval/agents/with-tools.d.ts +0 -44
  123. package/dist/eval/agents/with-tools.js +0 -261
  124. package/dist/eval/agents/workflow.d.ts +0 -31
  125. package/dist/eval/agents/workflow.js +0 -155
  126. package/dist/eval/baseline.d.ts +0 -38
  127. package/dist/eval/baseline.js +0 -282
  128. package/dist/eval/config-loader.d.ts +0 -14
  129. package/dist/eval/config-loader.js +0 -395
  130. package/dist/eval/corpus.d.ts +0 -30
  131. package/dist/eval/corpus.js +0 -330
  132. package/dist/eval/cost-guard.d.ts +0 -102
  133. package/dist/eval/cost-guard.js +0 -190
  134. package/dist/eval/diff.d.ts +0 -64
  135. package/dist/eval/diff.js +0 -323
  136. package/dist/eval/llm-client.d.ts +0 -176
  137. package/dist/eval/llm-client.js +0 -267
  138. package/dist/eval/mode.d.ts +0 -28
  139. package/dist/eval/mode.js +0 -61
  140. package/dist/eval/progress.d.ts +0 -83
  141. package/dist/eval/progress.js +0 -59
  142. package/dist/eval/report.d.ts +0 -11
  143. package/dist/eval/report.js +0 -181
  144. package/dist/eval/rubric-loader.d.ts +0 -20
  145. package/dist/eval/rubric-loader.js +0 -143
  146. package/dist/eval/runner.d.ts +0 -81
  147. package/dist/eval/runner.js +0 -746
  148. package/dist/eval/runs.d.ts +0 -41
  149. package/dist/eval/runs.js +0 -114
  150. package/dist/eval/sandbox.d.ts +0 -38
  151. package/dist/eval/sandbox.js +0 -137
  152. package/dist/eval/tools/glob.d.ts +0 -2
  153. package/dist/eval/tools/glob.js +0 -163
  154. package/dist/eval/tools/grep.d.ts +0 -2
  155. package/dist/eval/tools/grep.js +0 -152
  156. package/dist/eval/tools/index.d.ts +0 -7
  157. package/dist/eval/tools/index.js +0 -35
  158. package/dist/eval/tools/read.d.ts +0 -2
  159. package/dist/eval/tools/read.js +0 -122
  160. package/dist/eval/tools/types.d.ts +0 -49
  161. package/dist/eval/tools/types.js +0 -41
  162. package/dist/eval/tools/write.d.ts +0 -2
  163. package/dist/eval/tools/write.js +0 -92
  164. package/dist/eval/types.d.ts +0 -561
  165. package/dist/eval/types.js +0 -47
  166. package/dist/eval/verifiers/judge.d.ts +0 -40
  167. package/dist/eval/verifiers/judge.js +0 -256
  168. package/dist/eval/verifiers/rules.d.ts +0 -24
  169. package/dist/eval/verifiers/rules.js +0 -218
  170. package/dist/eval/verifiers/structural.d.ts +0 -14
  171. package/dist/eval/verifiers/structural.js +0 -171
  172. package/dist/eval/verifiers/traceability.d.ts +0 -23
  173. package/dist/eval/verifiers/traceability.js +0 -84
  174. package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
  175. package/dist/eval/verifiers/workflow-consistency.js +0 -225
  176. package/dist/eval/workflow-corpus.d.ts +0 -7
  177. package/dist/eval/workflow-corpus.js +0 -207
  178. package/dist/feature-system.d.ts +0 -42
  179. package/dist/feature-system.js +0 -432
  180. package/dist/internal/knowledge-digest.d.ts +0 -7
  181. package/dist/internal/knowledge-digest.js +0 -93
package/dist/install.js CHANGED
@@ -4,50 +4,28 @@ import path from "node:path";
4
4
  import { promisify } from "node:util";
5
5
  import { CCLAW_VERSION, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
6
6
  import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } from "./config.js";
7
- import { stageCommandContract } from "./content/contracts.js";
8
- import { contextModeFiles, createInitialContextModeState } from "./content/contexts.js";
9
- import { learnSkillMarkdown, learnCommandContract } from "./content/learnings.js";
7
+ import { learnSkillMarkdown } from "./content/learnings.js";
10
8
  import { nextCommandContract, nextCommandSkillMarkdown } from "./content/next-command.js";
11
9
  import { ideateCommandContract, ideateCommandSkillMarkdown } from "./content/ideate-command.js";
12
10
  import { startCommandContract, startCommandSkillMarkdown } from "./content/start-command.js";
13
- import { statusCommandContract, statusCommandSkillMarkdown } from "./content/status-command.js";
14
- import { treeCommandContract, treeCommandSkillMarkdown } from "./content/tree-command.js";
15
- import { diffCommandContract, diffCommandSkillMarkdown } from "./content/diff-command.js";
16
11
  import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-command.js";
17
- import { opsCommandContract, opsCommandSkillMarkdown } from "./content/ops-command.js";
18
- import { featureCommandContract, featureCommandSkillMarkdown } from "./content/feature-command.js";
19
- import { tddLogCommandContract, tddLogCommandSkillMarkdown } from "./content/tdd-log-command.js";
20
- import { retroCommandContract, retroCommandSkillMarkdown } from "./content/retro-command.js";
21
- import { compoundCommandContract, compoundCommandSkillMarkdown } from "./content/compound-command.js";
22
- import { archiveCommandContract, archiveCommandSkillMarkdown } from "./content/archive-command.js";
23
- import { rewindCommandContract, rewindCommandSkillMarkdown } from "./content/rewind-command.js";
24
12
  import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
25
13
  import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
26
14
  import { ironLawRuntimeDocument, ironLawsSkillMarkdown } from "./content/iron-laws.js";
27
15
  import { stageCompleteScript, runHookCmdScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
28
16
  import { nodeHookRuntimeScript } from "./content/node-hooks.js";
29
17
  import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
30
- import { decisionProtocolMarkdown, completionProtocolMarkdown, ethosProtocolMarkdown } from "./content/protocols.js";
31
- import { flowMapMarkdown } from "./content/flow-map.js";
32
18
  import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
33
- import { EVAL_BASELINES_README, EVAL_CONFIG_YAML, EVAL_CORPUS_README, EVAL_REPORTS_README, EVAL_RUBRIC_FILES, EVAL_RUBRICS_README } from "./content/eval-scaffold.js";
34
- import { TDD_BATCH_WALKTHROUGH_MARKDOWN, stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
35
- import { stageCommonGuidanceMarkdown } from "./content/stage-common-guidance.js";
36
- import { STAGE_EXAMPLES_REFERENCE_DIR, stageExamplesReferenceMarkdown } from "./content/examples.js";
37
- import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LANGUAGE_RULE_PACK_GENERATORS, LEGACY_LANGUAGE_RULE_PACK_FOLDERS, UTILITY_SKILL_FOLDERS, UTILITY_SKILL_MAP } from "./content/utility-skills.js";
19
+ import { stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
20
+ import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LANGUAGE_RULE_PACK_GENERATORS, LEGACY_LANGUAGE_RULE_PACK_FOLDERS } from "./content/utility-skills.js";
38
21
  import { RESEARCH_PLAYBOOKS } from "./content/research-playbooks.js";
39
- import { HARNESS_TOOL_REFS_DIR, HARNESS_TOOL_REFS_INDEX_MD, harnessToolRefMarkdown } from "./content/harness-tool-refs.js";
40
- import { DOCTOR_REFERENCE_MARKDOWN } from "./content/doctor-references.js";
41
- import { harnessDocsOverviewMarkdown, harnessIntegrationDocMarkdown } from "./content/harness-doc.js";
42
- import { HARNESS_PLAYBOOKS_DIR, harnessPlaybookFileName, harnessPlaybookMarkdown, harnessPlaybooksIndexMarkdown } from "./content/harness-playbooks.js";
43
- import { HOOK_EVENTS_BY_HARNESS, HOOK_SEMANTIC_EVENTS } from "./content/hook-events.js";
44
22
  import { createInitialFlowState } from "./flow-state.js";
45
23
  import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
46
24
  import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
47
- import { HARNESS_ADAPTERS, harnessShimFileNames, harnessTier, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
25
+ import { HARNESS_ADAPTERS, harnessShimFileNames, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
48
26
  import { validateHookDocument } from "./hook-schema.js";
49
27
  import { detectHarnesses } from "./init-detect.js";
50
- import { CorruptFlowStateError, ensureRunSystem, readFlowState } from "./runs.js";
28
+ import { ensureRunSystem } from "./runs.js";
51
29
  import { FLOW_STAGES } from "./types.js";
52
30
  const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
53
31
  const CURSOR_RULE_REL_PATH = ".cursor/rules/cclaw-workflow.mdc";
@@ -57,6 +35,108 @@ const execFileAsync = promisify(execFile);
57
35
  function runtimePath(projectRoot, ...segments) {
58
36
  return path.join(projectRoot, RUNTIME_ROOT, ...segments);
59
37
  }
38
+ async function removeBestEffort(targetPath, recursive = false) {
39
+ try {
40
+ await fs.rm(targetPath, { recursive, force: true });
41
+ }
42
+ catch {
43
+ // best-effort cleanup
44
+ }
45
+ }
46
+ const DEPRECATED_UTILITY_SKILL_FOLDERS = [
47
+ "project-learnings",
48
+ "auto-orchestration",
49
+ "autoplan",
50
+ "red-first-testing",
51
+ "incremental-implementation",
52
+ "subagent-driven-development",
53
+ "dispatching-parallel-agents",
54
+ "session-guidelines",
55
+ "security-review",
56
+ "documentation",
57
+ "browser-qa-testing",
58
+ "feature-workspaces",
59
+ "security",
60
+ "debugging",
61
+ "performance",
62
+ "ci-cd",
63
+ "docs",
64
+ "executing-plans",
65
+ "verification-before-completion",
66
+ "finishing-a-development-branch",
67
+ "context-engineering",
68
+ "source-driven-development",
69
+ "frontend-accessibility",
70
+ "landscape-check",
71
+ "adversarial-review",
72
+ "security-audit",
73
+ "knowledge-curation",
74
+ "retrospective",
75
+ "document-review",
76
+ "receiving-code-review",
77
+ "flow-status",
78
+ "flow-tree",
79
+ "flow-diff"
80
+ ];
81
+ const DEPRECATED_AGENT_FILES = [
82
+ "securityer.md",
83
+ "spec-reviewer.md",
84
+ "code-reviewer.md",
85
+ "repo-research-analyst.md",
86
+ "learnings-researcher.md",
87
+ "framework-docs-researcher.md",
88
+ "best-practices-researcher.md",
89
+ "git-history-analyzer.md"
90
+ ];
91
+ const DEPRECATED_COMMAND_FILES = [
92
+ "learn.md",
93
+ "status.md",
94
+ "tree.md",
95
+ "diff.md",
96
+ "feature.md",
97
+ "ops.md",
98
+ "tdd-log.md",
99
+ "retro.md",
100
+ "compound.md",
101
+ "archive.md",
102
+ "rewind.md"
103
+ ];
104
+ const DEPRECATED_SKILL_FILES = [
105
+ ["flow-ops", "SKILL.md"],
106
+ ["tdd-cycle-log", "SKILL.md"],
107
+ ["flow-retro", "SKILL.md"],
108
+ ["flow-compound", "SKILL.md"],
109
+ ["flow-archive", "SKILL.md"],
110
+ ["flow-rewind", "SKILL.md"],
111
+ ["using-git-worktrees", "SKILL.md"]
112
+ ];
113
+ const DEPRECATED_STATE_FILES = [
114
+ "checkpoint.json",
115
+ "flow-state.snapshot.json",
116
+ "stage-activity.jsonl",
117
+ "knowledge-digest.md",
118
+ "suggestion-memory.json",
119
+ "harness-gaps.json",
120
+ "context-mode.json",
121
+ "session-digest.md",
122
+ "context-warnings.jsonl"
123
+ ];
124
+ const DEPRECATED_HOOK_FILES = [
125
+ "observe.sh",
126
+ "summarize-observations.sh",
127
+ "summarize-observations.mjs",
128
+ "_lib.sh",
129
+ "session-start.sh",
130
+ "stop-checkpoint.sh",
131
+ "run-hook.cmd",
132
+ "stage-complete.sh",
133
+ "pre-compact.sh",
134
+ "prompt-guard.sh",
135
+ "workflow-guard.sh",
136
+ "context-monitor.sh"
137
+ ];
138
+ const DEPRECATED_RUNTIME_ROOT_FILES = ["learnings.jsonl", "observations.jsonl"];
139
+ const DEPRECATED_RUNTIME_DIRS = ["evals", "worktrees", "references", "contexts"];
60
140
  async function resolveGitHooksDir(projectRoot) {
61
141
  try {
62
142
  const { stdout } = await execFileAsync("git", ["rev-parse", "--git-path", "hooks"], {
@@ -274,93 +354,28 @@ async function ensureStructure(projectRoot) {
274
354
  await ensureDir(path.join(projectRoot, dir));
275
355
  }
276
356
  }
277
- async function writeCommandContracts(projectRoot, track = "standard") {
278
- await Promise.all(FLOW_STAGES.map(async (stage) => {
279
- await writeFileSafe(runtimePath(projectRoot, "commands", `${stage}.md`), stageCommandContract(stage, track));
280
- }));
281
- }
282
357
  async function writeArtifactTemplates(projectRoot) {
283
358
  await Promise.all(Object.entries(ARTIFACT_TEMPLATES).map(async ([fileName, content]) => {
284
359
  await writeFileSafe(runtimePath(projectRoot, "templates", fileName), content);
285
360
  }));
286
361
  }
287
- /**
288
- * Seed the `.cclaw/evals/` scaffold. Only writes files that do not already
289
- * exist so that user-authored config.yaml / corpus / rubrics / baselines are
290
- * never clobbered by `cclaw sync`.
291
- */
292
- async function writeEvalScaffold(projectRoot) {
293
- const targets = [
294
- { rel: "evals/config.yaml", content: EVAL_CONFIG_YAML },
295
- { rel: "evals/corpus/README.md", content: EVAL_CORPUS_README },
296
- { rel: "evals/rubrics/README.md", content: EVAL_RUBRICS_README },
297
- { rel: "evals/baselines/README.md", content: EVAL_BASELINES_README },
298
- { rel: "evals/reports/README.md", content: EVAL_REPORTS_README }
299
- ];
300
- for (const rubric of EVAL_RUBRIC_FILES) {
301
- targets.push({
302
- rel: `evals/rubrics/${rubric.stage}.yaml`,
303
- content: rubric.contents
304
- });
305
- }
306
- for (const target of targets) {
307
- const absolute = runtimePath(projectRoot, ...target.rel.split("/"));
308
- if (await exists(absolute))
309
- continue;
310
- await writeFileSafe(absolute, target.content);
311
- }
312
- }
313
362
  async function writeSkills(projectRoot, config) {
314
363
  const skillTrack = config?.defaultTrack ?? "standard";
315
364
  for (const stage of FLOW_STAGES) {
316
365
  const folder = stageSkillFolder(stage);
317
366
  await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), stageSkillMarkdown(stage, skillTrack));
318
- // Progressive disclosure (A.2#8): materialize the full example artifact as
319
- // a sibling reference file. The stage skill only links to it; agents load
320
- // the reference on demand.
321
- const referenceMarkdown = stageExamplesReferenceMarkdown(stage);
322
- if (referenceMarkdown) {
323
- const referenceDir = STAGE_EXAMPLES_REFERENCE_DIR.split("/");
324
- await writeFileSafe(runtimePath(projectRoot, ...referenceDir, `${stage}-examples.md`), referenceMarkdown);
325
- }
326
367
  }
327
- // Progressive disclosure for the TDD Batch Execution walkthrough (A.1#1).
328
- // The detailed 3-task transcript lives next to stage examples so the
329
- // always-rendered TDD skill stays under the line-budget and the reference
330
- // is loaded on demand.
331
- await writeFileSafe(runtimePath(projectRoot, ...STAGE_EXAMPLES_REFERENCE_DIR.split("/"), "tdd-batch-walkthrough.md"), TDD_BATCH_WALKTHROUGH_MARKDOWN);
332
- await writeFileSafe(runtimePath(projectRoot, ...STAGE_EXAMPLES_REFERENCE_DIR.split("/"), "common-guidance.md"), stageCommonGuidanceMarkdown());
333
368
  // Utility skills (not flow stages)
334
369
  await writeFileSafe(runtimePath(projectRoot, "skills", "learnings", "SKILL.md"), learnSkillMarkdown());
335
370
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-next-step", "SKILL.md"), nextCommandSkillMarkdown());
336
371
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-ideate", "SKILL.md"), ideateCommandSkillMarkdown());
337
372
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-start", "SKILL.md"), startCommandSkillMarkdown());
338
373
  await writeFileSafe(runtimePath(projectRoot, "skills", "flow-view", "SKILL.md"), viewCommandSkillMarkdown());
339
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-status", "SKILL.md"), statusCommandSkillMarkdown());
340
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-tree", "SKILL.md"), treeCommandSkillMarkdown());
341
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-diff", "SKILL.md"), diffCommandSkillMarkdown());
342
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-ops", "SKILL.md"), opsCommandSkillMarkdown());
343
- await writeFileSafe(runtimePath(projectRoot, "skills", "using-git-worktrees", "SKILL.md"), featureCommandSkillMarkdown());
344
- await writeFileSafe(runtimePath(projectRoot, "skills", "tdd-cycle-log", "SKILL.md"), tddLogCommandSkillMarkdown());
345
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-retro", "SKILL.md"), retroCommandSkillMarkdown());
346
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-compound", "SKILL.md"), compoundCommandSkillMarkdown({
347
- recurrenceThreshold: config?.compound?.recurrenceThreshold
348
- }));
349
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-rewind", "SKILL.md"), rewindCommandSkillMarkdown());
350
- await writeFileSafe(runtimePath(projectRoot, "skills", "flow-archive", "SKILL.md"), archiveCommandSkillMarkdown());
351
374
  await writeFileSafe(runtimePath(projectRoot, "skills", "subagent-dev", "SKILL.md"), subagentDrivenDevSkill());
352
375
  await writeFileSafe(runtimePath(projectRoot, "skills", "parallel-dispatch", "SKILL.md"), parallelAgentsSkill());
353
376
  await writeFileSafe(runtimePath(projectRoot, "skills", "session", "SKILL.md"), sessionHooksSkillMarkdown());
354
377
  await writeFileSafe(runtimePath(projectRoot, "skills", "iron-laws", "SKILL.md"), ironLawsSkillMarkdown());
355
378
  await writeFileSafe(runtimePath(projectRoot, "skills", META_SKILL_NAME, "SKILL.md"), usingCclawSkillMarkdown());
356
- await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "decision.md"), decisionProtocolMarkdown());
357
- await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "completion.md"), completionProtocolMarkdown());
358
- await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "ethos.md"), ethosProtocolMarkdown());
359
- await writeFileSafe(runtimePath(projectRoot, "references", "flow-map.md"), flowMapMarkdown());
360
- for (const folder of UTILITY_SKILL_FOLDERS) {
361
- const generator = UTILITY_SKILL_MAP[folder];
362
- await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
363
- }
364
379
  // In-thread research procedures (no YAML frontmatter, not delegated personas).
365
380
  for (const [fileName, markdown] of Object.entries(RESEARCH_PLAYBOOKS)) {
366
381
  await writeFileSafe(runtimePath(projectRoot, "skills", "research", fileName), markdown);
@@ -383,49 +398,12 @@ async function writeSkills(projectRoot, config) {
383
398
  await fs.rm(legacyPath, { recursive: true, force: true });
384
399
  }
385
400
  }
386
- // Per-harness tool maps (A.1#4). One reference file per supported harness
387
- // plus an index; stage/utility skills cite these instead of hardcoding
388
- // tool names inline.
389
- const harnessIds = ["claude", "cursor", "opencode", "codex"];
390
- const harnessRefsDir = HARNESS_TOOL_REFS_DIR.split("/");
391
- await writeFileSafe(runtimePath(projectRoot, ...harnessRefsDir, "README.md"), HARNESS_TOOL_REFS_INDEX_MD);
392
- for (const harness of harnessIds) {
393
- await writeFileSafe(runtimePath(projectRoot, ...harnessRefsDir, `${harness}.md`), harnessToolRefMarkdown(harness));
394
- }
395
- const doctorRefsDir = ["references", "doctor"];
396
- for (const [fileName, markdown] of Object.entries(DOCTOR_REFERENCE_MARKDOWN)) {
397
- await writeFileSafe(runtimePath(projectRoot, ...doctorRefsDir, fileName), markdown);
398
- }
399
- await writeFileSafe(runtimePath(projectRoot, "references", "harnesses.md"), harnessIntegrationDocMarkdown());
400
- await writeFileSafe(runtimePath(projectRoot, "references", "harnesses-overview.md"), harnessDocsOverviewMarkdown());
401
- // Per-harness parity playbooks. Generated for every supported harness
402
- // regardless of which harnesses the project installed — the index always
403
- // resolves, and doctor only asserts presence of the installed harnesses'
404
- // playbooks (see runtime-integrity checks).
405
- const playbookDirSegments = HARNESS_PLAYBOOKS_DIR.split("/");
406
- await writeFileSafe(runtimePath(projectRoot, ...playbookDirSegments, "README.md"), harnessPlaybooksIndexMarkdown());
407
- for (const harness of harnessIds) {
408
- await writeFileSafe(runtimePath(projectRoot, ...playbookDirSegments, harnessPlaybookFileName(harness)), harnessPlaybookMarkdown(harness));
409
- }
410
401
  }
411
- async function writeUtilityCommands(projectRoot, config) {
412
- await writeFileSafe(runtimePath(projectRoot, "commands", "learn.md"), learnCommandContract());
402
+ async function writeEntryCommands(projectRoot) {
403
+ await writeFileSafe(runtimePath(projectRoot, "commands", "start.md"), startCommandContract());
413
404
  await writeFileSafe(runtimePath(projectRoot, "commands", "next.md"), nextCommandContract());
414
405
  await writeFileSafe(runtimePath(projectRoot, "commands", "ideate.md"), ideateCommandContract());
415
406
  await writeFileSafe(runtimePath(projectRoot, "commands", "view.md"), viewCommandContract());
416
- await writeFileSafe(runtimePath(projectRoot, "commands", "start.md"), startCommandContract());
417
- await writeFileSafe(runtimePath(projectRoot, "commands", "status.md"), statusCommandContract());
418
- await writeFileSafe(runtimePath(projectRoot, "commands", "tree.md"), treeCommandContract());
419
- await writeFileSafe(runtimePath(projectRoot, "commands", "diff.md"), diffCommandContract());
420
- await writeFileSafe(runtimePath(projectRoot, "commands", "ops.md"), opsCommandContract());
421
- await writeFileSafe(runtimePath(projectRoot, "commands", "feature.md"), featureCommandContract());
422
- await writeFileSafe(runtimePath(projectRoot, "commands", "tdd-log.md"), tddLogCommandContract());
423
- await writeFileSafe(runtimePath(projectRoot, "commands", "retro.md"), retroCommandContract());
424
- await writeFileSafe(runtimePath(projectRoot, "commands", "compound.md"), compoundCommandContract({
425
- recurrenceThreshold: config.compound?.recurrenceThreshold
426
- }));
427
- await writeFileSafe(runtimePath(projectRoot, "commands", "archive.md"), archiveCommandContract());
428
- await writeFileSafe(runtimePath(projectRoot, "commands", "rewind.md"), rewindCommandContract());
429
407
  }
430
408
  function toObject(value) {
431
409
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -799,232 +777,10 @@ async function ensureKnowledgeStore(projectRoot) {
799
777
  await fs.rm(legacyMdPath, { force: true });
800
778
  }
801
779
  }
802
- async function ensureCustomSkillsScaffold(projectRoot) {
803
- const customDir = runtimePath(projectRoot, "custom-skills");
804
- await ensureDir(customDir);
805
- const readmePath = path.join(customDir, "README.md");
806
- if (!(await exists(readmePath))) {
807
- await writeFileSafe(readmePath, CUSTOM_SKILLS_README);
808
- }
809
- const examplePath = path.join(customDir, "example", "SKILL.md");
810
- if (!(await exists(examplePath))) {
811
- await writeFileSafe(examplePath, CUSTOM_SKILLS_EXAMPLE);
812
- }
813
- }
814
- const CUSTOM_SKILLS_README = `# Custom Skills (sync-safe)
815
-
816
- This directory is **never overwritten** by \`cclaw sync\` or \`cclaw upgrade\`. Use it
817
- to add project-specific skills that complement the managed skills under
818
- \`.cclaw/skills/\`.
819
-
820
- ## When to add a custom skill
821
-
822
- - A repeatable lens specific to **this project** (e.g. "billing-domain", "kafka-message-contracts").
823
- - A team convention you want every agent session to load.
824
- - A domain checklist that does not generalize to other projects.
825
-
826
- If the skill is general (security, performance, accessibility, etc.) prefer
827
- contributing it upstream instead — the managed skills receive maintenance.
828
-
829
- ## File format — public API (stable contract)
830
-
831
- Each skill lives at \`.cclaw/custom-skills/<folder>/SKILL.md\`. The format is a
832
- **stable public API**: \`cclaw sync\` and \`cclaw upgrade\` will not rewrite
833
- custom skills, and the fields below are guaranteed to be respected by the
834
- meta-skill router and the stage hooks.
835
-
836
- ### Frontmatter (YAML, required)
837
-
838
- \`\`\`yaml
839
- ---
840
- # Required fields
841
- name: <kebab-case-skill-name>
842
- description: >
843
- One sentence (≤180 chars) that triggers semantic routing. Include the
844
- concrete situation and the expected action
845
- (e.g. "Audit Kafka topic contracts when a producer or consumer signature changes").
846
-
847
- # Optional fields (omit when not applicable)
848
- stages: [design, spec, tdd, review] # flow stages this skill applies to
849
- triggers:
850
- - "kafka topic"
851
- - "producer.schema"
852
- - "consumer.schema"
853
- hardGate: false # true => skill body MUST include a ## HARD-GATE section
854
- owners: ["@team-messaging"] # informational routing hint, not enforced
855
- version: 0.1.0 # semver; bump when hardGate or algorithm changes
856
- ---
857
- \`\`\`
858
-
859
- ### Field contract
860
-
861
- | Field | Type | Required | Meaning |
862
- |---|---|---|---|
863
- | \`name\` | string (kebab-case) | yes | Unique id used by the router and by \`/cc-view status\` diagnostics. |
864
- | \`description\` | string ≤180 chars (single line OR YAML \`>\` folded) | yes | Drives semantic routing. Include trigger + action. |
865
- | \`stages\` | array of flow stages | no | When present, the meta-skill only surfaces this skill during those stages. Omit for "any stage". |
866
- | \`triggers\` | array of strings | no | Extra literal substrings that route to this skill when found in the user prompt or the active artifact. |
867
- | \`hardGate\` | boolean | no | When \`true\`, the body MUST include a \`## HARD-GATE\` section; the agent treats the rule as non-skippable. |
868
- | \`owners\` | array of strings | no | Informational only — surfaced to the user, never enforced. |
869
- | \`version\` | semver string | no | Bump when you change the HARD-GATE or algorithm so reviewers can spot changes. |
870
-
871
- ### Body sections (markdown, recommended order)
872
-
873
- \`\`\`markdown
874
- # <Skill title>
875
-
876
- ## Overview
877
- One-paragraph summary; context for when this skill is loaded.
878
-
879
- ## When to use
880
- - Bullet list of situations where this skill adds value.
881
-
882
- ## When NOT to use
883
- - Situations where loading this skill is context bloat or wrong.
884
-
885
- ## HARD-GATE (REQUIRED when frontmatter hardGate: true)
886
- Phrase it as a refusal:
887
- > Do not <X> while <Y>.
888
-
889
- ## Algorithm / checklist
890
- 1. Concrete, observable steps with evidence (file:line, artifact, or knowledge entry).
891
-
892
- ## Output protocol
893
- Where the artifact / chat output lives and what shape it takes.
894
-
895
- ## Anti-patterns
896
- - Common failure modes to reject.
897
- \`\`\`
898
-
899
- ### Stage association semantics
900
-
901
- - \`stages: []\` or missing → skill is available at any stage. The meta-skill still only surfaces it when \`description\` or \`triggers\` match the prompt.
902
- - \`stages: [review]\` → skill is offered only during the review stage.
903
- - Custom skills **never** become mandatory delegations. They are opt-in lenses. If you need a mandatory dispatch, add a proper managed specialist under \`.cclaw/skills/\` instead.
904
-
905
- ## Routing
906
-
907
- Custom skills are surfaced via the \`using-cclaw\` meta-skill at session start.
908
- Mention the skill name in your prompt or let the agent semantic-route to it
909
- based on the description + triggers + stages frontmatter.
910
-
911
- ## Versioning & removal
912
-
913
- Custom skills are user-owned. Bump \`version\` when you change the HARD-GATE or
914
- algorithm; delete or edit them at any time — \`cclaw sync\` will not touch them.
915
- `;
916
- const CUSTOM_SKILLS_EXAMPLE = `---
917
- name: example-custom-skill
918
- description: "Replace this with a one-sentence description that triggers when the skill should be used. Delete or rename this folder when you add a real skill."
919
- ---
920
-
921
- # Example Custom Skill
922
-
923
- This is a placeholder. Use it as a starting template, then delete or rename
924
- the \`example/\` folder.
925
-
926
- ## When to use
927
-
928
- - A real, repeatable situation in **this** project that needs a consistent lens.
929
-
930
- ## HARD-GATE (optional)
931
-
932
- Drop this section if no hard rule applies. Keep it crisp:
933
-
934
- > Do not <X> while <Y>.
935
-
936
- ## Algorithm
937
-
938
- 1. Step one — observable, file:line evidence required.
939
- 2. Step two — produce a named artifact, not a vibe.
940
- 3. Step three — escalate / hand off / record knowledge entry.
941
-
942
- ## Anti-patterns
943
-
944
- - Treating this skill as advisory when the situation matches the trigger.
945
- - Loading this skill when the situation clearly does not match (context bloat).
946
- `;
947
- async function ensureSessionStateFiles(projectRoot) {
948
- const stateDir = runtimePath(projectRoot, "state");
949
- await ensureDir(stateDir);
950
- // If flow-state.json is corrupt, `readFlowState` quarantines the bad
951
- // file and throws. During install we'd rather continue than abort:
952
- // the user just asked to set up cclaw, and the corrupt file is already
953
- // preserved next to the original path. Fall back to a fresh initial
954
- // state so the rest of install completes and the user can inspect the
955
- // `.corrupt-<timestamp>.json` quarantine afterwards.
956
- let flow;
957
- try {
958
- flow = await readFlowState(projectRoot);
959
- }
960
- catch (err) {
961
- if (err instanceof CorruptFlowStateError) {
962
- flow = createInitialFlowState();
963
- }
964
- else {
965
- throw err;
966
- }
967
- }
968
- const activityPath = path.join(stateDir, "stage-activity.jsonl");
969
- if (!(await exists(activityPath))) {
970
- await writeFileSafe(activityPath, "", { mode: 0o600 });
971
- }
972
- const checkpointPath = path.join(stateDir, "checkpoint.json");
973
- if (!(await exists(checkpointPath))) {
974
- const initialCheckpoint = {
975
- stage: flow.currentStage,
976
- runId: flow.activeRunId,
977
- status: "not_started",
978
- lastCompletedStep: "",
979
- remainingSteps: [],
980
- blockers: [],
981
- timestamp: new Date().toISOString()
982
- };
983
- await writeFileSafe(checkpointPath, `${JSON.stringify(initialCheckpoint, null, 2)}\n`, { mode: 0o600 });
984
- }
985
- const suggestionMemoryPath = path.join(stateDir, "suggestion-memory.json");
986
- if (!(await exists(suggestionMemoryPath))) {
987
- const suggestionMemory = {
988
- enabled: true,
989
- mutedStages: [],
990
- lastSuggestedStage: "",
991
- lastSuggestedAt: ""
992
- };
993
- await writeFileSafe(suggestionMemoryPath, `${JSON.stringify(suggestionMemory, null, 2)}\n`, { mode: 0o600 });
994
- }
995
- const contextModePath = path.join(stateDir, "context-mode.json");
996
- if (!(await exists(contextModePath))) {
997
- await writeFileSafe(contextModePath, `${JSON.stringify(createInitialContextModeState(), null, 2)}\n`, { mode: 0o600 });
998
- }
999
- const knowledgeDigestPath = path.join(stateDir, "knowledge-digest.md");
1000
- if (!(await exists(knowledgeDigestPath))) {
1001
- await writeFileSafe(knowledgeDigestPath, "# Knowledge digest (auto-generated)\n\n(no entries yet)\n");
1002
- }
1003
- const tddCycleLogPath = path.join(stateDir, "tdd-cycle-log.jsonl");
1004
- if (!(await exists(tddCycleLogPath))) {
1005
- await writeFileSafe(tddCycleLogPath, "", { mode: 0o600 });
1006
- }
1007
- const reconciliationNoticesPath = path.join(stateDir, "reconciliation-notices.json");
1008
- if (!(await exists(reconciliationNoticesPath))) {
1009
- await writeFileSafe(reconciliationNoticesPath, `${JSON.stringify({ schemaVersion: 1, notices: [] }, null, 2)}\n`, { mode: 0o600 });
1010
- }
1011
- const flowSnapshotPath = path.join(stateDir, "flow-state.snapshot.json");
1012
- if (!(await exists(flowSnapshotPath))) {
1013
- await writeFileSafe(flowSnapshotPath, `${JSON.stringify({
1014
- capturedAt: new Date().toISOString(),
1015
- state: flow
1016
- }, null, 2)}\n`, { mode: 0o600 });
1017
- }
1018
- }
1019
780
  async function writeRulebook(projectRoot) {
1020
781
  await writeFileSafe(runtimePath(projectRoot, "rules", "RULES.md"), RULEBOOK_MARKDOWN);
1021
782
  await writeFileSafe(runtimePath(projectRoot, "rules", "rules.json"), `${JSON.stringify(buildRulesJson(), null, 2)}\n`);
1022
783
  }
1023
- async function writeContextModes(projectRoot) {
1024
- for (const [mode, content] of Object.entries(contextModeFiles())) {
1025
- await writeFileSafe(runtimePath(projectRoot, "contexts", `${mode}.md`), content);
1026
- }
1027
- }
1028
784
  async function writeCursorWorkflowRule(projectRoot, harnesses) {
1029
785
  const rulePath = path.join(projectRoot, CURSOR_RULE_REL_PATH);
1030
786
  if (!harnesses.includes("cursor")) {
@@ -1073,175 +829,34 @@ async function writeState(projectRoot, config, forceReset = false) {
1073
829
  const state = createInitialFlowState({ track: config.defaultTrack ?? "standard" });
1074
830
  await writeFileSafe(statePath, `${JSON.stringify(state, null, 2)}\n`, { mode: 0o600 });
1075
831
  }
1076
- async function writeAdapterManifest(projectRoot, harnesses) {
1077
- const manifest = {
1078
- generatedAt: new Date().toISOString(),
1079
- harnesses,
1080
- commandSource: `${RUNTIME_ROOT}/commands/*.md`,
1081
- skillSource: `${RUNTIME_ROOT}/skills/*/SKILL.md`
1082
- };
1083
- await writeFileSafe(runtimePath(projectRoot, "adapters", "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
1084
- }
1085
- async function writeHarnessGapsState(projectRoot, harnesses) {
1086
- const report = harnesses.map((harness) => {
1087
- const capabilities = HARNESS_ADAPTERS[harness].capabilities;
1088
- const hookMap = HOOK_EVENTS_BY_HARNESS[harness];
1089
- const missingHookEvents = HOOK_SEMANTIC_EVENTS.filter((eventName) => !hookMap[eventName]);
1090
- const missingCapabilities = [];
1091
- if (capabilities.nativeSubagentDispatch !== "full") {
1092
- missingCapabilities.push(`nativeSubagentDispatch:${capabilities.nativeSubagentDispatch}`);
1093
- }
1094
- if (capabilities.hookSurface !== "full") {
1095
- missingCapabilities.push(`hookSurface:${capabilities.hookSurface}`);
1096
- }
1097
- if (capabilities.structuredAsk === "plain-text") {
1098
- missingCapabilities.push("structuredAsk:none");
1099
- }
1100
- const remediation = [];
1101
- switch (capabilities.subagentFallback) {
1102
- case "native":
1103
- // nothing to remediate — harness has first-class dispatch
1104
- break;
1105
- case "generic-dispatch":
1106
- remediation.push(`subagent dispatch → map named cclaw agents onto generic Task subagent_type per ${HARNESS_PLAYBOOKS_DIR}/${harness}-playbook.md`);
1107
- break;
1108
- case "role-switch":
1109
- remediation.push(`subagent dispatch → role-switch in-session with evidenceRefs per ${HARNESS_PLAYBOOKS_DIR}/${harness}-playbook.md`);
1110
- break;
1111
- case "waiver":
1112
- remediation.push(`subagent dispatch → record explicit harness_limitation waiver; no parity path available`);
1113
- break;
1114
- }
1115
- // Per-harness structuredAsk remediation: record either the fallback
1116
- // requirement (plain-text) or the gating / experimental status of the
1117
- // native primitive so `cclaw doctor` and harness-gaps.json stay
1118
- // honest about *why* a primitive might silently not fire.
1119
- switch (capabilities.structuredAsk) {
1120
- case "plain-text":
1121
- remediation.push("structured ask → fall back to a numbered plain-text list; first option is default");
1122
- break;
1123
- case "question":
1124
- remediation.push(`structured ask → OpenCode \`question\` tool; enable with \`permission.question: "allow"\` in \`opencode.json\` (ACP clients additionally need \`OPENCODE_ENABLE_QUESTION_TOOL=1\`). Fallback: shared plain-text lettered list.`);
1125
- break;
1126
- case "request_user_input":
1127
- remediation.push("structured ask → Codex `request_user_input` tool (experimental; surfaced in Plan / Collaboration mode). Fallback: shared plain-text lettered list when the tool is hidden.");
1128
- break;
1129
- case "AskUserQuestion":
1130
- case "AskQuestion":
1131
- // Native first-class ask — no remediation required.
1132
- break;
1133
- }
1134
- for (const event of missingHookEvents) {
1135
- if (harness === "codex" && event === "precompact_digest") {
1136
- // Codex CLI has no PreCompact event. Generic "schedule the script
1137
- // manually" copy doesn't help; instead, point the agent at the
1138
- // in-thread substitute that already exists in cclaw content
1139
- // (`/cc-ops retro` reads the same digest the hook would emit).
1140
- remediation.push("hook event precompact_digest → Codex has no PreCompact event; run `/cc-ops retro` in-thread before compaction instead of relying on a hook");
1141
- continue;
1142
- }
1143
- remediation.push(`hook event ${event} → schedule the corresponding script manually or accept reduced observability`);
1144
- }
1145
- return {
1146
- harness,
1147
- tier: harnessTier(harness),
1148
- subagentFallback: capabilities.subagentFallback,
1149
- playbookPath: `${RUNTIME_ROOT}/${HARNESS_PLAYBOOKS_DIR}/${harness}-playbook.md`,
1150
- missingCapabilities,
1151
- missingHookEvents,
1152
- remediation
1153
- };
1154
- });
1155
- await writeFileSafe(runtimePath(projectRoot, "state", "harness-gaps.json"), `${JSON.stringify({
1156
- generatedAt: new Date().toISOString(),
1157
- schemaVersion: 2,
1158
- harnesses: report
1159
- }, null, 2)}\n`);
1160
- }
1161
832
  async function cleanLegacyArtifacts(projectRoot) {
1162
- // Remove deprecated utility skill folders from older releases.
1163
- for (const legacyFolder of [
1164
- "project-learnings",
1165
- "auto-orchestration",
1166
- "autoplan",
1167
- "red-first-testing",
1168
- "incremental-implementation",
1169
- "subagent-driven-development",
1170
- "dispatching-parallel-agents",
1171
- "session-guidelines",
1172
- "security-review",
1173
- "documentation",
1174
- "browser-qa-testing",
1175
- "feature-workspaces"
1176
- ]) {
1177
- try {
1178
- await fs.rm(runtimePath(projectRoot, "skills", legacyFolder), {
1179
- recursive: true,
1180
- force: true
1181
- });
1182
- }
1183
- catch {
1184
- // best-effort cleanup
1185
- }
833
+ for (const legacyFolder of DEPRECATED_UTILITY_SKILL_FOLDERS) {
834
+ await removeBestEffort(runtimePath(projectRoot, "skills", legacyFolder), true);
1186
835
  }
1187
- // Remove legacy duplicate security agent file when present.
1188
- try {
1189
- await fs.rm(runtimePath(projectRoot, "agents", "securityer.md"), { force: true });
1190
- }
1191
- catch {
1192
- // best-effort cleanup
1193
- }
1194
- // Core-5 migration: remove deprecated generated agent personas.
1195
- for (const legacyAgentFile of [
1196
- "spec-reviewer.md",
1197
- "code-reviewer.md",
1198
- "repo-research-analyst.md",
1199
- "learnings-researcher.md",
1200
- "framework-docs-researcher.md",
1201
- "best-practices-researcher.md",
1202
- "git-history-analyzer.md"
1203
- ]) {
1204
- try {
1205
- await fs.rm(runtimePath(projectRoot, "agents", legacyAgentFile), { force: true });
1206
- }
1207
- catch {
1208
- // best-effort cleanup
1209
- }
836
+ for (const legacyAgentFile of DEPRECATED_AGENT_FILES) {
837
+ await removeBestEffort(runtimePath(projectRoot, "agents", legacyAgentFile));
1210
838
  }
1211
839
  for (const legacyPlugin of [
1212
840
  path.join(projectRoot, ".opencode/plugins/viby-plugin.mjs"),
1213
841
  path.join(projectRoot, ".opencode/plugins/opencode-plugin.mjs"),
1214
842
  path.join(projectRoot, OPENCODE_PLUGIN_REL_PATH)
1215
843
  ]) {
1216
- try {
1217
- await fs.rm(legacyPlugin, { force: true });
1218
- }
1219
- catch {
1220
- // best-effort cleanup
1221
- }
844
+ await removeBestEffort(legacyPlugin);
1222
845
  }
1223
846
  for (const legacyRuntimeFile of [
1224
- runtimePath(projectRoot, "learnings.jsonl"),
1225
- runtimePath(projectRoot, "observations.jsonl"),
1226
- runtimePath(projectRoot, "hooks", "observe.sh"),
1227
- runtimePath(projectRoot, "hooks", "summarize-observations.sh"),
1228
- runtimePath(projectRoot, "hooks", "summarize-observations.mjs"),
1229
- runtimePath(projectRoot, "hooks", "_lib.sh"),
1230
- runtimePath(projectRoot, "hooks", "session-start.sh"),
1231
- runtimePath(projectRoot, "hooks", "stop-checkpoint.sh"),
1232
- runtimePath(projectRoot, "hooks", "run-hook.cmd"),
1233
- runtimePath(projectRoot, "hooks", "stage-complete.sh"),
1234
- runtimePath(projectRoot, "hooks", "pre-compact.sh"),
1235
- runtimePath(projectRoot, "hooks", "prompt-guard.sh"),
1236
- runtimePath(projectRoot, "hooks", "workflow-guard.sh"),
1237
- runtimePath(projectRoot, "hooks", "context-monitor.sh")
847
+ ...FLOW_STAGES.map((stage) => runtimePath(projectRoot, "commands", `${stage}.md`)),
848
+ ...DEPRECATED_COMMAND_FILES.map((file) => runtimePath(projectRoot, "commands", file)),
849
+ ...DEPRECATED_SKILL_FILES.map((segments) => runtimePath(projectRoot, "skills", ...segments)),
850
+ ...DEPRECATED_STATE_FILES.map((file) => runtimePath(projectRoot, "state", file)),
851
+ ...DEPRECATED_RUNTIME_ROOT_FILES.map((file) => runtimePath(projectRoot, file)),
852
+ ...DEPRECATED_HOOK_FILES.map((file) => runtimePath(projectRoot, "hooks", file))
1238
853
  ]) {
1239
- try {
1240
- await fs.rm(legacyRuntimeFile, { force: true });
1241
- }
1242
- catch {
1243
- // best-effort cleanup
1244
- }
854
+ await removeBestEffort(legacyRuntimeFile);
855
+ }
856
+ // Runtime simplification cleanup: these folders were generated in older
857
+ // releases and are now intentionally removed from user projects.
858
+ for (const legacyRuntimeDir of DEPRECATED_RUNTIME_DIRS) {
859
+ await removeBestEffort(runtimePath(projectRoot, legacyRuntimeDir), true);
1245
860
  }
1246
861
  // D-4 terminology migration: rename historical ideation artifacts to the
1247
862
  // canonical ideate-* naming without deleting user-authored content.
@@ -1301,21 +916,14 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
1301
916
  await cleanLegacyArtifacts(projectRoot);
1302
917
  await cleanStaleFiles(projectRoot);
1303
918
  await Promise.all([
1304
- writeCommandContracts(projectRoot, config.defaultTrack ?? "standard"),
1305
- writeUtilityCommands(projectRoot, config),
919
+ writeEntryCommands(projectRoot),
1306
920
  writeSkills(projectRoot, config),
1307
- writeContextModes(projectRoot),
1308
921
  writeArtifactTemplates(projectRoot),
1309
- writeEvalScaffold(projectRoot),
1310
922
  writeRulebook(projectRoot)
1311
923
  ]);
1312
924
  await writeState(projectRoot, config, forceStateReset);
1313
925
  await ensureRunSystem(projectRoot, { createIfMissing: false });
1314
- await ensureSessionStateFiles(projectRoot);
1315
- await writeAdapterManifest(projectRoot, harnesses);
1316
- await writeHarnessGapsState(projectRoot, harnesses);
1317
926
  await ensureKnowledgeStore(projectRoot);
1318
- await ensureCustomSkillsScaffold(projectRoot);
1319
927
  await writeHooks(projectRoot, config);
1320
928
  await syncDisabledHarnessArtifacts(projectRoot, harnesses);
1321
929
  await syncManagedGitHooks(projectRoot, config);
@@ -1442,7 +1050,7 @@ function isManagedRuntimeHookCommand(command) {
1442
1050
  // (e.g. `node .cclaw\hooks\run-hook.mjs ...`) still round-trip through
1443
1051
  // sync without being duplicated alongside freshly generated entries.
1444
1052
  const normalized = command.trim().replace(/\s+/gu, " ").replace(/\\/gu, "/");
1445
- if (/(^|\s)(?:node\s+)?(?:"|')?(?:\.\/)?\.cclaw\/hooks\/run-hook\.(?:mjs|cmd)(?:"|')?\s+(?:session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor|verify-current-state)(?:\s|$)/u.test(normalized)) {
1053
+ if (/(^|\s)(?:node\s+)?(?:"|')?(?:\.\/)?\.cclaw\/hooks\/run-hook\.(?:mjs|cmd)(?:"|')?\s+(?:session-start|stop-handoff|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor|verify-current-state)(?:\s|$)/u.test(normalized)) {
1446
1054
  return true;
1447
1055
  }
1448
1056
  // Codex UserPromptSubmit non-blocking state nudge.