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.
- package/README.md +54 -82
- package/dist/artifact-linter.d.ts +4 -0
- package/dist/artifact-linter.js +24 -3
- package/dist/cli.d.ts +1 -19
- package/dist/cli.js +49 -495
- package/dist/constants.d.ts +2 -13
- package/dist/constants.js +1 -46
- package/dist/content/closeout-guidance.d.ts +14 -0
- package/dist/content/closeout-guidance.js +42 -0
- package/dist/content/core-agents.js +51 -9
- package/dist/content/decision-protocol.d.ts +12 -0
- package/dist/content/decision-protocol.js +20 -0
- package/dist/content/diff-command.d.ts +1 -2
- package/dist/content/diff-command.js +8 -94
- package/dist/content/examples.d.ts +4 -10
- package/dist/content/examples.js +10 -20
- package/dist/content/hook-events.js +2 -2
- package/dist/content/hook-inline-snippets.d.ts +5 -2
- package/dist/content/hook-inline-snippets.js +33 -1
- package/dist/content/hook-manifest.d.ts +3 -4
- package/dist/content/hook-manifest.js +11 -12
- package/dist/content/hooks.js +2 -0
- package/dist/content/ideate-command.d.ts +2 -0
- package/dist/content/ideate-command.js +31 -25
- package/dist/content/iron-laws.d.ts +5 -5
- package/dist/content/iron-laws.js +5 -5
- package/dist/content/learnings.d.ts +3 -4
- package/dist/content/learnings.js +24 -50
- package/dist/content/meta-skill.js +31 -24
- package/dist/content/next-command.js +38 -38
- package/dist/content/node-hooks.js +17 -343
- package/dist/content/opencode-plugin.js +2 -100
- package/dist/content/research-playbooks.js +14 -14
- package/dist/content/review-loop.d.ts +2 -0
- package/dist/content/review-loop.js +8 -0
- package/dist/content/session-hooks.js +14 -46
- package/dist/content/skills.d.ts +0 -5
- package/dist/content/skills.js +53 -128
- package/dist/content/stage-common-guidance.d.ts +0 -1
- package/dist/content/stage-common-guidance.js +15 -14
- package/dist/content/stage-schema.d.ts +26 -1
- package/dist/content/stage-schema.js +121 -40
- package/dist/content/stages/_lint-metadata/index.js +9 -15
- package/dist/content/stages/brainstorm.js +22 -43
- package/dist/content/stages/design.js +37 -57
- package/dist/content/stages/plan.js +22 -13
- package/dist/content/stages/review.js +24 -27
- package/dist/content/stages/scope.js +34 -46
- package/dist/content/stages/ship.js +7 -4
- package/dist/content/stages/spec.js +20 -9
- package/dist/content/stages/tdd.js +64 -44
- package/dist/content/start-command.js +10 -12
- package/dist/content/status-command.d.ts +2 -7
- package/dist/content/status-command.js +19 -146
- package/dist/content/subagents.d.ts +0 -5
- package/dist/content/subagents.js +47 -28
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +126 -135
- package/dist/content/track-render-context.d.ts +17 -0
- package/dist/content/track-render-context.js +44 -0
- package/dist/content/tree-command.d.ts +1 -2
- package/dist/content/tree-command.js +4 -87
- package/dist/content/utility-skills.d.ts +2 -29
- package/dist/content/utility-skills.js +2 -1533
- package/dist/content/view-command.js +29 -11
- package/dist/delegation.d.ts +1 -1
- package/dist/delegation.js +5 -15
- package/dist/doctor-registry.js +20 -21
- package/dist/doctor.js +88 -408
- package/dist/flow-state.d.ts +3 -0
- package/dist/flow-state.js +2 -0
- package/dist/harness-adapters.d.ts +1 -1
- package/dist/harness-adapters.js +48 -57
- package/dist/install.js +128 -520
- package/dist/internal/advance-stage.js +3 -9
- package/dist/internal/compound-readiness.d.ts +1 -1
- package/dist/internal/compound-readiness.js +1 -1
- package/dist/internal/tdd-loop-status.d.ts +1 -1
- package/dist/internal/tdd-loop-status.js +1 -1
- package/dist/knowledge-store.d.ts +16 -10
- package/dist/knowledge-store.js +51 -15
- package/dist/policy.js +16 -109
- package/dist/run-archive.d.ts +4 -6
- package/dist/run-archive.js +15 -20
- package/dist/run-persistence.d.ts +2 -2
- package/dist/run-persistence.js +3 -9
- package/package.json +1 -2
- package/dist/content/archive-command.d.ts +0 -2
- package/dist/content/archive-command.js +0 -124
- package/dist/content/compound-command.d.ts +0 -5
- package/dist/content/compound-command.js +0 -193
- package/dist/content/contexts.d.ts +0 -9
- package/dist/content/contexts.js +0 -65
- package/dist/content/contracts.d.ts +0 -2
- package/dist/content/contracts.js +0 -51
- package/dist/content/doctor-references.d.ts +0 -2
- package/dist/content/doctor-references.js +0 -150
- package/dist/content/eval-scaffold.d.ts +0 -15
- package/dist/content/eval-scaffold.js +0 -370
- package/dist/content/feature-command.d.ts +0 -2
- package/dist/content/feature-command.js +0 -123
- package/dist/content/flow-map.d.ts +0 -23
- package/dist/content/flow-map.js +0 -134
- package/dist/content/harness-doc.d.ts +0 -2
- package/dist/content/harness-doc.js +0 -202
- package/dist/content/harness-playbooks.d.ts +0 -24
- package/dist/content/harness-playbooks.js +0 -393
- package/dist/content/harness-tool-refs.d.ts +0 -20
- package/dist/content/harness-tool-refs.js +0 -268
- package/dist/content/ops-command.d.ts +0 -2
- package/dist/content/ops-command.js +0 -71
- package/dist/content/protocols.d.ts +0 -7
- package/dist/content/protocols.js +0 -215
- package/dist/content/retro-command.d.ts +0 -2
- package/dist/content/retro-command.js +0 -165
- package/dist/content/rewind-command.d.ts +0 -2
- package/dist/content/rewind-command.js +0 -106
- package/dist/content/tdd-log-command.d.ts +0 -2
- package/dist/content/tdd-log-command.js +0 -85
- package/dist/eval/agents/single-shot.d.ts +0 -27
- package/dist/eval/agents/single-shot.js +0 -79
- package/dist/eval/agents/with-tools.d.ts +0 -44
- package/dist/eval/agents/with-tools.js +0 -261
- package/dist/eval/agents/workflow.d.ts +0 -31
- package/dist/eval/agents/workflow.js +0 -155
- package/dist/eval/baseline.d.ts +0 -38
- package/dist/eval/baseline.js +0 -282
- package/dist/eval/config-loader.d.ts +0 -14
- package/dist/eval/config-loader.js +0 -395
- package/dist/eval/corpus.d.ts +0 -30
- package/dist/eval/corpus.js +0 -330
- package/dist/eval/cost-guard.d.ts +0 -102
- package/dist/eval/cost-guard.js +0 -190
- package/dist/eval/diff.d.ts +0 -64
- package/dist/eval/diff.js +0 -323
- package/dist/eval/llm-client.d.ts +0 -176
- package/dist/eval/llm-client.js +0 -267
- package/dist/eval/mode.d.ts +0 -28
- package/dist/eval/mode.js +0 -61
- package/dist/eval/progress.d.ts +0 -83
- package/dist/eval/progress.js +0 -59
- package/dist/eval/report.d.ts +0 -11
- package/dist/eval/report.js +0 -181
- package/dist/eval/rubric-loader.d.ts +0 -20
- package/dist/eval/rubric-loader.js +0 -143
- package/dist/eval/runner.d.ts +0 -81
- package/dist/eval/runner.js +0 -746
- package/dist/eval/runs.d.ts +0 -41
- package/dist/eval/runs.js +0 -114
- package/dist/eval/sandbox.d.ts +0 -38
- package/dist/eval/sandbox.js +0 -137
- package/dist/eval/tools/glob.d.ts +0 -2
- package/dist/eval/tools/glob.js +0 -163
- package/dist/eval/tools/grep.d.ts +0 -2
- package/dist/eval/tools/grep.js +0 -152
- package/dist/eval/tools/index.d.ts +0 -7
- package/dist/eval/tools/index.js +0 -35
- package/dist/eval/tools/read.d.ts +0 -2
- package/dist/eval/tools/read.js +0 -122
- package/dist/eval/tools/types.d.ts +0 -49
- package/dist/eval/tools/types.js +0 -41
- package/dist/eval/tools/write.d.ts +0 -2
- package/dist/eval/tools/write.js +0 -92
- package/dist/eval/types.d.ts +0 -561
- package/dist/eval/types.js +0 -47
- package/dist/eval/verifiers/judge.d.ts +0 -40
- package/dist/eval/verifiers/judge.js +0 -256
- package/dist/eval/verifiers/rules.d.ts +0 -24
- package/dist/eval/verifiers/rules.js +0 -218
- package/dist/eval/verifiers/structural.d.ts +0 -14
- package/dist/eval/verifiers/structural.js +0 -171
- package/dist/eval/verifiers/traceability.d.ts +0 -23
- package/dist/eval/verifiers/traceability.js +0 -84
- package/dist/eval/verifiers/workflow-consistency.d.ts +0 -21
- package/dist/eval/verifiers/workflow-consistency.js +0 -225
- package/dist/eval/workflow-corpus.d.ts +0 -7
- package/dist/eval/workflow-corpus.js +0 -207
- package/dist/feature-system.d.ts +0 -42
- package/dist/feature-system.js +0 -432
- package/dist/internal/knowledge-digest.d.ts +0 -7
- 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 {
|
|
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 {
|
|
34
|
-
import {
|
|
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,
|
|
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 {
|
|
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
|
|
412
|
-
await writeFileSafe(runtimePath(projectRoot, "commands", "
|
|
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
|
-
|
|
1163
|
-
|
|
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
|
-
|
|
1188
|
-
|
|
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
|
-
|
|
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, "
|
|
1225
|
-
runtimePath(projectRoot, "
|
|
1226
|
-
runtimePath(projectRoot, "
|
|
1227
|
-
runtimePath(projectRoot, "
|
|
1228
|
-
runtimePath(projectRoot,
|
|
1229
|
-
runtimePath(projectRoot, "hooks",
|
|
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
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
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
|
-
|
|
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.
|