cclaw-cli 0.51.27 → 0.51.29
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/artifact-linter.js +73 -16
- package/dist/cli.d.ts +17 -1
- package/dist/cli.js +185 -49
- 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/finish-command.d.ts +2 -0
- package/dist/content/finish-command.js +26 -0
- 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/next-command.js +17 -13
- package/dist/content/node-hooks.js +22 -6
- package/dist/content/opencode-plugin.js +1 -1
- package/dist/content/stages/review.js +1 -1
- package/dist/content/stages/tdd.js +1 -1
- package/dist/content/start-command.js +6 -5
- package/dist/content/state-contracts.js +1 -1
- package/dist/content/status-command.js +4 -3
- 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 +183 -2
- package/dist/fs-utils.js +6 -0
- package/dist/harness-adapters.js +29 -5
- package/dist/install.d.ts +4 -1
- package/dist/install.js +37 -4
- package/dist/internal/advance-stage.js +6 -6
- package/dist/managed-resources.d.ts +53 -0
- package/dist/managed-resources.js +289 -0
- package/dist/run-archive.d.ts +8 -0
- package/dist/run-archive.js +19 -5
- 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
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;
|
|
@@ -811,6 +985,8 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
811
985
|
const hasCcNext = content.includes("/cc-next");
|
|
812
986
|
const hasCcIdeate = content.includes("/cc-ideate");
|
|
813
987
|
const hasCcView = content.includes("/cc-view");
|
|
988
|
+
const hasCcFinish = content.includes("/cc-finish");
|
|
989
|
+
const hasCcCancel = content.includes("/cc-cancel");
|
|
814
990
|
const hasVerification = content.includes("Verification Discipline");
|
|
815
991
|
const hasMinimalMarker = content.includes("intentionally minimal for cross-project use");
|
|
816
992
|
const hasMetaSkillPointer = content.includes(".cclaw/skills/using-cclaw/SKILL.md");
|
|
@@ -819,6 +995,8 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
819
995
|
&& hasCcNext
|
|
820
996
|
&& hasCcIdeate
|
|
821
997
|
&& hasCcView
|
|
998
|
+
&& hasCcFinish
|
|
999
|
+
&& hasCcCancel
|
|
822
1000
|
&& hasVerification
|
|
823
1001
|
&& hasMinimalMarker
|
|
824
1002
|
&& hasMetaSkillPointer;
|
|
@@ -828,7 +1006,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
828
1006
|
ok: agentsBlockOk,
|
|
829
1007
|
details: `${agentsFile} must contain the managed cclaw marker block with routing, verification, and minimal detail pointer`
|
|
830
1008
|
});
|
|
831
|
-
for (const cmd of ["start", "next", "ideate", "view"]) {
|
|
1009
|
+
for (const cmd of ["start", "next", "ideate", "view", "finish", "cancel"]) {
|
|
832
1010
|
const cmdPath = path.join(projectRoot, RUNTIME_ROOT, "commands", `${cmd}.md`);
|
|
833
1011
|
checks.push({
|
|
834
1012
|
name: `utility_command:${cmd}`,
|
|
@@ -854,6 +1032,8 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
854
1032
|
["learnings", "learnings"],
|
|
855
1033
|
["flow-ideate", "flow-ideate"],
|
|
856
1034
|
["flow-view", "flow-view"],
|
|
1035
|
+
["flow-finish", "flow-finish"],
|
|
1036
|
+
["flow-cancel", "flow-cancel"],
|
|
857
1037
|
["subagent-dev", "sdd"],
|
|
858
1038
|
["parallel-dispatch", "parallel-agents"],
|
|
859
1039
|
["session", "session"],
|
|
@@ -2016,6 +2196,7 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
2016
2196
|
severity: check.severity ?? metadata.severity,
|
|
2017
2197
|
summary: check.summary ?? metadata.summary,
|
|
2018
2198
|
fix: check.fix ?? metadata.fix,
|
|
2199
|
+
actionGroup: check.actionGroup ?? metadata.actionGroup,
|
|
2019
2200
|
docRef: check.docRef ?? metadata.docRef
|
|
2020
2201
|
};
|
|
2021
2202
|
});
|
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;
|
package/dist/harness-adapters.js
CHANGED
|
@@ -35,6 +35,20 @@ const UTILITY_SHIMS = [
|
|
|
35
35
|
command: "view",
|
|
36
36
|
skillFolder: "flow-view",
|
|
37
37
|
commandFile: "view.md"
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
fileName: "cc-finish.md",
|
|
41
|
+
skillName: "cc-finish",
|
|
42
|
+
command: "finish",
|
|
43
|
+
skillFolder: "flow-finish",
|
|
44
|
+
commandFile: "finish.md"
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
fileName: "cc-cancel.md",
|
|
48
|
+
skillName: "cc-cancel",
|
|
49
|
+
command: "cancel",
|
|
50
|
+
skillFolder: "flow-cancel",
|
|
51
|
+
commandFile: "cancel.md"
|
|
38
52
|
}
|
|
39
53
|
];
|
|
40
54
|
/** Skill-kind shim name for the root `/cc` entry point. */
|
|
@@ -179,9 +193,9 @@ export function harnessDispatchSurface(harnessId) {
|
|
|
179
193
|
case "cursor":
|
|
180
194
|
return "Use Cursor Subagent/Task with a generic subagent_type (explore for read-only mapping, generalPurpose for broader work, shell/browser-use when specifically needed) and paste the cclaw role prompt; record fulfillmentMode: \"generic-dispatch\" with evidenceRefs.";
|
|
181
195
|
case "opencode":
|
|
182
|
-
return "Use OpenCode subagents: invoke the generated .opencode/agents/<agent>.md agent via Task or @<agent>; record scheduled/launched/acknowledged/completed events with spanId+dispatchId before claiming fulfillmentMode: \"isolated\".";
|
|
196
|
+
return "Use OpenCode subagents: invoke the generated .opencode/agents/<agent>.md agent via Task or @<agent>; if agents or plugin registration are missing, run `cclaw sync` and check opencode.json(.c) plugin registration with `cclaw doctor --explain`; record scheduled/launched/acknowledged/completed events with spanId+dispatchId before claiming fulfillmentMode: \"isolated\".";
|
|
183
197
|
case "codex":
|
|
184
|
-
return "Use Codex native subagents: ask Codex to spawn the generated .codex/agents/<agent>.toml agent(s) by name; record scheduled/launched/acknowledged/completed events with spanId+dispatchId before claiming fulfillmentMode: \"isolated\".";
|
|
198
|
+
return "Use Codex native subagents: ask Codex to spawn the generated .codex/agents/<agent>.toml agent(s) by name; if hooks are inert, set `[features] codex_hooks = true` in ~/.codex/config.toml or rerun init/sync repair, then `cclaw doctor --explain`; record scheduled/launched/acknowledged/completed events with spanId+dispatchId before claiming fulfillmentMode: \"isolated\".";
|
|
185
199
|
}
|
|
186
200
|
}
|
|
187
201
|
/**
|
|
@@ -362,13 +376,15 @@ When in doubt, prefer **non-trivial** — the quick track is opt-in and only saf
|
|
|
362
376
|
| \`/cc-next\` | **Progression.** Advances to the next stage when current is complete. |
|
|
363
377
|
| \`/cc-ideate\` | **Ideate mode.** Generates a ranked repo-improvement backlog before implementation. |
|
|
364
378
|
| \`/cc-view\` | **Read-only router.** Unified entry for status/tree/diff views. |
|
|
379
|
+
| \`/cc-finish\` | **Successful closeout.** Archives a completed run after strict ship closeout gates. |
|
|
380
|
+
| \`/cc-cancel\` | **Non-completion closeout.** Archives a cancelled/abandoned run with a required reason. |
|
|
365
381
|
|
|
366
382
|
Knowledge capture and curation run automatically as part of stage completion
|
|
367
383
|
protocols via the internal \`learnings\` skill — no user-facing command.
|
|
368
384
|
Reusable entries land in \`.cclaw/knowledge.jsonl\` as strict JSONL with
|
|
369
385
|
\`type\`, \`trigger\`, \`action\`, and \`origin_run\` metadata.
|
|
370
386
|
|
|
371
|
-
**Stage order:** brainstorm > scope > design > spec > plan > tdd > review > ship, then closeout: retro > compound > archive.
|
|
387
|
+
**Stage order:** brainstorm > scope > design > spec > plan > tdd > review > ship, then closeout: retro > compound > archive. Use \`/cc-finish\` for completed runs and \`/cc-cancel\` for cancelled/abandoned runs.
|
|
372
388
|
\`/cc-next\` loads the right stage skill automatically and also drives post-ship closeout. Gates must pass before handoff.
|
|
373
389
|
|
|
374
390
|
### Verification Discipline
|
|
@@ -391,8 +407,8 @@ If the same approach fails three times in a row (same command, same finding, sam
|
|
|
391
407
|
|
|
392
408
|
OpenAI Codex CLI has **no native \`/cc\` slash command** (custom prompts
|
|
393
409
|
were deprecated in v0.89, Jan 2026). The \`/cc\`, \`/cc-next\`,
|
|
394
|
-
\`/cc-ideate\`, \`/cc-view
|
|
395
|
-
Codex they map onto skills cclaw installs at
|
|
410
|
+
\`/cc-ideate\`, \`/cc-view\`, \`/cc-finish\`, and \`/cc-cancel\`
|
|
411
|
+
tokens above describe intent — in Codex they map onto skills cclaw installs at
|
|
396
412
|
\`.agents/skills/cc*/SKILL.md\`. Activate one of two ways:
|
|
397
413
|
|
|
398
414
|
- Type \`/use cc\` (or \`cc-next\`, etc.) at Codex's prompt.
|
|
@@ -474,6 +490,10 @@ function utilityShimBehavior(command) {
|
|
|
474
490
|
return "This is an ideation command, not a flow stage. It may write ideation artifacts/seeds but does not advance flow state.";
|
|
475
491
|
case "view":
|
|
476
492
|
return "This is a read-only view command, not a flow stage. It never mutates flow state.";
|
|
493
|
+
case "finish":
|
|
494
|
+
return "This is a successful closeout utility, not a flow stage. It archives a completed run after ship closeout gates pass and records completed disposition semantics.";
|
|
495
|
+
case "cancel":
|
|
496
|
+
return "This is a non-completion closeout utility, not a flow stage. It requires a reason and archives cancelled or abandoned work without presenting it as completed.";
|
|
477
497
|
default:
|
|
478
498
|
return "This is a utility command, not a flow stage.";
|
|
479
499
|
}
|
|
@@ -555,6 +575,10 @@ function codexSkillDescription(command) {
|
|
|
555
575
|
return `Read-only repo-improvement ideate mode for cclaw. Use when the user types \`/cc-ideate\` or asks to "ideate", "scan the repo for TODOs/tech debt", "generate a backlog", or wants a ranked list of candidate ideas before committing to a single flow. Does not mutate \`.cclaw/state/flow-state.json\`.`;
|
|
556
576
|
case "view":
|
|
557
577
|
return `Read-only router for cclaw flow views. Use when the user types \`/cc-view\`, \`/cc-view status\`, \`/cc-view tree\`, \`/cc-view diff\`, or asks to "show cclaw status", "show the flow tree", "diff flow state", or wants a snapshot without mutation.`;
|
|
578
|
+
case "finish":
|
|
579
|
+
return `Finish a completed cclaw run. Use when the user types \`/cc-finish\` or asks to finish, complete, close out, or archive a successful run. Runs cclaw archive with completed disposition after strict ship closeout gates.`;
|
|
580
|
+
case "cancel":
|
|
581
|
+
return `Cancel or abandon the active cclaw run. Use when the user types \`/cc-cancel\` or asks to cancel, abandon, stop, discard, or reset an unfinished run. Requires a reason and archives with cancelled/abandoned disposition.`;
|
|
558
582
|
default:
|
|
559
583
|
return `Generated cclaw skill for ${command}.`;
|
|
560
584
|
}
|
package/dist/install.d.ts
CHANGED
|
@@ -4,8 +4,11 @@ export interface InitOptions {
|
|
|
4
4
|
harnesses?: HarnessId[];
|
|
5
5
|
track?: FlowTrack;
|
|
6
6
|
}
|
|
7
|
+
export interface SyncOptions {
|
|
8
|
+
harnesses?: HarnessId[];
|
|
9
|
+
}
|
|
7
10
|
export declare function initCclaw(options: InitOptions): Promise<void>;
|
|
8
|
-
export declare function syncCclaw(projectRoot: string): Promise<void>;
|
|
11
|
+
export declare function syncCclaw(projectRoot: string, options?: SyncOptions): Promise<void>;
|
|
9
12
|
/**
|
|
10
13
|
* Refresh generated files in `.cclaw/` without touching user-authored
|
|
11
14
|
* artifacts, state, or custom config keys. Only the `version` + `flowVersion`
|