cclaw-cli 0.51.28 → 0.51.30
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/dist/cli.d.ts +6 -1
- package/dist/cli.js +117 -64
- package/dist/codex-feature-flag.d.ts +1 -1
- package/dist/codex-feature-flag.js +1 -1
- package/dist/config.js +3 -0
- package/dist/content/cancel-command.d.ts +2 -0
- package/dist/content/cancel-command.js +25 -0
- package/dist/content/closeout-guidance.js +3 -3
- package/dist/content/core-agents.js +5 -5
- package/dist/content/harness-doc.js +1 -1
- package/dist/content/hooks.js +32 -9
- package/dist/content/ideate-command.js +12 -7
- package/dist/content/meta-skill.js +7 -9
- package/dist/content/next-command.d.ts +2 -2
- package/dist/content/next-command.js +31 -27
- package/dist/content/node-hooks.js +24 -8
- package/dist/content/opencode-plugin.js +1 -1
- package/dist/content/session-hooks.js +1 -1
- package/dist/content/stage-command.js +1 -1
- package/dist/content/stage-common-guidance.js +4 -4
- package/dist/content/stages/plan.js +2 -2
- package/dist/content/stages/review.js +1 -1
- package/dist/content/stages/tdd.js +1 -1
- package/dist/content/start-command.d.ts +2 -2
- package/dist/content/start-command.js +18 -15
- package/dist/content/status-command.js +9 -8
- package/dist/content/subagents.js +1 -1
- package/dist/content/templates.d.ts +1 -1
- package/dist/content/templates.js +2 -2
- package/dist/content/track-render-context.d.ts +1 -0
- package/dist/content/track-render-context.js +2 -0
- package/dist/doctor-registry.d.ts +2 -0
- package/dist/doctor-registry.js +37 -10
- package/dist/doctor.d.ts +2 -1
- package/dist/doctor.js +184 -8
- package/dist/flow-state.d.ts +1 -1
- package/dist/flow-state.js +1 -1
- package/dist/fs-utils.js +6 -0
- package/dist/harness-adapters.d.ts +2 -2
- package/dist/harness-adapters.js +21 -94
- package/dist/harness-selection.d.ts +31 -0
- package/dist/harness-selection.js +214 -0
- package/dist/install.d.ts +4 -1
- package/dist/install.js +47 -10
- package/dist/internal/advance-stage.js +7 -7
- package/dist/managed-resources.d.ts +53 -0
- package/dist/managed-resources.js +289 -0
- package/dist/policy.js +1 -1
- package/dist/run-archive.d.ts +8 -0
- package/dist/run-archive.js +23 -9
- package/dist/run-persistence.js +1 -1
- package/dist/runs.d.ts +1 -1
- package/dist/runs.js +1 -1
- package/dist/tdd-cycle.js +10 -10
- package/dist/tdd-verification-evidence.js +4 -4
- package/dist/track-heuristics.d.ts +2 -0
- package/dist/track-heuristics.js +11 -3
- package/package.json +1 -1
|
@@ -445,7 +445,7 @@ Implementation that touches shared source trees must remain **sequential** unles
|
|
|
445
445
|
- **3–4:** appendix / “worth tracking” section (not merge-blocking alone)
|
|
446
446
|
- **1–2:** suppress from primary narrative unless paired with stronger evidence
|
|
447
447
|
|
|
448
|
-
### Review Army Artifact Contract (required in review stage via /cc
|
|
448
|
+
### Review Army Artifact Contract (required in review stage via /cc)
|
|
449
449
|
|
|
450
450
|
Write a structured reconciliation artifact at \`.cclaw/artifacts/07-review-army.json\` using this schema:
|
|
451
451
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export declare const ARTIFACT_TEMPLATES: Record<string, string>;
|
|
2
2
|
export declare const RULEBOOK_MARKDOWN = "# Cclaw Rulebook\n\n## MUST_ALWAYS\n- Follow flow order: brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship\n- Require explicit user confirmation after plan before TDD\n- Keep evidence artifacts in `.cclaw/artifacts/`\n- Enforce RED before GREEN in TDD\n- Run two-layer review (spec_compliance and code_quality) before ship\n- Validate all inputs before processing \u2014 never trust external data without sanitization\n- Prefer immutable data patterns and pure functions where the language supports them\n- Follow existing repo conventions, patterns, and directory structure \u2014 match the codebase\n- Verify claims with fresh evidence: \"tests pass\" requires running tests in this message\n- Use conventional commits: `type(scope): description` (feat, fix, refactor, test, docs, chore)\n\n## MUST_NEVER\n- Skip RED phase and jump directly to GREEN in TDD\n- Ship with critical review findings\n- Start implementation during /brainstorm\n- Modify generated cclaw files manually when CLI can regenerate them\n- Commit `.cclaw/` or generated shim files\n- Expose secrets, tokens, API keys, or absolute system paths in agent output\n- Duplicate existing functionality without explicit justification \u2014 search before building\n- Bypass security checks, linting hooks, or type checking to \"move faster\"\n- Claim success (\"Done,\" \"All good,\" \"Tests pass\") without running verification in this message\n- Make changes outside the blast radius of the current task without user consent\n\n## DELEGATION\nWhen a task requires specialist knowledge (security audit, performance profiling, database review),\ndelegate to a specialized agent or skill if the harness supports it. The primary agent should:\n1. Identify the specialist domain\n2. Provide focused context (relevant files, the specific concern)\n3. Evaluate the specialist output before acting on it \u2014 do not blindly apply recommendations\n";
|
|
3
|
-
export declare const CURSOR_WORKFLOW_RULE_MDC = "---\ndescription: cclaw workflow guardrails for Cursor agent sessions\nglobs:\n - \"**/*\"\nalwaysApply: true\n---\n\n<!-- cclaw-managed-cursor-workflow-rule -->\n\n# Cclaw Workflow Guardrails\n\n## Activation Rule\n\nBefore responding to coding work:\n1. Read `.cclaw/state/flow-state.json`.\n2. Start with `/cc` or continue with `/cc
|
|
3
|
+
export declare const CURSOR_WORKFLOW_RULE_MDC = "---\ndescription: cclaw workflow guardrails for Cursor agent sessions\nglobs:\n - \"**/*\"\nalwaysApply: true\n---\n\n<!-- cclaw-managed-cursor-workflow-rule -->\n\n# Cclaw Workflow Guardrails\n\n## Activation Rule\n\nBefore responding to coding work:\n1. Read `.cclaw/state/flow-state.json`.\n2. Start with `/cc` or continue with `/cc`.\n3. If no software-stage flow applies, respond normally.\n\n## Stage Order\n\n`brainstorm -> scope -> design -> spec -> plan -> tdd -> review -> ship`\n\nTrack-specific skips are allowed only when `flow-state.track` + `skippedStages` explicitly say so.\n\n## Task Classification\n\n| Class | Route |\n|---|---|\n| non-trivial software work | `/cc <idea>` |\n| trivial software fix | `/cc <idea>` (quick track) |\n| bugfix with repro | `/cc <idea>` and enforce RED-first in tdd |\n| pure question / non-software | direct answer (no stage flow) |\n\n## Command Surface\n\n- `/cc` = entry and resume.\n- `/cc` = only progression path.\n- Knowledge capture and recall use the `learnings` skill when requested.\n\n## Verification Discipline\n\n- No completion claim without fresh command evidence in this turn.\n- Do not mark gates passed from memory.\n- Keep evidence in `.cclaw/artifacts/`; archive via `npx cclaw-cli archive`.\n\n## Delegation And Approvals\n\n- Machine-only checks in design/plan/tdd/review/ship should auto-dispatch when tooling supports it.\n- Ask for user input only at explicit approval gates (scope mode, plan approval, challenge resolution, ship finalization).\n- If harness capabilities are partial, record waiver reasons in delegation logs.\n\n## Routing Source Of Truth\n\n- Primary router: `.cclaw/skills/using-cclaw/SKILL.md`.\n- Stage behavior: current stage skill plus `.cclaw/state/flow-state.json`.\n- Preamble budget: keep role/status announcements brief and avoid repeating\n them unless the stage or role changes.\n";
|
|
4
4
|
export declare function buildRulesJson(): Record<string, unknown>;
|
|
@@ -1410,7 +1410,7 @@ alwaysApply: true
|
|
|
1410
1410
|
|
|
1411
1411
|
Before responding to coding work:
|
|
1412
1412
|
1. Read \`.cclaw/state/flow-state.json\`.
|
|
1413
|
-
2. Start with \`/cc\` or continue with \`/cc
|
|
1413
|
+
2. Start with \`/cc\` or continue with \`/cc\`.
|
|
1414
1414
|
3. If no software-stage flow applies, respond normally.
|
|
1415
1415
|
|
|
1416
1416
|
## Stage Order
|
|
@@ -1431,7 +1431,7 @@ Track-specific skips are allowed only when \`flow-state.track\` + \`skippedStage
|
|
|
1431
1431
|
## Command Surface
|
|
1432
1432
|
|
|
1433
1433
|
- \`/cc\` = entry and resume.
|
|
1434
|
-
- \`/cc
|
|
1434
|
+
- \`/cc\` = only progression path.
|
|
1435
1435
|
- Knowledge capture and recall use the \`learnings\` skill when requested.
|
|
1436
1436
|
|
|
1437
1437
|
## Verification Discipline
|
|
@@ -9,6 +9,7 @@ export function trackRenderContext(track) {
|
|
|
9
9
|
traceabilitySourceNoun: "acceptance criterion",
|
|
10
10
|
traceabilityIdNoun: "acceptance criterion ID",
|
|
11
11
|
traceabilitySliceNoun: "acceptance slice",
|
|
12
|
+
safetySummary: "quick skips ceremony, not safety: spec approval, TDD, review, and ship gates remain mandatory",
|
|
12
13
|
upstreamArtifactLabel: "spec artifact",
|
|
13
14
|
upstreamArtifactPath: ".cclaw/artifacts/04-spec.md"
|
|
14
15
|
};
|
|
@@ -19,6 +20,7 @@ export function trackRenderContext(track) {
|
|
|
19
20
|
traceabilitySourceNoun: "plan task",
|
|
20
21
|
traceabilityIdNoun: "plan task ID",
|
|
21
22
|
traceabilitySliceNoun: "plan slice",
|
|
23
|
+
safetySummary: "full upstream planning safety remains in the active track",
|
|
22
24
|
upstreamArtifactLabel: "plan artifact",
|
|
23
25
|
upstreamArtifactPath: ".cclaw/artifacts/05-plan.md"
|
|
24
26
|
};
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
export type DoctorSeverity = "error" | "warning" | "info";
|
|
2
|
+
export type DoctorActionGroup = "sync" | "user-action" | "stage-work" | "informational";
|
|
2
3
|
export interface DoctorCheckMetadata {
|
|
3
4
|
severity: DoctorSeverity;
|
|
4
5
|
summary: string;
|
|
5
6
|
fix: string;
|
|
7
|
+
actionGroup: DoctorActionGroup;
|
|
6
8
|
docRef?: string;
|
|
7
9
|
}
|
|
8
10
|
export declare function doctorCheckMetadata(checkName: string): DoctorCheckMetadata;
|
package/dist/doctor-registry.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
function ref(fileName) {
|
|
2
|
-
|
|
2
|
+
const anchor = fileName.replace(/\.md$/u, "").replace(/[^a-z0-9]+/giu, "-").toLowerCase();
|
|
3
|
+
return `README.md#${anchor}`;
|
|
3
4
|
}
|
|
4
5
|
const RULES = [
|
|
5
6
|
{
|
|
@@ -8,6 +9,7 @@ const RULES = [
|
|
|
8
9
|
severity: "info",
|
|
9
10
|
summary: "Gate reconciliation status update.",
|
|
10
11
|
fix: "No action required unless subsequent gate checks fail.",
|
|
12
|
+
actionGroup: "informational",
|
|
11
13
|
docRef: ref("config.md")
|
|
12
14
|
}
|
|
13
15
|
},
|
|
@@ -17,6 +19,7 @@ const RULES = [
|
|
|
17
19
|
severity: "warning",
|
|
18
20
|
summary: "Advisory signal; runtime can continue with caution.",
|
|
19
21
|
fix: "Address when possible to prevent future drift or degraded behavior.",
|
|
22
|
+
actionGroup: "informational",
|
|
20
23
|
docRef: "README.md"
|
|
21
24
|
}
|
|
22
25
|
},
|
|
@@ -26,6 +29,7 @@ const RULES = [
|
|
|
26
29
|
severity: "warning",
|
|
27
30
|
summary: "Stage skill quality guardrail check.",
|
|
28
31
|
fix: "Tune generated stage skill content and re-run `cclaw sync`.",
|
|
32
|
+
actionGroup: "sync",
|
|
29
33
|
docRef: "README.md"
|
|
30
34
|
}
|
|
31
35
|
},
|
|
@@ -35,6 +39,7 @@ const RULES = [
|
|
|
35
39
|
severity: "error",
|
|
36
40
|
summary: "Required runtime tooling availability check.",
|
|
37
41
|
fix: "Install the missing required tool and re-run `cclaw doctor`.",
|
|
42
|
+
actionGroup: "user-action",
|
|
38
43
|
docRef: "README.md"
|
|
39
44
|
}
|
|
40
45
|
},
|
|
@@ -43,16 +48,28 @@ const RULES = [
|
|
|
43
48
|
metadata: {
|
|
44
49
|
severity: "error",
|
|
45
50
|
summary: "Generated runtime surface presence check.",
|
|
46
|
-
fix: "Run `cclaw sync` to regenerate runtime files, then re-run doctor.",
|
|
51
|
+
fix: "Run `cclaw sync` to safely regenerate generated runtime files, then re-run doctor.",
|
|
52
|
+
actionGroup: "sync",
|
|
47
53
|
docRef: "README.md"
|
|
48
54
|
}
|
|
49
55
|
},
|
|
56
|
+
{
|
|
57
|
+
test: /^managed_resources:/,
|
|
58
|
+
metadata: {
|
|
59
|
+
severity: "error",
|
|
60
|
+
summary: "Managed generated resource manifest integrity check.",
|
|
61
|
+
fix: "Run `cclaw sync` to refresh managed generated files; inspect upgrade backup paths before discarding local edits.",
|
|
62
|
+
actionGroup: "sync",
|
|
63
|
+
docRef: ref("config.md")
|
|
64
|
+
}
|
|
65
|
+
},
|
|
50
66
|
{
|
|
51
67
|
test: /^(hook:|hooks:|lifecycle:|git_hooks:)/,
|
|
52
68
|
metadata: {
|
|
53
69
|
severity: "error",
|
|
54
70
|
summary: "Hook wiring and lifecycle integration check.",
|
|
55
|
-
fix: "
|
|
71
|
+
fix: "Run `cclaw sync` to regenerate hook/plugin wiring; if the check still fails, validate harness config and permissions.",
|
|
72
|
+
actionGroup: "sync",
|
|
56
73
|
docRef: ref("harnesses.md")
|
|
57
74
|
}
|
|
58
75
|
},
|
|
@@ -61,7 +78,8 @@ const RULES = [
|
|
|
61
78
|
metadata: {
|
|
62
79
|
severity: "error",
|
|
63
80
|
summary: "Harness shim and routing file consistency check.",
|
|
64
|
-
fix: "
|
|
81
|
+
fix: "Run `cclaw sync` to regenerate harness adapters; confirm enabled harness list if it remains failing.",
|
|
82
|
+
actionGroup: "sync",
|
|
65
83
|
docRef: ref("harnesses.md")
|
|
66
84
|
}
|
|
67
85
|
},
|
|
@@ -70,7 +88,8 @@ const RULES = [
|
|
|
70
88
|
metadata: {
|
|
71
89
|
severity: "error",
|
|
72
90
|
summary: "Flow state and gate evidence consistency check.",
|
|
73
|
-
fix: "Repair
|
|
91
|
+
fix: "Repair the named stage artifacts/gate evidence, then run `cclaw doctor --reconcile-gates --explain` to refresh derived gate status only.",
|
|
92
|
+
actionGroup: "stage-work",
|
|
74
93
|
docRef: ref("config.md")
|
|
75
94
|
}
|
|
76
95
|
},
|
|
@@ -79,7 +98,8 @@ const RULES = [
|
|
|
79
98
|
metadata: {
|
|
80
99
|
severity: "error",
|
|
81
100
|
summary: "Knowledge and artifact runtime integrity check.",
|
|
82
|
-
fix: "Restore missing
|
|
101
|
+
fix: "Restore the missing `.cclaw/` runtime file or run `cclaw sync` when it is generated surface drift.",
|
|
102
|
+
actionGroup: "sync",
|
|
83
103
|
docRef: "README.md"
|
|
84
104
|
}
|
|
85
105
|
},
|
|
@@ -88,7 +108,8 @@ const RULES = [
|
|
|
88
108
|
metadata: {
|
|
89
109
|
severity: "error",
|
|
90
110
|
summary: "Routing skill and protocol integrity check.",
|
|
91
|
-
fix: "
|
|
111
|
+
fix: "Run `cclaw sync` to regenerate runtime skills, then re-run doctor.",
|
|
112
|
+
actionGroup: "sync",
|
|
92
113
|
docRef: ref("harnesses.md")
|
|
93
114
|
}
|
|
94
115
|
},
|
|
@@ -103,7 +124,8 @@ const RULES = [
|
|
|
103
124
|
metadata: {
|
|
104
125
|
severity: "warning",
|
|
105
126
|
summary: "Reference/overview doc integrity (non-blocking).",
|
|
106
|
-
fix: "Run `cclaw sync` to regenerate the reference
|
|
127
|
+
fix: "Run `cclaw sync` to regenerate the reference surface from the canonical source.",
|
|
128
|
+
actionGroup: "sync",
|
|
107
129
|
docRef: ref("harnesses.md")
|
|
108
130
|
}
|
|
109
131
|
},
|
|
@@ -113,6 +135,7 @@ const RULES = [
|
|
|
113
135
|
severity: "info",
|
|
114
136
|
summary: "Harness reality label for dispatch/proof support.",
|
|
115
137
|
fix: "No action required; use this label to interpret native/generic/role-switch proof requirements.",
|
|
138
|
+
actionGroup: "informational",
|
|
116
139
|
docRef: ref("harnesses.md")
|
|
117
140
|
}
|
|
118
141
|
},
|
|
@@ -121,7 +144,8 @@ const RULES = [
|
|
|
121
144
|
metadata: {
|
|
122
145
|
severity: "error",
|
|
123
146
|
summary: "Mandatory delegation completion check.",
|
|
124
|
-
fix: "
|
|
147
|
+
fix: "Run the named mandatory agent, record dispatch proof/evidenceRefs, or explicitly waive it with a user-visible rationale.",
|
|
148
|
+
actionGroup: "user-action",
|
|
125
149
|
docRef: ref("harnesses.md")
|
|
126
150
|
}
|
|
127
151
|
},
|
|
@@ -130,7 +154,8 @@ const RULES = [
|
|
|
130
154
|
metadata: {
|
|
131
155
|
severity: "error",
|
|
132
156
|
summary: "Cross-artifact traceability integrity check.",
|
|
133
|
-
fix: "
|
|
157
|
+
fix: "Repair criterion/task/test ID mappings across spec, plan, and TDD artifacts, then re-run doctor.",
|
|
158
|
+
actionGroup: "stage-work",
|
|
134
159
|
docRef: "README.md"
|
|
135
160
|
}
|
|
136
161
|
},
|
|
@@ -140,6 +165,7 @@ const RULES = [
|
|
|
140
165
|
severity: "error",
|
|
141
166
|
summary: "Config or policy schema consistency check.",
|
|
142
167
|
fix: "Fix config/rules drift, then run `cclaw sync` and re-run doctor.",
|
|
168
|
+
actionGroup: "user-action",
|
|
143
169
|
docRef: ref("config.md")
|
|
144
170
|
}
|
|
145
171
|
}
|
|
@@ -154,6 +180,7 @@ export function doctorCheckMetadata(checkName) {
|
|
|
154
180
|
severity: "warning",
|
|
155
181
|
summary: "Unclassified doctor check.",
|
|
156
182
|
fix: "Report this check name to cclaw maintainers so doctor-registry can classify it explicitly.",
|
|
183
|
+
actionGroup: "informational",
|
|
157
184
|
docRef: "README.md"
|
|
158
185
|
};
|
|
159
186
|
}
|
package/dist/doctor.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { DoctorSeverity } from "./doctor-registry.js";
|
|
1
|
+
import type { DoctorActionGroup, DoctorSeverity } from "./doctor-registry.js";
|
|
2
2
|
export interface DoctorCheck {
|
|
3
3
|
name: string;
|
|
4
4
|
ok: boolean;
|
|
@@ -6,6 +6,7 @@ export interface DoctorCheck {
|
|
|
6
6
|
severity: DoctorSeverity;
|
|
7
7
|
summary: string;
|
|
8
8
|
fix: string;
|
|
9
|
+
actionGroup: DoctorActionGroup;
|
|
9
10
|
docRef?: string;
|
|
10
11
|
}
|
|
11
12
|
export interface DoctorOptions {
|
package/dist/doctor.js
CHANGED
|
@@ -3,10 +3,11 @@ import path from "node:path";
|
|
|
3
3
|
import { execFile } from "node:child_process";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { promisify } from "node:util";
|
|
6
|
-
import { REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
|
|
6
|
+
import { CCLAW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./constants.js";
|
|
7
7
|
import { CCLAW_AGENTS } from "./content/core-agents.js";
|
|
8
8
|
import { detectAdvancedKeys, InvalidConfigError, readConfig } from "./config.js";
|
|
9
9
|
import { exists } from "./fs-utils.js";
|
|
10
|
+
import { hashManagedResourceContent, isManagedGeneratedPath, MANAGED_RESOURCE_MANIFEST_REL_PATH, readManagedResourceManifest, validateManagedResourceManifest } from "./managed-resources.js";
|
|
10
11
|
import { gitignoreHasRequiredPatterns } from "./gitignore.js";
|
|
11
12
|
import { HARNESS_ADAPTERS, CCLAW_MARKER_START, CCLAW_MARKER_END, harnessShimFileNames, harnessShimSkillNames } from "./harness-adapters.js";
|
|
12
13
|
import { policyChecks } from "./policy.js";
|
|
@@ -21,6 +22,7 @@ import { stageSkillFolder } from "./content/skills.js";
|
|
|
21
22
|
import { stageCommandShimMarkdown } from "./content/stage-command.js";
|
|
22
23
|
import { doctorCheckMetadata } from "./doctor-registry.js";
|
|
23
24
|
import { resolveTrackFromPrompt } from "./track-heuristics.js";
|
|
25
|
+
import { detectHarnesses } from "./init-detect.js";
|
|
24
26
|
import { classifyCodexHooksFlag, codexConfigPath, readCodexConfig } from "./codex-feature-flag.js";
|
|
25
27
|
import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LEGACY_LANGUAGE_RULE_PACK_FOLDERS } from "./content/utility-skills.js";
|
|
26
28
|
import { validateHookDocument } from "./hook-schema.js";
|
|
@@ -92,8 +94,48 @@ function extractGeneratedCliEntrypoints(scriptContent) {
|
|
|
92
94
|
// malformed generated constant; treat below as missing/unusable
|
|
93
95
|
}
|
|
94
96
|
}
|
|
97
|
+
for (const match of scriptContent.matchAll(/const\s+CCLAW_CLI_ARGS_PREFIX\s*=\s*(\[(?:\\.|[^\]])*\]);/gu)) {
|
|
98
|
+
try {
|
|
99
|
+
const parsed = JSON.parse(match[1] ?? "[]");
|
|
100
|
+
if (Array.isArray(parsed)) {
|
|
101
|
+
for (const item of parsed) {
|
|
102
|
+
if (typeof item === "string" && item.trim().length > 0 && !item.startsWith("-")) {
|
|
103
|
+
paths.push(item);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
// malformed generated constant; treat below as missing/unusable
|
|
110
|
+
}
|
|
111
|
+
}
|
|
95
112
|
return paths;
|
|
96
113
|
}
|
|
114
|
+
async function walkGeneratedCandidates(projectRoot, relDir, candidates) {
|
|
115
|
+
const fullDir = path.join(projectRoot, relDir);
|
|
116
|
+
if (!(await exists(fullDir)))
|
|
117
|
+
return;
|
|
118
|
+
let entries = [];
|
|
119
|
+
try {
|
|
120
|
+
entries = await fs.readdir(fullDir, { withFileTypes: true });
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
for (const entry of entries) {
|
|
126
|
+
const rel = path.join(relDir, entry.name).replace(/\\/gu, "/");
|
|
127
|
+
if (entry.isDirectory()) {
|
|
128
|
+
await walkGeneratedCandidates(projectRoot, rel, candidates);
|
|
129
|
+
}
|
|
130
|
+
else if (entry.isFile() && isManagedGeneratedPath(rel)) {
|
|
131
|
+
candidates.push(rel);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
function formatManagedValidationIssue(issue) {
|
|
136
|
+
const subject = issue.path ?? (issue.index !== undefined ? `resources[${issue.index}]` : "manifest");
|
|
137
|
+
return `${subject} ${issue.field}: ${issue.message}`;
|
|
138
|
+
}
|
|
97
139
|
async function generatedCliEntrypointsOk(projectRoot) {
|
|
98
140
|
const hookScripts = ["stage-complete.mjs", "start-flow.mjs", "run-hook.mjs"];
|
|
99
141
|
const problems = [];
|
|
@@ -690,8 +732,140 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
690
732
|
ok: await gitignoreHasRequiredPatterns(projectRoot),
|
|
691
733
|
details: ".gitignore must include cclaw ignore block"
|
|
692
734
|
});
|
|
735
|
+
const managedManifestPath = path.join(projectRoot, MANAGED_RESOURCE_MANIFEST_REL_PATH);
|
|
736
|
+
let rawManagedManifest = null;
|
|
737
|
+
let managedManifestParseError = null;
|
|
738
|
+
if (await exists(managedManifestPath)) {
|
|
739
|
+
try {
|
|
740
|
+
rawManagedManifest = JSON.parse(await fs.readFile(managedManifestPath, "utf8"));
|
|
741
|
+
}
|
|
742
|
+
catch (error) {
|
|
743
|
+
managedManifestParseError = error instanceof Error ? error.message : String(error);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
const managedManifestValidationIssues = rawManagedManifest === null
|
|
747
|
+
? []
|
|
748
|
+
: validateManagedResourceManifest(rawManagedManifest);
|
|
749
|
+
const managedManifest = await readManagedResourceManifest(projectRoot).catch(() => null);
|
|
750
|
+
checks.push({
|
|
751
|
+
name: "managed_resources:manifest_exists",
|
|
752
|
+
ok: managedManifest !== null,
|
|
753
|
+
details: managedManifest
|
|
754
|
+
? `${MANAGED_RESOURCE_MANIFEST_REL_PATH} tracks ${managedManifest.resources.length} managed generated file(s)`
|
|
755
|
+
: `${MANAGED_RESOURCE_MANIFEST_REL_PATH} missing; run cclaw sync to establish generated file ownership`
|
|
756
|
+
});
|
|
757
|
+
checks.push({
|
|
758
|
+
name: "managed_resources:manifest_valid",
|
|
759
|
+
ok: managedManifestParseError === null && managedManifestValidationIssues.length === 0,
|
|
760
|
+
details: managedManifestParseError
|
|
761
|
+
? `${MANAGED_RESOURCE_MANIFEST_REL_PATH} is unreadable JSON (${managedManifestParseError})`
|
|
762
|
+
: managedManifestValidationIssues.length === 0
|
|
763
|
+
? `${MANAGED_RESOURCE_MANIFEST_REL_PATH} metadata is structurally valid`
|
|
764
|
+
: `malformed managed resource metadata: ${managedManifestValidationIssues.slice(0, 12).map(formatManagedValidationIssue).join("; ")}`
|
|
765
|
+
});
|
|
766
|
+
if (managedManifest) {
|
|
767
|
+
checks.push({
|
|
768
|
+
name: "managed_resources:manifest_package_version",
|
|
769
|
+
ok: managedManifest.packageVersion === CCLAW_VERSION,
|
|
770
|
+
details: managedManifest.packageVersion === CCLAW_VERSION
|
|
771
|
+
? `${MANAGED_RESOURCE_MANIFEST_REL_PATH} packageVersion matches cclaw ${CCLAW_VERSION}`
|
|
772
|
+
: `${MANAGED_RESOURCE_MANIFEST_REL_PATH} packageVersion ${managedManifest.packageVersion} is stale; current cclaw is ${CCLAW_VERSION}. Run cclaw upgrade.`
|
|
773
|
+
});
|
|
774
|
+
const rawResources = toObject(rawManagedManifest)?.resources;
|
|
775
|
+
const stalePackageEntries = Array.isArray(rawResources)
|
|
776
|
+
? rawResources.flatMap((entry, index) => {
|
|
777
|
+
const obj = toObject(entry);
|
|
778
|
+
if (!obj)
|
|
779
|
+
return [];
|
|
780
|
+
const entryPath = typeof obj.path === "string" ? obj.path : `resources[${index}]`;
|
|
781
|
+
return typeof obj.packageVersion === "string" && obj.packageVersion !== CCLAW_VERSION
|
|
782
|
+
? [`${entryPath} (${obj.packageVersion})`]
|
|
783
|
+
: [];
|
|
784
|
+
})
|
|
785
|
+
: [];
|
|
786
|
+
const stale = [];
|
|
787
|
+
const missing = [];
|
|
788
|
+
for (const entry of managedManifest.resources) {
|
|
789
|
+
const filePath = path.join(projectRoot, entry.path);
|
|
790
|
+
if (!(await exists(filePath))) {
|
|
791
|
+
missing.push(entry.path);
|
|
792
|
+
continue;
|
|
793
|
+
}
|
|
794
|
+
const currentHash = hashManagedResourceContent(await fs.readFile(filePath));
|
|
795
|
+
if (currentHash !== entry.sha256) {
|
|
796
|
+
stale.push(entry.path);
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
checks.push({
|
|
800
|
+
name: "managed_resources:entry_package_versions",
|
|
801
|
+
ok: stalePackageEntries.length === 0,
|
|
802
|
+
details: stalePackageEntries.length === 0
|
|
803
|
+
? `all manifest entries match cclaw ${CCLAW_VERSION}`
|
|
804
|
+
: `manifest entries have stale packageVersion; run cclaw upgrade: ${stalePackageEntries.slice(0, 12).join(", ")}`
|
|
805
|
+
});
|
|
806
|
+
checks.push({
|
|
807
|
+
name: "managed_resources:user_modified",
|
|
808
|
+
ok: stale.length === 0,
|
|
809
|
+
details: stale.length === 0
|
|
810
|
+
? "all manifest-tracked managed files match recorded hashes"
|
|
811
|
+
: `manifest-tracked managed files have user modifications: ${stale.slice(0, 12).join(", ")}`
|
|
812
|
+
});
|
|
813
|
+
checks.push({
|
|
814
|
+
name: "managed_resources:stale_entries",
|
|
815
|
+
ok: missing.length === 0,
|
|
816
|
+
details: missing.length === 0
|
|
817
|
+
? "all manifest entries still exist"
|
|
818
|
+
: `manifest entries point to missing files: ${missing.slice(0, 12).join(", ")}`
|
|
819
|
+
});
|
|
820
|
+
const manifestPaths = new Set(managedManifest.resources.map((entry) => entry.path));
|
|
821
|
+
const candidates = [];
|
|
822
|
+
for (const relDir of [
|
|
823
|
+
`${RUNTIME_ROOT}/commands`,
|
|
824
|
+
`${RUNTIME_ROOT}/skills`,
|
|
825
|
+
`${RUNTIME_ROOT}/templates`,
|
|
826
|
+
`${RUNTIME_ROOT}/rules`,
|
|
827
|
+
`${RUNTIME_ROOT}/agents`,
|
|
828
|
+
`${RUNTIME_ROOT}/hooks`,
|
|
829
|
+
".claude/commands",
|
|
830
|
+
".cursor/commands",
|
|
831
|
+
".opencode/commands",
|
|
832
|
+
".opencode/agents",
|
|
833
|
+
".codex/agents",
|
|
834
|
+
".agents/skills",
|
|
835
|
+
".claude/hooks",
|
|
836
|
+
".cursor",
|
|
837
|
+
".codex",
|
|
838
|
+
".opencode/plugins"
|
|
839
|
+
]) {
|
|
840
|
+
await walkGeneratedCandidates(projectRoot, relDir, candidates);
|
|
841
|
+
}
|
|
842
|
+
for (const rel of ["AGENTS.md", "CLAUDE.md"]) {
|
|
843
|
+
if ((await exists(path.join(projectRoot, rel))) && isManagedGeneratedPath(rel)) {
|
|
844
|
+
candidates.push(rel);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
const orphaned = [...new Set(candidates)].filter((rel) => !manifestPaths.has(rel)).sort();
|
|
848
|
+
checks.push({
|
|
849
|
+
name: "managed_resources:orphaned_generated_files",
|
|
850
|
+
ok: orphaned.length === 0,
|
|
851
|
+
details: orphaned.length === 0
|
|
852
|
+
? "no orphaned generated files detected across known cclaw surfaces"
|
|
853
|
+
: `warning: generated-looking files are not tracked in manifest: ${orphaned.slice(0, 12).join(", ")}`
|
|
854
|
+
});
|
|
855
|
+
}
|
|
693
856
|
let configuredHarnesses = [];
|
|
694
857
|
let parsedConfig = null;
|
|
858
|
+
const configFileExists = await exists(path.join(projectRoot, RUNTIME_ROOT, "config.yaml"));
|
|
859
|
+
if (!configFileExists) {
|
|
860
|
+
const detectedHarnesses = await detectHarnesses(projectRoot).catch(() => []);
|
|
861
|
+
checks.push({
|
|
862
|
+
name: "config:present",
|
|
863
|
+
ok: detectedHarnesses.length === 0,
|
|
864
|
+
details: detectedHarnesses.length > 0
|
|
865
|
+
? `${RUNTIME_ROOT}/config.yaml is missing but harness markers were detected (${detectedHarnesses.join(", ")}). Run cclaw sync --harnesses=${detectedHarnesses.join(",")} or cclaw sync --interactive.`
|
|
866
|
+
: `${RUNTIME_ROOT}/config.yaml missing and no harness markers were detected; run cclaw init or cclaw sync when ready.`
|
|
867
|
+
});
|
|
868
|
+
}
|
|
695
869
|
try {
|
|
696
870
|
const config = await readConfig(projectRoot);
|
|
697
871
|
parsedConfig = config;
|
|
@@ -808,17 +982,17 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
808
982
|
const content = await fs.readFile(agentsFile, "utf8");
|
|
809
983
|
const hasMarkers = content.includes(CCLAW_MARKER_START) && content.includes(CCLAW_MARKER_END);
|
|
810
984
|
const hasCcCommand = content.includes("/cc");
|
|
811
|
-
const hasCcNext = content.includes("/cc-next");
|
|
812
985
|
const hasCcIdeate = content.includes("/cc-ideate");
|
|
813
|
-
const
|
|
986
|
+
const hasCcCancel = content.includes("/cc-cancel");
|
|
987
|
+
const omitsFinish = !content.includes("/cc-finish");
|
|
814
988
|
const hasVerification = content.includes("Verification Discipline");
|
|
815
989
|
const hasMinimalMarker = content.includes("intentionally minimal for cross-project use");
|
|
816
990
|
const hasMetaSkillPointer = content.includes(".cclaw/skills/using-cclaw/SKILL.md");
|
|
817
991
|
agentsBlockOk = hasMarkers
|
|
818
992
|
&& hasCcCommand
|
|
819
|
-
&& hasCcNext
|
|
820
993
|
&& hasCcIdeate
|
|
821
|
-
&&
|
|
994
|
+
&& hasCcCancel
|
|
995
|
+
&& omitsFinish
|
|
822
996
|
&& hasVerification
|
|
823
997
|
&& hasMinimalMarker
|
|
824
998
|
&& hasMetaSkillPointer;
|
|
@@ -828,7 +1002,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
828
1002
|
ok: agentsBlockOk,
|
|
829
1003
|
details: `${agentsFile} must contain the managed cclaw marker block with routing, verification, and minimal detail pointer`
|
|
830
1004
|
});
|
|
831
|
-
for (const cmd of ["start", "next", "ideate", "view"]) {
|
|
1005
|
+
for (const cmd of ["start", "next", "ideate", "view", "cancel"]) {
|
|
832
1006
|
const cmdPath = path.join(projectRoot, RUNTIME_ROOT, "commands", `${cmd}.md`);
|
|
833
1007
|
checks.push({
|
|
834
1008
|
name: `utility_command:${cmd}`,
|
|
@@ -846,7 +1020,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
846
1020
|
checks.push({
|
|
847
1021
|
name: `stage_command:${stage}`,
|
|
848
1022
|
ok: stageCommandOk,
|
|
849
|
-
details: `${cmdPath} must be a thin shim to ${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md and /cc
|
|
1023
|
+
details: `${cmdPath} must be a thin shim to ${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md and /cc`
|
|
850
1024
|
});
|
|
851
1025
|
}
|
|
852
1026
|
// Utility skills
|
|
@@ -854,6 +1028,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
854
1028
|
["learnings", "learnings"],
|
|
855
1029
|
["flow-ideate", "flow-ideate"],
|
|
856
1030
|
["flow-view", "flow-view"],
|
|
1031
|
+
["flow-cancel", "flow-cancel"],
|
|
857
1032
|
["subagent-dev", "sdd"],
|
|
858
1033
|
["parallel-dispatch", "parallel-agents"],
|
|
859
1034
|
["session", "session"],
|
|
@@ -1090,7 +1265,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1090
1265
|
cursorRuleOk =
|
|
1091
1266
|
content.includes("cclaw-managed-cursor-workflow-rule") &&
|
|
1092
1267
|
content.includes(".cclaw/state/flow-state.json") &&
|
|
1093
|
-
content.includes("/cc
|
|
1268
|
+
content.includes("/cc");
|
|
1094
1269
|
}
|
|
1095
1270
|
checks.push({
|
|
1096
1271
|
name: "rules:cursor:workflow",
|
|
@@ -2016,6 +2191,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
2016
2191
|
severity: check.severity ?? metadata.severity,
|
|
2017
2192
|
summary: check.summary ?? metadata.summary,
|
|
2018
2193
|
fix: check.fix ?? metadata.fix,
|
|
2194
|
+
actionGroup: check.actionGroup ?? metadata.actionGroup,
|
|
2019
2195
|
docRef: check.docRef ?? metadata.docRef
|
|
2020
2196
|
};
|
|
2021
2197
|
});
|
package/dist/flow-state.d.ts
CHANGED
|
@@ -33,7 +33,7 @@ export interface RetroState {
|
|
|
33
33
|
* Ship closeout substate machine.
|
|
34
34
|
*
|
|
35
35
|
* After ship completes, cclaw auto-chains retro → compound → archive.
|
|
36
|
-
* Each step is interruptible: `/cc
|
|
36
|
+
* Each step is interruptible: `/cc` reads `shipSubstate` and resumes
|
|
37
37
|
* from the correct step even across sessions.
|
|
38
38
|
*
|
|
39
39
|
* - `idle` — ship not complete, or closeout not yet started.
|
package/dist/flow-state.js
CHANGED
|
@@ -6,7 +6,7 @@ export const FLOW_STATE_SCHEMA_VERSION = 1;
|
|
|
6
6
|
* Ship closeout substate machine.
|
|
7
7
|
*
|
|
8
8
|
* After ship completes, cclaw auto-chains retro → compound → archive.
|
|
9
|
-
* Each step is interruptible: `/cc
|
|
9
|
+
* Each step is interruptible: `/cc` reads `shipSubstate` and resumes
|
|
10
10
|
* from the correct step even across sessions.
|
|
11
11
|
*
|
|
12
12
|
* - `idle` — ship not complete, or closeout not yet started.
|
package/dist/fs-utils.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { getActiveManagedResourceSession } from "./managed-resources.js";
|
|
3
4
|
export async function ensureDir(dirPath) {
|
|
4
5
|
await fs.mkdir(dirPath, { recursive: true });
|
|
5
6
|
}
|
|
@@ -83,6 +84,11 @@ export async function withDirectoryLock(lockPath, fn, options = {}) {
|
|
|
83
84
|
}
|
|
84
85
|
}
|
|
85
86
|
export async function writeFileSafe(filePath, content, options = {}) {
|
|
87
|
+
const managedSession = getActiveManagedResourceSession();
|
|
88
|
+
if (managedSession?.shouldManage(filePath)) {
|
|
89
|
+
await managedSession.writeFileSafe(filePath, content, options);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
86
92
|
await ensureDir(path.dirname(filePath));
|
|
87
93
|
const tempPath = path.join(path.dirname(filePath), `.${path.basename(filePath)}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`);
|
|
88
94
|
const targetMode = options.mode;
|
|
@@ -32,7 +32,7 @@ export type SubagentFallback =
|
|
|
32
32
|
* directories under a skills root (Codex CLI ≥0.89, Jan 2026). cclaw
|
|
33
33
|
* writes `<commandDir>/<skillName>/SKILL.md` and the agent invokes it
|
|
34
34
|
* either via `/use <skillName>` or via automatic description matching
|
|
35
|
-
* when the user's text mentions `/cc`, `/cc-
|
|
35
|
+
* when the user's text mentions `/cc`, `/cc-ideate`, or `/cc-cancel`.
|
|
36
36
|
*/
|
|
37
37
|
export type ShimKind = "command" | "skill";
|
|
38
38
|
export interface HarnessAdapter {
|
|
@@ -47,7 +47,7 @@ export interface HarnessAdapter {
|
|
|
47
47
|
* Root directory where cclaw writes `/cc*` entry points.
|
|
48
48
|
*
|
|
49
49
|
* - For `shimKind: "command"` this is the directory containing flat
|
|
50
|
-
* markdown files (`<commandDir>/cc.md`, `<commandDir>/cc-
|
|
50
|
+
* markdown files (`<commandDir>/cc.md`, `<commandDir>/cc-ideate.md`, …).
|
|
51
51
|
* - For `shimKind: "skill"` this is the skills root that contains
|
|
52
52
|
* per-skill subdirectories (`<commandDir>/<skillName>/SKILL.md`).
|
|
53
53
|
*/
|