cclaw-cli 0.11.0 → 0.12.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 +4 -3
- package/dist/cli.d.ts +6 -0
- package/dist/cli.js +297 -9
- package/dist/content/core-agents.d.ts +44 -0
- package/dist/content/core-agents.js +225 -0
- package/dist/content/doctor-references.d.ts +2 -0
- package/dist/content/doctor-references.js +144 -0
- package/dist/content/examples.js +1 -1
- package/dist/content/harnesses-doc.d.ts +1 -0
- package/dist/content/harnesses-doc.js +95 -0
- package/dist/content/hook-events.d.ts +4 -0
- package/dist/content/hook-events.js +42 -0
- package/dist/content/protocols.js +34 -6
- package/dist/content/research-playbooks.d.ts +8 -0
- package/dist/content/research-playbooks.js +135 -0
- package/dist/content/skills.js +20 -0
- package/dist/content/stage-schema.d.ts +3 -1
- package/dist/content/stage-schema.js +20 -51
- package/dist/content/subagents.d.ts +1 -1
- package/dist/content/subagents.js +23 -38
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +49 -11
- package/dist/delegation.d.ts +1 -0
- package/dist/delegation.js +27 -1
- package/dist/doctor-registry.d.ts +8 -0
- package/dist/doctor-registry.js +127 -0
- package/dist/doctor.d.ts +5 -0
- package/dist/doctor.js +112 -4
- package/dist/harness-adapters.d.ts +7 -0
- package/dist/harness-adapters.js +53 -9
- package/dist/init-detect.d.ts +2 -0
- package/dist/init-detect.js +45 -0
- package/dist/install.js +63 -1
- package/dist/policy.js +5 -0
- package/package.json +2 -1
- package/dist/content/agents.d.ts +0 -48
- package/dist/content/agents.js +0 -411
package/dist/doctor.js
CHANGED
|
@@ -4,7 +4,7 @@ import { execFile } from "node:child_process";
|
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
6
|
import { COMMAND_FILE_ORDER, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
|
|
7
|
-
import { CCLAW_AGENTS } from "./content/agents.js";
|
|
7
|
+
import { CCLAW_AGENTS } from "./content/core-agents.js";
|
|
8
8
|
import { readConfig } from "./config.js";
|
|
9
9
|
import { exists } from "./fs-utils.js";
|
|
10
10
|
import { gitignoreHasRequiredPatterns } from "./gitignore.js";
|
|
@@ -17,10 +17,13 @@ import { checkMandatoryDelegations } from "./delegation.js";
|
|
|
17
17
|
import { buildTraceMatrix } from "./trace-matrix.js";
|
|
18
18
|
import { reconcileAndWriteCurrentStageGateCatalog, verifyCompletedStagesGateClosure, verifyCurrentStageGateEvidence } from "./gate-evidence.js";
|
|
19
19
|
import { stageSkillFolder } from "./content/skills.js";
|
|
20
|
+
import { doctorCheckMetadata } from "./doctor-registry.js";
|
|
20
21
|
import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LEGACY_LANGUAGE_RULE_PACK_FOLDERS, UTILITY_SKILL_FOLDERS } from "./content/utility-skills.js";
|
|
21
22
|
import { CONTEXT_MODES, DEFAULT_CONTEXT_MODE } from "./content/contexts.js";
|
|
23
|
+
import { DOCTOR_REFERENCE_MARKDOWN } from "./content/doctor-references.js";
|
|
22
24
|
import { validateHookDocument } from "./hook-schema.js";
|
|
23
25
|
const execFileAsync = promisify(execFile);
|
|
26
|
+
const PREAMBLE_COOLDOWN_MS = 15 * 60 * 1000;
|
|
24
27
|
async function isGitRepo(projectRoot) {
|
|
25
28
|
try {
|
|
26
29
|
await execFileAsync("git", ["rev-parse", "--is-inside-work-tree"], { cwd: projectRoot });
|
|
@@ -345,6 +348,20 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
345
348
|
details: refPath
|
|
346
349
|
});
|
|
347
350
|
}
|
|
351
|
+
checks.push({
|
|
352
|
+
name: "harness_ref:matrix",
|
|
353
|
+
ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "references", "harnesses.md")),
|
|
354
|
+
details: `${RUNTIME_ROOT}/references/harnesses.md`
|
|
355
|
+
});
|
|
356
|
+
const doctorRefDir = path.join(projectRoot, RUNTIME_ROOT, "references", "doctor");
|
|
357
|
+
for (const fileName of Object.keys(DOCTOR_REFERENCE_MARKDOWN)) {
|
|
358
|
+
const refPath = path.join(doctorRefDir, fileName);
|
|
359
|
+
checks.push({
|
|
360
|
+
name: `doctor_ref:${fileName.replace(/\.md$/, "")}`,
|
|
361
|
+
ok: await exists(refPath),
|
|
362
|
+
details: refPath
|
|
363
|
+
});
|
|
364
|
+
}
|
|
348
365
|
checks.push({
|
|
349
366
|
name: "gitignore:required_patterns",
|
|
350
367
|
ok: await gitignoreHasRequiredPatterns(projectRoot),
|
|
@@ -810,6 +827,11 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
810
827
|
ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "state", "suggestion-memory.json")),
|
|
811
828
|
details: `${RUNTIME_ROOT}/state/suggestion-memory.json must exist for proactive suggestion memory`
|
|
812
829
|
});
|
|
830
|
+
checks.push({
|
|
831
|
+
name: "state:harness_gaps_exists",
|
|
832
|
+
ok: await exists(path.join(projectRoot, RUNTIME_ROOT, "state", "harness-gaps.json")),
|
|
833
|
+
details: `${RUNTIME_ROOT}/state/harness-gaps.json must exist for tiered harness capability tracking`
|
|
834
|
+
});
|
|
813
835
|
const contextModeStatePath = path.join(projectRoot, RUNTIME_ROOT, "state", "context-mode.json");
|
|
814
836
|
checks.push({
|
|
815
837
|
name: "state:context_mode_exists",
|
|
@@ -840,6 +862,81 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
840
862
|
details: modePath
|
|
841
863
|
});
|
|
842
864
|
}
|
|
865
|
+
const preambleLogPath = path.join(projectRoot, RUNTIME_ROOT, "state", "preamble-log.jsonl");
|
|
866
|
+
const preambleLogExists = await exists(preambleLogPath);
|
|
867
|
+
checks.push({
|
|
868
|
+
name: "state:preamble_log_exists",
|
|
869
|
+
ok: preambleLogExists,
|
|
870
|
+
details: `${RUNTIME_ROOT}/state/preamble-log.jsonl must exist for preamble budget tracking`
|
|
871
|
+
});
|
|
872
|
+
if (preambleLogExists) {
|
|
873
|
+
let duplicateHits = 0;
|
|
874
|
+
let parsedEntries = 0;
|
|
875
|
+
let malformedEntries = 0;
|
|
876
|
+
try {
|
|
877
|
+
const now = Date.now();
|
|
878
|
+
const byKey = new Map();
|
|
879
|
+
const raw = await fs.readFile(preambleLogPath, "utf8");
|
|
880
|
+
const lines = raw
|
|
881
|
+
.split("\n")
|
|
882
|
+
.map((line) => line.trim())
|
|
883
|
+
.filter((line) => line.length > 0);
|
|
884
|
+
for (const line of lines) {
|
|
885
|
+
try {
|
|
886
|
+
const parsed = JSON.parse(line);
|
|
887
|
+
const tsRaw = parsed.ts;
|
|
888
|
+
const stageRaw = parsed.stage;
|
|
889
|
+
const triggerRaw = parsed.trigger;
|
|
890
|
+
const hashRaw = parsed.hash;
|
|
891
|
+
if (typeof tsRaw !== "string" ||
|
|
892
|
+
typeof stageRaw !== "string" ||
|
|
893
|
+
typeof triggerRaw !== "string" ||
|
|
894
|
+
typeof hashRaw !== "string") {
|
|
895
|
+
malformedEntries += 1;
|
|
896
|
+
continue;
|
|
897
|
+
}
|
|
898
|
+
const stamp = Date.parse(tsRaw);
|
|
899
|
+
if (!Number.isFinite(stamp)) {
|
|
900
|
+
malformedEntries += 1;
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
if (now - stamp > 24 * 60 * 60 * 1000) {
|
|
904
|
+
continue;
|
|
905
|
+
}
|
|
906
|
+
parsedEntries += 1;
|
|
907
|
+
const key = `${stageRaw}|${triggerRaw}|${hashRaw}`;
|
|
908
|
+
const bucket = byKey.get(key) ?? [];
|
|
909
|
+
bucket.push(stamp);
|
|
910
|
+
byKey.set(key, bucket);
|
|
911
|
+
}
|
|
912
|
+
catch {
|
|
913
|
+
malformedEntries += 1;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
for (const stamps of byKey.values()) {
|
|
917
|
+
stamps.sort((a, b) => a - b);
|
|
918
|
+
for (let i = 1; i < stamps.length; i += 1) {
|
|
919
|
+
if (stamps[i] - stamps[i - 1] < PREAMBLE_COOLDOWN_MS) {
|
|
920
|
+
duplicateHits += 1;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
catch {
|
|
926
|
+
malformedEntries += 1;
|
|
927
|
+
}
|
|
928
|
+
checks.push({
|
|
929
|
+
name: "warning:preamble:dedup",
|
|
930
|
+
ok: true,
|
|
931
|
+
details: duplicateHits > 0
|
|
932
|
+
? `warning: detected ${duplicateHits} repeated preamble emission(s) inside ${Math.floor(PREAMBLE_COOLDOWN_MS / 60000)}m cooldown window`
|
|
933
|
+
: parsedEntries > 0
|
|
934
|
+
? `preamble budget healthy (${parsedEntries} recent preamble entry/entries checked)`
|
|
935
|
+
: malformedEntries > 0
|
|
936
|
+
? `warning: preamble log exists but entries are malformed (${malformedEntries} line(s) ignored)`
|
|
937
|
+
: "preamble log is empty; no recent preamble emissions recorded"
|
|
938
|
+
});
|
|
939
|
+
}
|
|
843
940
|
let flowState = await readFlowState(projectRoot);
|
|
844
941
|
if (options.reconcileCurrentStageGates === true) {
|
|
845
942
|
const reconciliation = await reconcileAndWriteCurrentStageGateCatalog(projectRoot);
|
|
@@ -911,7 +1008,9 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
911
1008
|
name: "warning:delegation:waived",
|
|
912
1009
|
ok: true,
|
|
913
1010
|
details: delegation.waived.length > 0
|
|
914
|
-
? `warning: waived mandatory delegations for stage "${flowState.currentStage}": ${delegation.waived.join(", ")}
|
|
1011
|
+
? `warning: waived mandatory delegations for stage "${flowState.currentStage}": ${delegation.waived.join(", ")}${delegation.autoWaived.length > 0
|
|
1012
|
+
? ` (auto-waived due to harness limitation: ${delegation.autoWaived.join(", ")})`
|
|
1013
|
+
: ""}`
|
|
915
1014
|
: "no waived mandatory delegations for current stage"
|
|
916
1015
|
});
|
|
917
1016
|
checks.push({
|
|
@@ -1025,8 +1124,17 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1025
1124
|
});
|
|
1026
1125
|
const policy = await policyChecks(projectRoot, { harnesses: configuredHarnesses });
|
|
1027
1126
|
checks.push(...policy);
|
|
1028
|
-
return checks
|
|
1127
|
+
return checks.map((check) => {
|
|
1128
|
+
const metadata = doctorCheckMetadata(check.name);
|
|
1129
|
+
return {
|
|
1130
|
+
...check,
|
|
1131
|
+
severity: check.severity ?? metadata.severity,
|
|
1132
|
+
summary: check.summary ?? metadata.summary,
|
|
1133
|
+
fix: check.fix ?? metadata.fix,
|
|
1134
|
+
docRef: check.docRef ?? metadata.docRef
|
|
1135
|
+
};
|
|
1136
|
+
});
|
|
1029
1137
|
}
|
|
1030
1138
|
export function doctorSucceeded(checks) {
|
|
1031
|
-
return checks.every((check) => check.ok);
|
|
1139
|
+
return checks.every((check) => check.ok || check.severity !== "error");
|
|
1032
1140
|
}
|
|
@@ -4,8 +4,15 @@ export declare const CCLAW_MARKER_END = "<!-- cclaw-end -->";
|
|
|
4
4
|
export interface HarnessAdapter {
|
|
5
5
|
id: HarnessId;
|
|
6
6
|
commandDir: string;
|
|
7
|
+
capabilities: {
|
|
8
|
+
nativeSubagentDispatch: "full" | "partial" | "none";
|
|
9
|
+
hookSurface: "full" | "plugin" | "limited" | "none";
|
|
10
|
+
structuredAsk: "AskUserQuestion" | "AskQuestion" | "plain-text";
|
|
11
|
+
};
|
|
7
12
|
}
|
|
8
13
|
export declare const HARNESS_ADAPTERS: Record<HarnessId, HarnessAdapter>;
|
|
14
|
+
export type HarnessTier = "tier1" | "tier2" | "tier3";
|
|
15
|
+
export declare function harnessTier(harnessId: HarnessId): HarnessTier;
|
|
9
16
|
/** Removes the cclaw AGENTS.md block. */
|
|
10
17
|
export declare function stripCclawBlock(content: string): string;
|
|
11
18
|
export declare function removeCclawFromAgentsMd(projectRoot: string): Promise<void>;
|
package/dist/harness-adapters.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { RUNTIME_ROOT } from "./constants.js";
|
|
4
|
-
import { CCLAW_AGENTS, agentMarkdown } from "./content/agents.js";
|
|
4
|
+
import { CCLAW_AGENTS, agentMarkdown } from "./content/core-agents.js";
|
|
5
5
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
6
6
|
export const CCLAW_MARKER_START = "<!-- cclaw-start -->";
|
|
7
7
|
export const CCLAW_MARKER_END = "<!-- cclaw-end -->";
|
|
@@ -12,11 +12,57 @@ const RUNTIME_AGENTS_BLOCK_SOURCE = `${escapeRegExp(CCLAW_MARKER_START)}[\\s\\S]
|
|
|
12
12
|
const RUNTIME_AGENTS_BLOCK_PATTERN = new RegExp(RUNTIME_AGENTS_BLOCK_SOURCE, "u");
|
|
13
13
|
const RUNTIME_AGENTS_BLOCK_GLOBAL_PATTERN = new RegExp(RUNTIME_AGENTS_BLOCK_SOURCE, "gu");
|
|
14
14
|
export const HARNESS_ADAPTERS = {
|
|
15
|
-
claude: {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
15
|
+
claude: {
|
|
16
|
+
id: "claude",
|
|
17
|
+
commandDir: ".claude/commands",
|
|
18
|
+
capabilities: {
|
|
19
|
+
nativeSubagentDispatch: "full",
|
|
20
|
+
hookSurface: "full",
|
|
21
|
+
structuredAsk: "AskUserQuestion"
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
cursor: {
|
|
25
|
+
id: "cursor",
|
|
26
|
+
commandDir: ".cursor/commands",
|
|
27
|
+
capabilities: {
|
|
28
|
+
nativeSubagentDispatch: "partial",
|
|
29
|
+
hookSurface: "full",
|
|
30
|
+
structuredAsk: "AskQuestion"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
opencode: {
|
|
34
|
+
id: "opencode",
|
|
35
|
+
commandDir: ".opencode/commands",
|
|
36
|
+
capabilities: {
|
|
37
|
+
nativeSubagentDispatch: "partial",
|
|
38
|
+
hookSurface: "plugin",
|
|
39
|
+
structuredAsk: "plain-text"
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
codex: {
|
|
43
|
+
id: "codex",
|
|
44
|
+
commandDir: ".codex/commands",
|
|
45
|
+
capabilities: {
|
|
46
|
+
nativeSubagentDispatch: "none",
|
|
47
|
+
hookSurface: "full",
|
|
48
|
+
structuredAsk: "plain-text"
|
|
49
|
+
}
|
|
50
|
+
}
|
|
19
51
|
};
|
|
52
|
+
export function harnessTier(harnessId) {
|
|
53
|
+
const capabilities = HARNESS_ADAPTERS[harnessId].capabilities;
|
|
54
|
+
if (capabilities.nativeSubagentDispatch === "full" &&
|
|
55
|
+
capabilities.structuredAsk !== "plain-text" &&
|
|
56
|
+
capabilities.hookSurface === "full") {
|
|
57
|
+
return "tier1";
|
|
58
|
+
}
|
|
59
|
+
if (capabilities.hookSurface === "full" ||
|
|
60
|
+
capabilities.hookSurface === "plugin" ||
|
|
61
|
+
capabilities.nativeSubagentDispatch === "partial") {
|
|
62
|
+
return "tier2";
|
|
63
|
+
}
|
|
64
|
+
return "tier3";
|
|
65
|
+
}
|
|
20
66
|
function agentsMdBlock() {
|
|
21
67
|
return `${CCLAW_MARKER_START}
|
|
22
68
|
## Cclaw — Workflow Adapter
|
|
@@ -63,10 +109,6 @@ When in doubt, prefer **non-trivial** — the quick track is opt-in and only saf
|
|
|
63
109
|
**Stage order:** brainstorm > scope > design > spec > plan > tdd > review > ship.
|
|
64
110
|
\`/cc-next\` loads the right stage skill automatically. Gates must pass before handoff.
|
|
65
111
|
|
|
66
|
-
### Invocation Preamble (non-trivial turns)
|
|
67
|
-
|
|
68
|
-
Before starting substantive work, emit a one-paragraph preamble: **Stage**, **Goal**, **Plan** (next 1–3 actions), **Guardrails**. Skip for pure questions, trivial edits, and dispatched subagent invocations.
|
|
69
|
-
|
|
70
112
|
### Verification Discipline
|
|
71
113
|
|
|
72
114
|
No completion claims without fresh evidence. No "Done" / "All good" / "Tests pass" without running the command in this message. Failed tool calls are diagnostic data, not instructions.
|
|
@@ -78,7 +120,9 @@ If the same approach fails three times in a row (same command, same finding, sam
|
|
|
78
120
|
### Detail Level
|
|
79
121
|
|
|
80
122
|
- This managed AGENTS block is intentionally minimal for cross-project use.
|
|
123
|
+
- Harness coverage is tiered: Tier1 (claude), Tier2 (cursor/opencode/codex), Tier3 (fallback/manual-only).
|
|
81
124
|
- Detailed operating procedures live in \`.cclaw/skills/using-cclaw/SKILL.md\`.
|
|
125
|
+
- Preamble budget and cooldown rules live in \`.cclaw/references/protocols/ethos.md\`.
|
|
82
126
|
- Subagent orchestration patterns: \`.cclaw/skills/subagent-dev/SKILL.md\` and \`.cclaw/skills/parallel-dispatch/SKILL.md\`.
|
|
83
127
|
${CCLAW_MARKER_END}`;
|
|
84
128
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { exists } from "./fs-utils.js";
|
|
3
|
+
export async function detectHarnesses(projectRoot) {
|
|
4
|
+
const detected = [];
|
|
5
|
+
const claudeHints = [
|
|
6
|
+
path.join(projectRoot, ".claude"),
|
|
7
|
+
path.join(projectRoot, "CLAUDE.md")
|
|
8
|
+
];
|
|
9
|
+
if (await anyExists(claudeHints)) {
|
|
10
|
+
detected.push("claude");
|
|
11
|
+
}
|
|
12
|
+
const cursorHints = [
|
|
13
|
+
path.join(projectRoot, ".cursor"),
|
|
14
|
+
path.join(projectRoot, ".cursor/rules")
|
|
15
|
+
];
|
|
16
|
+
if (await anyExists(cursorHints)) {
|
|
17
|
+
detected.push("cursor");
|
|
18
|
+
}
|
|
19
|
+
const opencodeHints = [
|
|
20
|
+
path.join(projectRoot, ".opencode"),
|
|
21
|
+
path.join(projectRoot, "opencode.json"),
|
|
22
|
+
path.join(projectRoot, "opencode.jsonc"),
|
|
23
|
+
path.join(projectRoot, ".opencode/opencode.json"),
|
|
24
|
+
path.join(projectRoot, ".opencode/opencode.jsonc")
|
|
25
|
+
];
|
|
26
|
+
if (await anyExists(opencodeHints)) {
|
|
27
|
+
detected.push("opencode");
|
|
28
|
+
}
|
|
29
|
+
const codexHints = [
|
|
30
|
+
path.join(projectRoot, ".codex"),
|
|
31
|
+
path.join(projectRoot, ".codex/hooks.json")
|
|
32
|
+
];
|
|
33
|
+
if (await anyExists(codexHints)) {
|
|
34
|
+
detected.push("codex");
|
|
35
|
+
}
|
|
36
|
+
return detected;
|
|
37
|
+
}
|
|
38
|
+
async function anyExists(paths) {
|
|
39
|
+
for (const candidate of paths) {
|
|
40
|
+
if (await exists(candidate)) {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return false;
|
|
45
|
+
}
|
package/dist/install.js
CHANGED
|
@@ -21,11 +21,15 @@ import { TDD_WAVE_WALKTHROUGH_MARKDOWN, stageSkillFolder, stageSkillMarkdown } f
|
|
|
21
21
|
import { stageCommonGuidanceMarkdown } from "./content/stage-common-guidance.js";
|
|
22
22
|
import { STAGE_EXAMPLES_REFERENCE_DIR, stageExamplesReferenceMarkdown } from "./content/examples.js";
|
|
23
23
|
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";
|
|
24
|
+
import { RESEARCH_PLAYBOOKS } from "./content/research-playbooks.js";
|
|
24
25
|
import { HARNESS_TOOL_REFS_DIR, HARNESS_TOOL_REFS_INDEX_MD, harnessToolRefMarkdown } from "./content/harness-tool-refs.js";
|
|
26
|
+
import { DOCTOR_REFERENCE_MARKDOWN } from "./content/doctor-references.js";
|
|
27
|
+
import { harnessIntegrationDocMarkdown } from "./content/harnesses-doc.js";
|
|
28
|
+
import { HOOK_EVENTS_BY_HARNESS, HOOK_SEMANTIC_EVENTS } from "./content/hook-events.js";
|
|
25
29
|
import { createInitialFlowState } from "./flow-state.js";
|
|
26
30
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
27
31
|
import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
|
|
28
|
-
import { HARNESS_ADAPTERS, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
32
|
+
import { HARNESS_ADAPTERS, harnessTier, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
29
33
|
import { validateHookDocument } from "./hook-schema.js";
|
|
30
34
|
import { ensureRunSystem, readFlowState } from "./runs.js";
|
|
31
35
|
const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
|
|
@@ -204,6 +208,10 @@ async function writeSkills(projectRoot, config) {
|
|
|
204
208
|
const generator = UTILITY_SKILL_MAP[folder];
|
|
205
209
|
await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
|
|
206
210
|
}
|
|
211
|
+
// In-thread research procedures (no YAML frontmatter, not delegated personas).
|
|
212
|
+
for (const [fileName, markdown] of Object.entries(RESEARCH_PLAYBOOKS)) {
|
|
213
|
+
await writeFileSafe(runtimePath(projectRoot, "skills", "research", fileName), markdown);
|
|
214
|
+
}
|
|
207
215
|
// Language rule packs live under .cclaw/rules/lang/<pack>.md. They are opt-in:
|
|
208
216
|
// only the packs listed in config.languageRulePacks are materialised. Any
|
|
209
217
|
// legacy per-language skill folders from v0.7.0 (.cclaw/skills/language-*)
|
|
@@ -231,6 +239,11 @@ async function writeSkills(projectRoot, config) {
|
|
|
231
239
|
for (const harness of harnessIds) {
|
|
232
240
|
await writeFileSafe(runtimePath(projectRoot, ...harnessRefsDir, `${harness}.md`), harnessToolRefMarkdown(harness));
|
|
233
241
|
}
|
|
242
|
+
const doctorRefsDir = ["references", "doctor"];
|
|
243
|
+
for (const [fileName, markdown] of Object.entries(DOCTOR_REFERENCE_MARKDOWN)) {
|
|
244
|
+
await writeFileSafe(runtimePath(projectRoot, ...doctorRefsDir, fileName), markdown);
|
|
245
|
+
}
|
|
246
|
+
await writeFileSafe(runtimePath(projectRoot, "references", "harnesses.md"), harnessIntegrationDocMarkdown());
|
|
234
247
|
}
|
|
235
248
|
async function writeUtilityCommands(projectRoot) {
|
|
236
249
|
await writeFileSafe(runtimePath(projectRoot, "commands", "learn.md"), learnCommandContract());
|
|
@@ -783,6 +796,10 @@ async function ensureSessionStateFiles(projectRoot) {
|
|
|
783
796
|
if (!(await exists(knowledgeDigestPath))) {
|
|
784
797
|
await writeFileSafe(knowledgeDigestPath, "# Knowledge digest (auto-generated)\n\n(no entries yet)\n");
|
|
785
798
|
}
|
|
799
|
+
const preambleLogPath = path.join(stateDir, "preamble-log.jsonl");
|
|
800
|
+
if (!(await exists(preambleLogPath))) {
|
|
801
|
+
await writeFileSafe(preambleLogPath, "");
|
|
802
|
+
}
|
|
786
803
|
}
|
|
787
804
|
async function writeRulebook(projectRoot) {
|
|
788
805
|
await writeFileSafe(runtimePath(projectRoot, "rules", "RULES.md"), RULEBOOK_MARKDOWN);
|
|
@@ -846,6 +863,33 @@ async function writeAdapterManifest(projectRoot, harnesses) {
|
|
|
846
863
|
};
|
|
847
864
|
await writeFileSafe(runtimePath(projectRoot, "adapters", "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
848
865
|
}
|
|
866
|
+
async function writeHarnessGapsState(projectRoot, harnesses) {
|
|
867
|
+
const report = harnesses.map((harness) => {
|
|
868
|
+
const capabilities = HARNESS_ADAPTERS[harness].capabilities;
|
|
869
|
+
const hookMap = HOOK_EVENTS_BY_HARNESS[harness];
|
|
870
|
+
const missingHookEvents = HOOK_SEMANTIC_EVENTS.filter((eventName) => !hookMap[eventName]);
|
|
871
|
+
const missingCapabilities = [];
|
|
872
|
+
if (capabilities.nativeSubagentDispatch !== "full") {
|
|
873
|
+
missingCapabilities.push(`nativeSubagentDispatch:${capabilities.nativeSubagentDispatch}`);
|
|
874
|
+
}
|
|
875
|
+
if (capabilities.hookSurface !== "full") {
|
|
876
|
+
missingCapabilities.push(`hookSurface:${capabilities.hookSurface}`);
|
|
877
|
+
}
|
|
878
|
+
if (capabilities.structuredAsk === "plain-text") {
|
|
879
|
+
missingCapabilities.push("structuredAsk:none");
|
|
880
|
+
}
|
|
881
|
+
return {
|
|
882
|
+
harness,
|
|
883
|
+
tier: harnessTier(harness),
|
|
884
|
+
missingCapabilities,
|
|
885
|
+
missingHookEvents
|
|
886
|
+
};
|
|
887
|
+
});
|
|
888
|
+
await writeFileSafe(runtimePath(projectRoot, "state", "harness-gaps.json"), `${JSON.stringify({
|
|
889
|
+
generatedAt: new Date().toISOString(),
|
|
890
|
+
harnesses: report
|
|
891
|
+
}, null, 2)}\n`);
|
|
892
|
+
}
|
|
849
893
|
async function cleanLegacyArtifacts(projectRoot) {
|
|
850
894
|
// Remove deprecated utility skill folders from older releases.
|
|
851
895
|
for (const legacyFolder of [
|
|
@@ -878,6 +922,23 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
878
922
|
catch {
|
|
879
923
|
// best-effort cleanup
|
|
880
924
|
}
|
|
925
|
+
// Core-5 migration: remove deprecated generated agent personas.
|
|
926
|
+
for (const legacyAgentFile of [
|
|
927
|
+
"spec-reviewer.md",
|
|
928
|
+
"code-reviewer.md",
|
|
929
|
+
"repo-research-analyst.md",
|
|
930
|
+
"learnings-researcher.md",
|
|
931
|
+
"framework-docs-researcher.md",
|
|
932
|
+
"best-practices-researcher.md",
|
|
933
|
+
"git-history-analyzer.md"
|
|
934
|
+
]) {
|
|
935
|
+
try {
|
|
936
|
+
await fs.rm(runtimePath(projectRoot, "agents", legacyAgentFile), { force: true });
|
|
937
|
+
}
|
|
938
|
+
catch {
|
|
939
|
+
// best-effort cleanup
|
|
940
|
+
}
|
|
941
|
+
}
|
|
881
942
|
for (const legacyPlugin of [
|
|
882
943
|
path.join(projectRoot, ".opencode/plugins/viby-plugin.mjs"),
|
|
883
944
|
path.join(projectRoot, ".opencode/plugins/opencode-plugin.mjs"),
|
|
@@ -950,6 +1011,7 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
|
|
|
950
1011
|
await ensureRunSystem(projectRoot, { createIfMissing: false });
|
|
951
1012
|
await ensureSessionStateFiles(projectRoot);
|
|
952
1013
|
await writeAdapterManifest(projectRoot, harnesses);
|
|
1014
|
+
await writeHarnessGapsState(projectRoot, harnesses);
|
|
953
1015
|
await ensureKnowledgeStore(projectRoot);
|
|
954
1016
|
await ensureCustomSkillsScaffold(projectRoot);
|
|
955
1017
|
await writeHooks(projectRoot, config);
|
package/dist/policy.js
CHANGED
|
@@ -98,6 +98,11 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
98
98
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "Review Army", name: "utility_skill:parallel:review_army" },
|
|
99
99
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "Reconciliation", name: "utility_skill:parallel:reconciliation" },
|
|
100
100
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "## Model & Harness Routing Notes", name: "utility_skill:parallel:routing_notes" },
|
|
101
|
+
{ file: runtimeFile("skills/research/repo-scan.md"), needle: "# Repo Scan Playbook", name: "utility_skill:research:repo_scan" },
|
|
102
|
+
{ file: runtimeFile("skills/research/learnings-lookup.md"), needle: "# Learnings Lookup Playbook", name: "utility_skill:research:learnings_lookup" },
|
|
103
|
+
{ file: runtimeFile("skills/research/framework-docs-lookup.md"), needle: "# Framework Docs Lookup Playbook", name: "utility_skill:research:framework_docs_lookup" },
|
|
104
|
+
{ file: runtimeFile("skills/research/best-practices-lookup.md"), needle: "# Best Practices Lookup Playbook", name: "utility_skill:research:best_practices_lookup" },
|
|
105
|
+
{ file: runtimeFile("skills/research/git-history.md"), needle: "# Git History Playbook", name: "utility_skill:research:git_history" },
|
|
101
106
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:session:hard_gate" },
|
|
102
107
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Start Protocol", name: "utility_skill:session:start" },
|
|
103
108
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Stop Protocol", name: "utility_skill:session:stop" },
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cclaw-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "Installer-first flow toolkit for coding agents",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
"test:coverage": "vitest run --coverage",
|
|
24
24
|
"smoke:runtime": "npm run build && node scripts/smoke-init.mjs",
|
|
25
25
|
"lint:hooks": "npm run build && node scripts/lint-generated-hooks.mjs",
|
|
26
|
+
"build:harness-docs": "npm run build && node scripts/build-harness-docs.mjs",
|
|
26
27
|
"build:plugin-manifests": "npm run build && node scripts/build-plugin-manifests.mjs",
|
|
27
28
|
"release:check": "npm run build && npm run test && node scripts/lint-generated-hooks.mjs && node scripts/build-plugin-manifests.mjs && npm pack --dry-run && node scripts/smoke-init.mjs",
|
|
28
29
|
"release:bundle": "npm run release:check && npm pack"
|
package/dist/content/agents.d.ts
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent persona content for Cclaw.
|
|
3
|
-
*
|
|
4
|
-
* Cclaw emits markdown agent definitions (`.md` with YAML frontmatter) that harnesses
|
|
5
|
-
* use for specialist delegation. Agents are isolated context windows with constrained
|
|
6
|
-
* tools; skills remain procedural recipes.
|
|
7
|
-
*/
|
|
8
|
-
export interface AgentDefinition {
|
|
9
|
-
/** Kebab-case identifier, e.g. `"spec-reviewer"`. */
|
|
10
|
-
name: string;
|
|
11
|
-
/** When to invoke — include PROACTIVE / MUST BE USED style guidance for harnesses. */
|
|
12
|
-
description: string;
|
|
13
|
-
/** Allowed tools for this agent (harness-specific names). */
|
|
14
|
-
tools: string[];
|
|
15
|
-
/** Model tier for routing cost/latency vs depth. */
|
|
16
|
-
model: "fast" | "balanced" | "deep";
|
|
17
|
-
/** How the harness should treat activation relative to flow context. */
|
|
18
|
-
activation: "proactive" | "on-demand" | "mandatory";
|
|
19
|
-
/** Cclaw flow stages this agent is designed to support. */
|
|
20
|
-
relatedStages: string[];
|
|
21
|
-
/** Markdown body rendered below the YAML frontmatter. */
|
|
22
|
-
body: string;
|
|
23
|
-
}
|
|
24
|
-
/**
|
|
25
|
-
* Canonical specialist agents Cclaw can materialize under `.cclaw/agents/`.
|
|
26
|
-
*/
|
|
27
|
-
export declare const CCLAW_AGENTS: AgentDefinition[];
|
|
28
|
-
/**
|
|
29
|
-
* Render a complete Cclaw agent markdown file (YAML frontmatter + body).
|
|
30
|
-
*/
|
|
31
|
-
export declare function agentMarkdown(agent: AgentDefinition): string;
|
|
32
|
-
/**
|
|
33
|
-
* Markdown table mapping Cclaw stage entry points to specialist agents.
|
|
34
|
-
*/
|
|
35
|
-
export declare function agentRoutingTable(): string;
|
|
36
|
-
/**
|
|
37
|
-
* Cost tier routing: keep heavy reasoning on the \`deep\` tier (planner, a
|
|
38
|
-
* single post-review reconciliation), push read-only research and narrow
|
|
39
|
-
* machine-only checks to the \`fast\` tier, and default review to \`balanced\`.
|
|
40
|
-
* This table is emitted into AGENTS.md so harness users understand why
|
|
41
|
-
* certain specialists are automatically fan-out-able without blowing the
|
|
42
|
-
* context budget.
|
|
43
|
-
*/
|
|
44
|
-
export declare function agentCostTierTable(): string;
|
|
45
|
-
/**
|
|
46
|
-
* AGENTS.md-ready section describing Cclaw’s specialist delegation model.
|
|
47
|
-
*/
|
|
48
|
-
export declare function agentsAgentsMdBlock(): string;
|