cclaw-cli 0.10.1 → 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/config.js +83 -3
- 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/hooks.js +81 -11
- package/dist/content/meta-skill.d.ts +0 -8
- package/dist/content/meta-skill.js +51 -341
- package/dist/content/next-command.js +2 -1
- package/dist/content/protocols.d.ts +7 -0
- package/dist/content/protocols.js +123 -0
- package/dist/content/research-playbooks.d.ts +8 -0
- package/dist/content/research-playbooks.js +135 -0
- package/dist/content/skills.js +202 -312
- package/dist/content/stage-common-guidance.d.ts +2 -0
- package/dist/content/stage-common-guidance.js +71 -0
- package/dist/content/stage-schema.d.ts +11 -1
- package/dist/content/stage-schema.js +155 -52
- package/dist/content/start-command.js +19 -13
- 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 +133 -27
- package/dist/flow-state.d.ts +4 -0
- package/dist/flow-state.js +4 -1
- package/dist/gate-evidence.d.ts +9 -1
- package/dist/gate-evidence.js +121 -17
- 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 +73 -1
- package/dist/policy.js +21 -13
- package/dist/runs.js +21 -4
- package/dist/track-heuristics.d.ts +12 -0
- package/dist/track-heuristics.js +144 -0
- package/dist/types.d.ts +26 -3
- package/dist/types.js +6 -3
- package/package.json +2 -1
- package/dist/content/agents.d.ts +0 -48
- package/dist/content/agents.js +0 -411
|
@@ -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
|
@@ -15,15 +15,21 @@ import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
|
|
|
15
15
|
import { sessionStartScript, stopCheckpointScript, preCompactScript, opencodePluginJs, claudeHooksJson, cursorHooksJson, codexHooksJson } from "./content/hooks.js";
|
|
16
16
|
import { contextMonitorScript, promptGuardScript, workflowGuardScript } from "./content/observe.js";
|
|
17
17
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
18
|
+
import { decisionProtocolMarkdown, completionProtocolMarkdown, ethosProtocolMarkdown } from "./content/protocols.js";
|
|
18
19
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
19
20
|
import { TDD_WAVE_WALKTHROUGH_MARKDOWN, stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
|
|
21
|
+
import { stageCommonGuidanceMarkdown } from "./content/stage-common-guidance.js";
|
|
20
22
|
import { STAGE_EXAMPLES_REFERENCE_DIR, stageExamplesReferenceMarkdown } from "./content/examples.js";
|
|
21
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";
|
|
22
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";
|
|
23
29
|
import { createInitialFlowState } from "./flow-state.js";
|
|
24
30
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
25
31
|
import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
|
|
26
|
-
import { HARNESS_ADAPTERS, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
32
|
+
import { HARNESS_ADAPTERS, harnessTier, syncHarnessShims, removeCclawFromAgentsMd } from "./harness-adapters.js";
|
|
27
33
|
import { validateHookDocument } from "./hook-schema.js";
|
|
28
34
|
import { ensureRunSystem, readFlowState } from "./runs.js";
|
|
29
35
|
const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
|
|
@@ -185,6 +191,7 @@ async function writeSkills(projectRoot, config) {
|
|
|
185
191
|
// always-rendered TDD skill stays under the line-budget and the reference
|
|
186
192
|
// is loaded on demand.
|
|
187
193
|
await writeFileSafe(runtimePath(projectRoot, ...STAGE_EXAMPLES_REFERENCE_DIR.split("/"), "tdd-wave-walkthrough.md"), TDD_WAVE_WALKTHROUGH_MARKDOWN);
|
|
194
|
+
await writeFileSafe(runtimePath(projectRoot, ...STAGE_EXAMPLES_REFERENCE_DIR.split("/"), "common-guidance.md"), stageCommonGuidanceMarkdown());
|
|
188
195
|
// Utility skills (not flow stages)
|
|
189
196
|
await writeFileSafe(runtimePath(projectRoot, "skills", "learnings", "SKILL.md"), learnSkillMarkdown());
|
|
190
197
|
await writeFileSafe(runtimePath(projectRoot, "skills", "flow-next-step", "SKILL.md"), nextCommandSkillMarkdown());
|
|
@@ -194,10 +201,17 @@ async function writeSkills(projectRoot, config) {
|
|
|
194
201
|
await writeFileSafe(runtimePath(projectRoot, "skills", "parallel-dispatch", "SKILL.md"), parallelAgentsSkill());
|
|
195
202
|
await writeFileSafe(runtimePath(projectRoot, "skills", "session", "SKILL.md"), sessionHooksSkillMarkdown());
|
|
196
203
|
await writeFileSafe(runtimePath(projectRoot, "skills", META_SKILL_NAME, "SKILL.md"), usingCclawSkillMarkdown());
|
|
204
|
+
await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "decision.md"), decisionProtocolMarkdown());
|
|
205
|
+
await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "completion.md"), completionProtocolMarkdown());
|
|
206
|
+
await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "ethos.md"), ethosProtocolMarkdown());
|
|
197
207
|
for (const folder of UTILITY_SKILL_FOLDERS) {
|
|
198
208
|
const generator = UTILITY_SKILL_MAP[folder];
|
|
199
209
|
await writeFileSafe(runtimePath(projectRoot, "skills", folder, "SKILL.md"), generator());
|
|
200
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
|
+
}
|
|
201
215
|
// Language rule packs live under .cclaw/rules/lang/<pack>.md. They are opt-in:
|
|
202
216
|
// only the packs listed in config.languageRulePacks are materialised. Any
|
|
203
217
|
// legacy per-language skill folders from v0.7.0 (.cclaw/skills/language-*)
|
|
@@ -225,6 +239,11 @@ async function writeSkills(projectRoot, config) {
|
|
|
225
239
|
for (const harness of harnessIds) {
|
|
226
240
|
await writeFileSafe(runtimePath(projectRoot, ...harnessRefsDir, `${harness}.md`), harnessToolRefMarkdown(harness));
|
|
227
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());
|
|
228
247
|
}
|
|
229
248
|
async function writeUtilityCommands(projectRoot) {
|
|
230
249
|
await writeFileSafe(runtimePath(projectRoot, "commands", "learn.md"), learnCommandContract());
|
|
@@ -773,6 +792,14 @@ async function ensureSessionStateFiles(projectRoot) {
|
|
|
773
792
|
if (!(await exists(contextModePath))) {
|
|
774
793
|
await writeFileSafe(contextModePath, `${JSON.stringify(createInitialContextModeState(), null, 2)}\n`);
|
|
775
794
|
}
|
|
795
|
+
const knowledgeDigestPath = path.join(stateDir, "knowledge-digest.md");
|
|
796
|
+
if (!(await exists(knowledgeDigestPath))) {
|
|
797
|
+
await writeFileSafe(knowledgeDigestPath, "# Knowledge digest (auto-generated)\n\n(no entries yet)\n");
|
|
798
|
+
}
|
|
799
|
+
const preambleLogPath = path.join(stateDir, "preamble-log.jsonl");
|
|
800
|
+
if (!(await exists(preambleLogPath))) {
|
|
801
|
+
await writeFileSafe(preambleLogPath, "");
|
|
802
|
+
}
|
|
776
803
|
}
|
|
777
804
|
async function writeRulebook(projectRoot) {
|
|
778
805
|
await writeFileSafe(runtimePath(projectRoot, "rules", "RULES.md"), RULEBOOK_MARKDOWN);
|
|
@@ -836,6 +863,33 @@ async function writeAdapterManifest(projectRoot, harnesses) {
|
|
|
836
863
|
};
|
|
837
864
|
await writeFileSafe(runtimePath(projectRoot, "adapters", "manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
|
|
838
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
|
+
}
|
|
839
893
|
async function cleanLegacyArtifacts(projectRoot) {
|
|
840
894
|
// Remove deprecated utility skill folders from older releases.
|
|
841
895
|
for (const legacyFolder of [
|
|
@@ -868,6 +922,23 @@ async function cleanLegacyArtifacts(projectRoot) {
|
|
|
868
922
|
catch {
|
|
869
923
|
// best-effort cleanup
|
|
870
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
|
+
}
|
|
871
942
|
for (const legacyPlugin of [
|
|
872
943
|
path.join(projectRoot, ".opencode/plugins/viby-plugin.mjs"),
|
|
873
944
|
path.join(projectRoot, ".opencode/plugins/opencode-plugin.mjs"),
|
|
@@ -940,6 +1011,7 @@ async function materializeRuntime(projectRoot, config, forceStateReset) {
|
|
|
940
1011
|
await ensureRunSystem(projectRoot, { createIfMissing: false });
|
|
941
1012
|
await ensureSessionStateFiles(projectRoot);
|
|
942
1013
|
await writeAdapterManifest(projectRoot, harnesses);
|
|
1014
|
+
await writeHarnessGapsState(projectRoot, harnesses);
|
|
943
1015
|
await ensureKnowledgeStore(projectRoot);
|
|
944
1016
|
await ensureCustomSkillsScaffold(projectRoot);
|
|
945
1017
|
await writeHooks(projectRoot, config);
|
package/dist/policy.js
CHANGED
|
@@ -40,16 +40,15 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
40
40
|
"## Process",
|
|
41
41
|
"## Verification",
|
|
42
42
|
"## Interaction Protocol",
|
|
43
|
-
"## Common Rationalizations",
|
|
44
43
|
"## Anti-Patterns & Red Flags",
|
|
45
44
|
"## HARD-GATE",
|
|
46
45
|
"## Checklist",
|
|
47
46
|
"## Context Loading",
|
|
48
47
|
"## Automatic Subagent Dispatch",
|
|
49
|
-
"## Cognitive Patterns",
|
|
50
48
|
"## Cross-Stage Traceability",
|
|
51
|
-
"##
|
|
52
|
-
"##
|
|
49
|
+
"## Artifact Validation",
|
|
50
|
+
"## Completion Parameters",
|
|
51
|
+
"## Shared Stage Guidance"
|
|
53
52
|
]) {
|
|
54
53
|
rules.push({
|
|
55
54
|
filePath: skillFile,
|
|
@@ -99,18 +98,25 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
99
98
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "Review Army", name: "utility_skill:parallel:review_army" },
|
|
100
99
|
{ file: runtimeFile("skills/parallel-dispatch/SKILL.md"), needle: "Reconciliation", name: "utility_skill:parallel:reconciliation" },
|
|
101
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" },
|
|
102
106
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:session:hard_gate" },
|
|
103
107
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Start Protocol", name: "utility_skill:session:start" },
|
|
104
108
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Stop Protocol", name: "utility_skill:session:stop" },
|
|
105
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "##
|
|
106
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "##
|
|
107
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Stage
|
|
108
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "##
|
|
109
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "##
|
|
110
|
-
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "##
|
|
111
|
-
{ file: runtimeFile("
|
|
109
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Routing flow", name: "meta_skill:routing_flow" },
|
|
110
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Task classification", name: "meta_skill:task_classification" },
|
|
111
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Stage quick map", name: "meta_skill:stage_quick_map" },
|
|
112
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Contextual skill activation", name: "meta_skill:contextual_skills" },
|
|
113
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Protocol references", name: "meta_skill:protocol_refs" },
|
|
114
|
+
{ file: runtimeFile("skills/using-cclaw/SKILL.md"), needle: "## Failure guardrails", name: "meta_skill:failure_guardrails" },
|
|
115
|
+
{ file: runtimeFile("references/protocols/decision.md"), needle: "# Decision Protocol", name: "protocol:decision" },
|
|
116
|
+
{ file: runtimeFile("references/protocols/completion.md"), needle: "# Stage Completion Protocol", name: "protocol:completion" },
|
|
117
|
+
{ file: runtimeFile("references/protocols/ethos.md"), needle: "# Engineering Ethos", name: "protocol:ethos" },
|
|
112
118
|
{ file: runtimeFile("skills/session/SKILL.md"), needle: "## Session Resume Protocol", name: "utility_skill:session:resume" },
|
|
113
|
-
{ file: runtimeFile("skills/brainstorming/SKILL.md"), needle: "
|
|
119
|
+
{ file: runtimeFile("skills/brainstorming/SKILL.md"), needle: "common-guidance.md", name: "stage_skill:shared_guidance_reference" },
|
|
114
120
|
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## HARD-GATE", name: "utility_skill:security:hard_gate" },
|
|
115
121
|
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## Checklist", name: "utility_skill:security:checklist" },
|
|
116
122
|
{ file: runtimeFile("skills/security/SKILL.md"), needle: "## Severity Classification", name: "utility_skill:security:severity" },
|
|
@@ -153,7 +159,9 @@ export async function policyChecks(projectRoot, options = {}) {
|
|
|
153
159
|
{ file: runtimeFile("hooks/workflow-guard.sh"), needle: "stage_invocation_without_recent_flow_read", name: "hooks:workflow_guard:flow_read_reason" },
|
|
154
160
|
{ file: runtimeFile("hooks/workflow-guard.sh"), needle: "stage_jump_", name: "hooks:workflow_guard:stage_jump_reason" },
|
|
155
161
|
{ file: runtimeFile("hooks/context-monitor.sh"), needle: "remaining is", name: "hooks:context:threshold_warning" },
|
|
156
|
-
{ file: runtimeFile("hooks/opencode-plugin.mjs"), needle: "activeRunId", name: "hooks:opencode:active_run" }
|
|
162
|
+
{ file: runtimeFile("hooks/opencode-plugin.mjs"), needle: "activeRunId", name: "hooks:opencode:active_run" },
|
|
163
|
+
{ file: runtimeFile("hooks/session-start.sh"), needle: "Knowledge digest", name: "hooks:session_start:knowledge_digest" },
|
|
164
|
+
{ file: runtimeFile("hooks/opencode-plugin.mjs"), needle: "Knowledge digest", name: "hooks:opencode:knowledge_digest" }
|
|
157
165
|
];
|
|
158
166
|
if (activeHarnesses.has("opencode")) {
|
|
159
167
|
utilitySkillChecks.push({
|
package/dist/runs.js
CHANGED
|
@@ -128,11 +128,15 @@ function sanitizeGuardEvidence(value) {
|
|
|
128
128
|
return next;
|
|
129
129
|
}
|
|
130
130
|
function sanitizeStageGateCatalog(value, fallback) {
|
|
131
|
+
const uniqueStrings = (items) => [...new Set(items)];
|
|
131
132
|
const next = {};
|
|
132
133
|
for (const stage of COMMAND_FILE_ORDER) {
|
|
133
134
|
const base = fallback[stage];
|
|
134
135
|
next[stage] = {
|
|
135
136
|
required: [...base.required],
|
|
137
|
+
recommended: [...base.recommended],
|
|
138
|
+
conditional: [...base.conditional],
|
|
139
|
+
triggered: [...base.triggered],
|
|
136
140
|
passed: [...base.passed],
|
|
137
141
|
blocked: [...base.blocked]
|
|
138
142
|
};
|
|
@@ -147,11 +151,24 @@ function sanitizeStageGateCatalog(value, fallback) {
|
|
|
147
151
|
continue;
|
|
148
152
|
}
|
|
149
153
|
const typed = rawStage;
|
|
150
|
-
const
|
|
154
|
+
const stageState = next[stage];
|
|
155
|
+
const allowedGateIds = new Set([
|
|
156
|
+
...stageState.required,
|
|
157
|
+
...stageState.recommended,
|
|
158
|
+
...stageState.conditional
|
|
159
|
+
]);
|
|
160
|
+
const conditionalGateIds = new Set(stageState.conditional);
|
|
161
|
+
const passed = sanitizeStringArray(typed.passed).filter((gate) => allowedGateIds.has(gate));
|
|
162
|
+
const blocked = sanitizeStringArray(typed.blocked).filter((gate) => allowedGateIds.has(gate));
|
|
163
|
+
const triggeredFromState = sanitizeStringArray(typed.triggered).filter((gate) => conditionalGateIds.has(gate));
|
|
164
|
+
const touchedConditionals = [...passed, ...blocked].filter((gate) => conditionalGateIds.has(gate));
|
|
151
165
|
next[stage] = {
|
|
152
|
-
required: [...
|
|
153
|
-
|
|
154
|
-
|
|
166
|
+
required: [...stageState.required],
|
|
167
|
+
recommended: [...stageState.recommended],
|
|
168
|
+
conditional: [...stageState.conditional],
|
|
169
|
+
triggered: uniqueStrings([...triggeredFromState, ...touchedConditionals]),
|
|
170
|
+
passed,
|
|
171
|
+
blocked
|
|
155
172
|
};
|
|
156
173
|
}
|
|
157
174
|
return next;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { FlowTrack, TrackHeuristicRule, TrackHeuristicsConfig } from "./types.js";
|
|
2
|
+
export interface TrackResolution {
|
|
3
|
+
track: FlowTrack;
|
|
4
|
+
reason: string;
|
|
5
|
+
matchedTokens: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function resolveTrackFromPrompt(prompt: string, config: TrackHeuristicsConfig | undefined): TrackResolution;
|
|
8
|
+
export declare const TRACK_HEURISTICS_DEFAULTS: {
|
|
9
|
+
readonly fallback: "standard";
|
|
10
|
+
readonly priority: ("quick" | "medium" | "standard")[];
|
|
11
|
+
readonly tracks: Record<"quick" | "medium" | "standard", TrackHeuristicRule>;
|
|
12
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { FLOW_TRACKS } from "./types.js";
|
|
2
|
+
const DEFAULT_RULES = {
|
|
3
|
+
quick: {
|
|
4
|
+
triggers: [
|
|
5
|
+
"bug",
|
|
6
|
+
"bugfix",
|
|
7
|
+
"fix",
|
|
8
|
+
"hotfix",
|
|
9
|
+
"patch",
|
|
10
|
+
"typo",
|
|
11
|
+
"regression",
|
|
12
|
+
"copy change",
|
|
13
|
+
"rename",
|
|
14
|
+
"bump",
|
|
15
|
+
"upgrade dep",
|
|
16
|
+
"config tweak",
|
|
17
|
+
"docs only",
|
|
18
|
+
"comment",
|
|
19
|
+
"lint",
|
|
20
|
+
"format",
|
|
21
|
+
"small",
|
|
22
|
+
"tiny",
|
|
23
|
+
"one-liner",
|
|
24
|
+
"revert"
|
|
25
|
+
]
|
|
26
|
+
},
|
|
27
|
+
medium: {
|
|
28
|
+
triggers: [
|
|
29
|
+
"add endpoint",
|
|
30
|
+
"add field",
|
|
31
|
+
"extend existing",
|
|
32
|
+
"wire integration",
|
|
33
|
+
"small migration",
|
|
34
|
+
"new screen following existing pattern"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
standard: {
|
|
38
|
+
triggers: [
|
|
39
|
+
"new feature",
|
|
40
|
+
"refactor",
|
|
41
|
+
"migration",
|
|
42
|
+
"platform",
|
|
43
|
+
"architecture",
|
|
44
|
+
"schema",
|
|
45
|
+
"integrate",
|
|
46
|
+
"workflow",
|
|
47
|
+
"onboarding"
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const DEFAULT_PRIORITY = ["standard", "medium", "quick"];
|
|
52
|
+
const DEFAULT_FALLBACK = "standard";
|
|
53
|
+
function hasToken(promptLower, token) {
|
|
54
|
+
return promptLower.includes(token.toLowerCase());
|
|
55
|
+
}
|
|
56
|
+
function matchRule(promptLower, rule) {
|
|
57
|
+
if (!rule)
|
|
58
|
+
return [];
|
|
59
|
+
const matches = [];
|
|
60
|
+
for (const trigger of rule.triggers ?? []) {
|
|
61
|
+
if (hasToken(promptLower, trigger)) {
|
|
62
|
+
matches.push(trigger);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
for (const pattern of rule.patterns ?? []) {
|
|
66
|
+
try {
|
|
67
|
+
const regex = new RegExp(pattern, "iu");
|
|
68
|
+
if (regex.test(promptLower)) {
|
|
69
|
+
matches.push(`/${pattern}/`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch {
|
|
73
|
+
// Ignore invalid custom regex entries; config validation should catch these.
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return [...new Set(matches)];
|
|
77
|
+
}
|
|
78
|
+
function isValidTrack(value) {
|
|
79
|
+
return FLOW_TRACKS.includes(value);
|
|
80
|
+
}
|
|
81
|
+
function mergeRules(base, overrides) {
|
|
82
|
+
const merged = { ...base };
|
|
83
|
+
const over = overrides?.tracks;
|
|
84
|
+
if (!over)
|
|
85
|
+
return merged;
|
|
86
|
+
for (const track of FLOW_TRACKS) {
|
|
87
|
+
const rule = over[track];
|
|
88
|
+
if (!rule)
|
|
89
|
+
continue;
|
|
90
|
+
merged[track] = {
|
|
91
|
+
triggers: rule.triggers ?? merged[track].triggers,
|
|
92
|
+
patterns: rule.patterns ?? merged[track].patterns,
|
|
93
|
+
veto: rule.veto ?? merged[track].veto
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
return merged;
|
|
97
|
+
}
|
|
98
|
+
function resolvePriority(config) {
|
|
99
|
+
const configured = config?.priority ?? [];
|
|
100
|
+
const filtered = configured.filter((track) => isValidTrack(track));
|
|
101
|
+
const unique = [...new Set(filtered)];
|
|
102
|
+
if (unique.length === 0)
|
|
103
|
+
return [...DEFAULT_PRIORITY];
|
|
104
|
+
// Ensure all tracks are still represented in deterministic order.
|
|
105
|
+
for (const track of FLOW_TRACKS) {
|
|
106
|
+
if (!unique.includes(track))
|
|
107
|
+
unique.push(track);
|
|
108
|
+
}
|
|
109
|
+
return unique;
|
|
110
|
+
}
|
|
111
|
+
function resolveFallback(config) {
|
|
112
|
+
return config?.fallback && isValidTrack(config.fallback) ? config.fallback : DEFAULT_FALLBACK;
|
|
113
|
+
}
|
|
114
|
+
export function resolveTrackFromPrompt(prompt, config) {
|
|
115
|
+
const promptLower = prompt.toLowerCase();
|
|
116
|
+
const rules = mergeRules(DEFAULT_RULES, config);
|
|
117
|
+
const priority = resolvePriority(config);
|
|
118
|
+
const fallback = resolveFallback(config);
|
|
119
|
+
for (const track of priority) {
|
|
120
|
+
const rule = rules[track];
|
|
121
|
+
const vetoes = rule.veto ?? [];
|
|
122
|
+
if (vetoes.some((token) => hasToken(promptLower, token))) {
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const matched = matchRule(promptLower, rule);
|
|
126
|
+
if (matched.length > 0) {
|
|
127
|
+
return {
|
|
128
|
+
track,
|
|
129
|
+
reason: `matched ${track} heuristic`,
|
|
130
|
+
matchedTokens: matched
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return {
|
|
135
|
+
track: fallback,
|
|
136
|
+
reason: `no explicit match, fallback=${fallback}`,
|
|
137
|
+
matchedTokens: []
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
export const TRACK_HEURISTICS_DEFAULTS = {
|
|
141
|
+
fallback: DEFAULT_FALLBACK,
|
|
142
|
+
priority: DEFAULT_PRIORITY,
|
|
143
|
+
tracks: DEFAULT_RULES
|
|
144
|
+
};
|
package/dist/types.d.ts
CHANGED
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export declare const FLOW_STAGES: readonly ["brainstorm", "scope", "design", "spec", "plan", "tdd", "review", "ship"];
|
|
2
2
|
export type FlowStage = (typeof FLOW_STAGES)[number];
|
|
3
|
-
export declare const FLOW_TRACKS: readonly ["quick", "standard"];
|
|
3
|
+
export declare const FLOW_TRACKS: readonly ["quick", "medium", "standard"];
|
|
4
4
|
export type FlowTrack = (typeof FLOW_TRACKS)[number];
|
|
5
5
|
/**
|
|
6
6
|
* Ordered stages that make up each flow track.
|
|
7
7
|
*
|
|
8
8
|
* - `standard` runs the full 8-stage pipeline (default — same as before tracks existed).
|
|
9
|
+
* - `medium` keeps product framing but skips heavy scope/design lock-in:
|
|
10
|
+
* brainstorm -> spec -> plan -> tdd -> review -> ship.
|
|
9
11
|
* - `quick` skips the upstream product stages (brainstorm/scope/design/plan) for
|
|
10
12
|
* small bug fixes or single-purpose changes where the spec is already known.
|
|
11
13
|
* It still keeps the non-negotiable safety gates: spec → tdd → review → ship.
|
|
@@ -16,8 +18,8 @@ export type HarnessId = (typeof HARNESS_IDS)[number];
|
|
|
16
18
|
/**
|
|
17
19
|
* Init profiles pre-fill `cclaw init` flags for common install shapes.
|
|
18
20
|
*
|
|
19
|
-
* - `minimal` — single-harness (claude),
|
|
20
|
-
* contributors
|
|
21
|
+
* - `minimal` — single-harness (claude), medium track default, no git hook guards. For solo
|
|
22
|
+
* contributors who still want brainstorm/spec/plan rigor without full scope+design overhead.
|
|
21
23
|
* - `standard` — default harness set, standard track, no git hook guards, advisory guards.
|
|
22
24
|
* Matches the pre-profile default behavior.
|
|
23
25
|
* - `full` — default harness set, standard track, git hook guards on, strict prompt guards.
|
|
@@ -35,6 +37,22 @@ export type InitProfile = (typeof INIT_PROFILES)[number];
|
|
|
35
37
|
*/
|
|
36
38
|
export declare const LANGUAGE_RULE_PACKS: readonly ["typescript", "python", "go"];
|
|
37
39
|
export type LanguageRulePack = (typeof LANGUAGE_RULE_PACKS)[number];
|
|
40
|
+
export interface TrackHeuristicRule {
|
|
41
|
+
triggers?: string[];
|
|
42
|
+
patterns?: string[];
|
|
43
|
+
veto?: string[];
|
|
44
|
+
}
|
|
45
|
+
export interface TrackHeuristicsConfig {
|
|
46
|
+
/** Track used when no trigger/pattern matches. */
|
|
47
|
+
fallback?: FlowTrack;
|
|
48
|
+
/**
|
|
49
|
+
* Track evaluation order. First matching track wins.
|
|
50
|
+
* Example: ["standard", "medium", "quick"].
|
|
51
|
+
*/
|
|
52
|
+
priority?: FlowTrack[];
|
|
53
|
+
/** Per-track matching rules. */
|
|
54
|
+
tracks?: Partial<Record<FlowTrack, TrackHeuristicRule>>;
|
|
55
|
+
}
|
|
38
56
|
export interface VibyConfig {
|
|
39
57
|
version: string;
|
|
40
58
|
flowVersion: string;
|
|
@@ -54,6 +72,11 @@ export interface VibyConfig {
|
|
|
54
72
|
* the language in question. Disabled packs have no on-disk footprint.
|
|
55
73
|
*/
|
|
56
74
|
languageRulePacks?: LanguageRulePack[];
|
|
75
|
+
/**
|
|
76
|
+
* Optional prompt-to-track mapping overrides for /cc classification.
|
|
77
|
+
* If omitted, cclaw uses built-in defaults.
|
|
78
|
+
*/
|
|
79
|
+
trackHeuristics?: TrackHeuristicsConfig;
|
|
57
80
|
}
|
|
58
81
|
export interface TransitionRule {
|
|
59
82
|
from: FlowStage;
|
package/dist/types.js
CHANGED
|
@@ -8,25 +8,28 @@ export const FLOW_STAGES = [
|
|
|
8
8
|
"review",
|
|
9
9
|
"ship"
|
|
10
10
|
];
|
|
11
|
-
export const FLOW_TRACKS = ["quick", "standard"];
|
|
11
|
+
export const FLOW_TRACKS = ["quick", "medium", "standard"];
|
|
12
12
|
/**
|
|
13
13
|
* Ordered stages that make up each flow track.
|
|
14
14
|
*
|
|
15
15
|
* - `standard` runs the full 8-stage pipeline (default — same as before tracks existed).
|
|
16
|
+
* - `medium` keeps product framing but skips heavy scope/design lock-in:
|
|
17
|
+
* brainstorm -> spec -> plan -> tdd -> review -> ship.
|
|
16
18
|
* - `quick` skips the upstream product stages (brainstorm/scope/design/plan) for
|
|
17
19
|
* small bug fixes or single-purpose changes where the spec is already known.
|
|
18
20
|
* It still keeps the non-negotiable safety gates: spec → tdd → review → ship.
|
|
19
21
|
*/
|
|
20
22
|
export const TRACK_STAGES = {
|
|
21
23
|
standard: FLOW_STAGES,
|
|
24
|
+
medium: ["brainstorm", "spec", "plan", "tdd", "review", "ship"],
|
|
22
25
|
quick: ["spec", "tdd", "review", "ship"]
|
|
23
26
|
};
|
|
24
27
|
export const HARNESS_IDS = ["claude", "cursor", "opencode", "codex"];
|
|
25
28
|
/**
|
|
26
29
|
* Init profiles pre-fill `cclaw init` flags for common install shapes.
|
|
27
30
|
*
|
|
28
|
-
* - `minimal` — single-harness (claude),
|
|
29
|
-
* contributors
|
|
31
|
+
* - `minimal` — single-harness (claude), medium track default, no git hook guards. For solo
|
|
32
|
+
* contributors who still want brainstorm/spec/plan rigor without full scope+design overhead.
|
|
30
33
|
* - `standard` — default harness set, standard track, no git hook guards, advisory guards.
|
|
31
34
|
* Matches the pre-profile default behavior.
|
|
32
35
|
* - `full` — default harness set, standard track, git hook guards on, strict prompt guards.
|
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;
|