cclaw-cli 0.51.24 → 0.51.26
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 +135 -414
- package/dist/artifact-linter.js +10 -6
- package/dist/config.d.ts +1 -1
- package/dist/config.js +28 -3
- package/dist/content/core-agents.d.ts +110 -0
- package/dist/content/core-agents.js +255 -3
- package/dist/content/examples.js +8 -5
- package/dist/content/harness-doc.d.ts +1 -0
- package/dist/content/harness-doc.js +3 -0
- package/dist/content/hooks.d.ts +1 -0
- package/dist/content/hooks.js +189 -0
- package/dist/content/next-command.js +10 -6
- package/dist/content/reference-patterns.d.ts +18 -0
- package/dist/content/reference-patterns.js +391 -0
- package/dist/content/skills.js +42 -36
- package/dist/content/stage-common-guidance.js +19 -3
- package/dist/content/stage-schema.d.ts +12 -0
- package/dist/content/stage-schema.js +184 -28
- package/dist/content/stages/_lint-metadata/index.js +3 -2
- package/dist/content/stages/brainstorm.js +7 -3
- package/dist/content/stages/design.js +12 -3
- package/dist/content/stages/review.js +7 -5
- package/dist/content/stages/schema-types.d.ts +9 -2
- package/dist/content/stages/scope.js +8 -2
- package/dist/content/stages/ship.js +3 -2
- package/dist/content/stages/tdd.js +18 -13
- package/dist/content/start-command.js +3 -2
- package/dist/content/status-command.js +17 -6
- package/dist/content/subagents.js +286 -40
- package/dist/content/templates.js +64 -3
- package/dist/content/tree-command.js +7 -1
- package/dist/delegation.d.ts +34 -1
- package/dist/delegation.js +168 -8
- package/dist/doctor-registry.js +9 -0
- package/dist/doctor.js +121 -6
- package/dist/gate-evidence.js +25 -2
- package/dist/harness-adapters.d.ts +6 -0
- package/dist/harness-adapters.js +28 -4
- package/dist/install.js +5 -10
- package/dist/internal/advance-stage.js +179 -26
- package/dist/run-persistence.js +21 -3
- package/dist/tdd-verification-evidence.d.ts +17 -0
- package/dist/tdd-verification-evidence.js +43 -0
- package/dist/types.d.ts +10 -0
- package/package.json +1 -1
package/dist/doctor.js
CHANGED
|
@@ -13,7 +13,7 @@ import { policyChecks } from "./policy.js";
|
|
|
13
13
|
import { CorruptFlowStateError, readFlowState } from "./runs.js";
|
|
14
14
|
import { createInitialFlowState, skippedStagesForTrack } from "./flow-state.js";
|
|
15
15
|
import { FLOW_STAGES, TRACK_STAGES } from "./types.js";
|
|
16
|
-
import { checkMandatoryDelegations } from "./delegation.js";
|
|
16
|
+
import { checkMandatoryDelegations, readDelegationEvents } from "./delegation.js";
|
|
17
17
|
import { buildTraceMatrix } from "./trace-matrix.js";
|
|
18
18
|
import { classifyReconciliationNotices, reconcileAndWriteCurrentStageGateCatalog, readReconciliationNotices, RECONCILIATION_NOTICES_REL_PATH, verifyCompletedStagesGateClosure, verifyCurrentStageGateEvidence } from "./gate-evidence.js";
|
|
19
19
|
import { parseTddCycleLog, validateTddCycleOrder } from "./tdd-cycle.js";
|
|
@@ -131,6 +131,20 @@ async function generatedCliEntrypointsOk(projectRoot) {
|
|
|
131
131
|
: "local CLI entrypoint check skipped because generated hook scripts are absent"
|
|
132
132
|
};
|
|
133
133
|
}
|
|
134
|
+
function expectedArtifactPrefix(stage) {
|
|
135
|
+
const index = FLOW_STAGES.indexOf(stage);
|
|
136
|
+
return `${String(index + 1).padStart(2, "0")}-`;
|
|
137
|
+
}
|
|
138
|
+
function artifactStageFromFileName(fileName) {
|
|
139
|
+
if (!fileName.endsWith(".md"))
|
|
140
|
+
return null;
|
|
141
|
+
for (const stage of FLOW_STAGES) {
|
|
142
|
+
if (fileName.startsWith(expectedArtifactPrefix(stage))) {
|
|
143
|
+
return stage;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return null;
|
|
147
|
+
}
|
|
134
148
|
function extractUserPromptFromIdeaArtifact(markdown) {
|
|
135
149
|
const normalized = markdown.replace(/\r\n?/gu, "\n");
|
|
136
150
|
const heading = /^##\s+User prompt\s*$/imu.exec(normalized);
|
|
@@ -291,6 +305,20 @@ function normalizeOpenCodePluginEntry(entry) {
|
|
|
291
305
|
}
|
|
292
306
|
return null;
|
|
293
307
|
}
|
|
308
|
+
function generatedAgentShape(content, kind, agentName) {
|
|
309
|
+
if (kind === "opencode") {
|
|
310
|
+
return content.includes("mode: subagent") && content.includes(`# ${agentName}`) && content.includes("STRICT_RETURN_SCHEMA");
|
|
311
|
+
}
|
|
312
|
+
return content.includes(`name = "${agentName}"`) && content.includes("developer_instructions") && content.includes("STRICT_RETURN_SCHEMA");
|
|
313
|
+
}
|
|
314
|
+
function harnessRealityLabel(harness) {
|
|
315
|
+
const adapter = HARNESS_ADAPTERS[harness];
|
|
316
|
+
const declaredSupport = adapter.capabilities.nativeSubagentDispatch;
|
|
317
|
+
const runtimeLaunch = harness === "opencode" || harness === "codex" ? "prompt-level launch" : declaredSupport === "generic" ? "generic Task launch" : "native tool launch";
|
|
318
|
+
const proofRequired = adapter.capabilities.subagentFallback === "native" ? "dispatchId+spanId+ack" : "evidenceRefs";
|
|
319
|
+
const proofSource = harness === "opencode" ? ".opencode/agents + delegation-events.jsonl" : harness === "codex" ? ".codex/agents + delegation-events.jsonl" : ".cclaw/state/delegation-log.json";
|
|
320
|
+
return `declaredSupport=${declaredSupport}; runtimeLaunch=${runtimeLaunch}; proofRequired=${proofRequired}; proofSource=${proofSource}`;
|
|
321
|
+
}
|
|
294
322
|
const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
|
|
295
323
|
function opencodeConfigCandidates(projectRoot) {
|
|
296
324
|
return [
|
|
@@ -766,6 +794,14 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
766
794
|
}
|
|
767
795
|
}
|
|
768
796
|
}
|
|
797
|
+
for (const harness of configuredHarnesses) {
|
|
798
|
+
checks.push({
|
|
799
|
+
name: `harness:reality:${harness}`,
|
|
800
|
+
ok: true,
|
|
801
|
+
severity: "info",
|
|
802
|
+
details: harnessRealityLabel(harness)
|
|
803
|
+
});
|
|
804
|
+
}
|
|
769
805
|
const agentsFile = path.join(projectRoot, "AGENTS.md");
|
|
770
806
|
let agentsBlockOk = false;
|
|
771
807
|
if (await exists(agentsFile)) {
|
|
@@ -870,12 +906,39 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
870
906
|
details: agentPath
|
|
871
907
|
});
|
|
872
908
|
}
|
|
909
|
+
for (const agent of CCLAW_AGENTS) {
|
|
910
|
+
if (configuredHarnesses.includes("opencode")) {
|
|
911
|
+
const agentPath = path.join(projectRoot, ".opencode", "agents", `${agent.name}.md`);
|
|
912
|
+
let ok = false;
|
|
913
|
+
if (await exists(agentPath)) {
|
|
914
|
+
ok = generatedAgentShape(await fs.readFile(agentPath, "utf8"), "opencode", agent.name);
|
|
915
|
+
}
|
|
916
|
+
checks.push({
|
|
917
|
+
name: `agent:opencode:${agent.name}:shape`,
|
|
918
|
+
ok,
|
|
919
|
+
details: `${agentPath} must be a generated OpenCode subagent with mode: subagent and strict return schema`
|
|
920
|
+
});
|
|
921
|
+
}
|
|
922
|
+
if (configuredHarnesses.includes("codex")) {
|
|
923
|
+
const agentPath = path.join(projectRoot, ".codex", "agents", `${agent.name}.toml`);
|
|
924
|
+
let ok = false;
|
|
925
|
+
if (await exists(agentPath)) {
|
|
926
|
+
ok = generatedAgentShape(await fs.readFile(agentPath, "utf8"), "codex", agent.name);
|
|
927
|
+
}
|
|
928
|
+
checks.push({
|
|
929
|
+
name: `agent:codex:${agent.name}:shape`,
|
|
930
|
+
ok,
|
|
931
|
+
details: `${agentPath} must be a generated Codex custom agent TOML with developer_instructions and strict return schema`
|
|
932
|
+
});
|
|
933
|
+
}
|
|
934
|
+
}
|
|
873
935
|
// Hook scripts
|
|
874
936
|
for (const script of [
|
|
875
937
|
"run-hook.mjs",
|
|
876
938
|
"run-hook.cmd",
|
|
877
939
|
"stage-complete.mjs",
|
|
878
940
|
"start-flow.mjs",
|
|
941
|
+
"delegation-record.mjs",
|
|
879
942
|
"opencode-plugin.mjs"
|
|
880
943
|
]) {
|
|
881
944
|
const scriptPath = path.join(projectRoot, RUNTIME_ROOT, "hooks", script);
|
|
@@ -1630,13 +1693,21 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1630
1693
|
});
|
|
1631
1694
|
const artifactsRoot = path.join(projectRoot, RUNTIME_ROOT, "artifacts");
|
|
1632
1695
|
let artifactPlaceholderHits = [];
|
|
1696
|
+
let duplicateArtifactGroups = [];
|
|
1633
1697
|
if (await exists(artifactsRoot)) {
|
|
1634
1698
|
try {
|
|
1635
1699
|
const entries = await fs.readdir(artifactsRoot, { withFileTypes: true });
|
|
1636
1700
|
const placeholderPattern = /\b(?:TODO|TBD|FIXME)\b|<fill-in>|<your-.*-here>/giu;
|
|
1701
|
+
const stageArtifactFiles = new Map();
|
|
1637
1702
|
for (const entry of entries) {
|
|
1638
1703
|
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
1639
1704
|
continue;
|
|
1705
|
+
const stageForArtifact = artifactStageFromFileName(entry.name);
|
|
1706
|
+
if (stageForArtifact) {
|
|
1707
|
+
const files = stageArtifactFiles.get(stageForArtifact) ?? [];
|
|
1708
|
+
files.push(entry.name);
|
|
1709
|
+
stageArtifactFiles.set(stageForArtifact, files);
|
|
1710
|
+
}
|
|
1640
1711
|
const filePath = path.join(artifactsRoot, entry.name);
|
|
1641
1712
|
const content = await fs.readFile(filePath, "utf8");
|
|
1642
1713
|
const matchCount = (content.match(placeholderPattern) ?? []).length;
|
|
@@ -1644,9 +1715,13 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1644
1715
|
artifactPlaceholderHits.push(`${entry.name}:${matchCount}`);
|
|
1645
1716
|
}
|
|
1646
1717
|
}
|
|
1718
|
+
duplicateArtifactGroups = [...stageArtifactFiles.entries()]
|
|
1719
|
+
.filter(([, files]) => files.length > 1)
|
|
1720
|
+
.map(([stageName, files]) => `${stageName}: ${files.sort().join(", ")}`);
|
|
1647
1721
|
}
|
|
1648
1722
|
catch {
|
|
1649
1723
|
artifactPlaceholderHits = [];
|
|
1724
|
+
duplicateArtifactGroups = [];
|
|
1650
1725
|
}
|
|
1651
1726
|
}
|
|
1652
1727
|
checks.push({
|
|
@@ -1656,13 +1731,20 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1656
1731
|
? "no TODO/TBD/FIXME placeholder markers found in active artifacts"
|
|
1657
1732
|
: `warning: placeholder markers detected in active artifacts (${artifactPlaceholderHits.join(", ")}). Clear before marking completion.`
|
|
1658
1733
|
});
|
|
1734
|
+
checks.push({
|
|
1735
|
+
name: "warning:artifacts:duplicate_stage_artifacts",
|
|
1736
|
+
ok: duplicateArtifactGroups.length === 0,
|
|
1737
|
+
details: duplicateArtifactGroups.length === 0
|
|
1738
|
+
? "no duplicate stage artifacts detected in active artifacts"
|
|
1739
|
+
: `warning: duplicate stage artifacts detected (${duplicateArtifactGroups.join("; ")}). The resolver uses the newest matching file; archive or rename stale copies to avoid ambiguous operator handoff.`
|
|
1740
|
+
});
|
|
1659
1741
|
const staleStages = Object.keys(flowState.staleStages).filter((value) => FLOW_STAGES.includes(value));
|
|
1660
1742
|
checks.push({
|
|
1661
1743
|
name: "state:stale_stages_resolved",
|
|
1662
1744
|
ok: staleStages.length === 0,
|
|
1663
1745
|
details: staleStages.length === 0
|
|
1664
1746
|
? "no stale stages pending acknowledgement"
|
|
1665
|
-
: `stale stages pending acknowledgement: ${staleStages.join(", ")}
|
|
1747
|
+
: `stale stages pending acknowledgement: ${staleStages.join(", ")}. Re-run the current stale stage, then clear it with cclaw internal rewind --ack ${flowState.currentStage}.`
|
|
1666
1748
|
});
|
|
1667
1749
|
const retroGateStatus = await evaluateRetroGate(projectRoot, flowState);
|
|
1668
1750
|
checks.push({
|
|
@@ -1732,18 +1814,51 @@ export async function doctorChecks(projectRoot, options = {}) {
|
|
|
1732
1814
|
ok: archiveIntegrity.ok,
|
|
1733
1815
|
details: archiveIntegrity.details
|
|
1734
1816
|
});
|
|
1817
|
+
const currentGateState = flowState.stageGateCatalog[flowState.currentStage];
|
|
1818
|
+
const currentStageUntouched = flowState.completedStages.length === 0 &&
|
|
1819
|
+
flowState.rewinds.length === 0 &&
|
|
1820
|
+
Object.keys(flowState.guardEvidence).length === 0 &&
|
|
1821
|
+
(currentGateState?.passed.length ?? 0) === 0 &&
|
|
1822
|
+
(currentGateState?.blocked.length ?? 0) === 0;
|
|
1735
1823
|
const delegation = await checkMandatoryDelegations(projectRoot, flowState.currentStage, {
|
|
1736
1824
|
repairFeatureSystem: false
|
|
1737
1825
|
});
|
|
1826
|
+
const delegationEvents = await readDelegationEvents(projectRoot);
|
|
1827
|
+
const delegationSatisfiedForDoctor = currentStageUntouched || delegation.satisfied;
|
|
1738
1828
|
const missingEvidenceNote = delegation.missingEvidence && delegation.missingEvidence.length > 0
|
|
1739
1829
|
? ` (role-switch rows without evidenceRefs: ${delegation.missingEvidence.join(", ")})`
|
|
1740
1830
|
: "";
|
|
1741
1831
|
checks.push({
|
|
1742
1832
|
name: "delegation:mandatory:current_stage",
|
|
1743
|
-
ok:
|
|
1744
|
-
details:
|
|
1745
|
-
? `
|
|
1746
|
-
:
|
|
1833
|
+
ok: delegationSatisfiedForDoctor,
|
|
1834
|
+
details: currentStageUntouched
|
|
1835
|
+
? `mandatory delegation check deferred for untouched stage "${flowState.currentStage}"; stage-complete enforces it when work begins`
|
|
1836
|
+
: delegation.satisfied
|
|
1837
|
+
? `All mandatory delegations satisfied for stage "${flowState.currentStage}" (mode: ${delegation.expectedMode})`
|
|
1838
|
+
: `Missing mandatory delegations for stage "${flowState.currentStage}": ${delegation.missing.join(", ")}${missingEvidenceNote}; missingDispatchProof=${delegation.missingDispatchProof.join(", ")}; staleWorkers=${delegation.staleWorkers.join(", ")}; corruptEventLines=${delegation.corruptEventLines.join(", ")}`
|
|
1839
|
+
});
|
|
1840
|
+
checks.push({
|
|
1841
|
+
name: "delegation:events:parse",
|
|
1842
|
+
ok: delegationEvents.corruptLines.length === 0,
|
|
1843
|
+
details: delegationEvents.corruptLines.length === 0
|
|
1844
|
+
? `${RUNTIME_ROOT}/state/delegation-events.jsonl parsed successfully (${delegationEvents.events.length} event(s))`
|
|
1845
|
+
: `corrupt delegation event line(s): ${delegationEvents.corruptLines.join(", ")}`
|
|
1846
|
+
});
|
|
1847
|
+
checks.push({
|
|
1848
|
+
name: "delegation:proof:current_stage",
|
|
1849
|
+
ok: currentStageUntouched || delegation.missingDispatchProof.length === 0,
|
|
1850
|
+
details: currentStageUntouched
|
|
1851
|
+
? `dispatch proof check deferred for untouched stage "${flowState.currentStage}"`
|
|
1852
|
+
: delegation.missingDispatchProof.length === 0
|
|
1853
|
+
? `no dispatch proof gaps for current stage "${flowState.currentStage}"`
|
|
1854
|
+
: `isolated completions missing dispatchId/dispatchSurface/agentDefinitionPath/ackTs/completedTs: ${delegation.missingDispatchProof.join(", ")}`
|
|
1855
|
+
});
|
|
1856
|
+
checks.push({
|
|
1857
|
+
name: "warning:delegation:legacy_inferred_completions",
|
|
1858
|
+
ok: true,
|
|
1859
|
+
details: delegation.legacyInferredCompletions.length > 0
|
|
1860
|
+
? `warning: legacy inferred isolated completion rows lack event-log proof: ${delegation.legacyInferredCompletions.join(", ")}`
|
|
1861
|
+
: "no legacy inferred isolated completions for current stage"
|
|
1747
1862
|
});
|
|
1748
1863
|
checks.push({
|
|
1749
1864
|
name: "warning:delegation:waived",
|
package/dist/gate-evidence.js
CHANGED
|
@@ -9,6 +9,7 @@ import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
|
9
9
|
import { detectPublicApiChanges } from "./internal/detect-public-api-changes.js";
|
|
10
10
|
import { readFlowState, writeFlowState } from "./runs.js";
|
|
11
11
|
import { parseTddCycleLog, validateTddCycleOrder } from "./tdd-cycle.js";
|
|
12
|
+
import { validateTddVerificationEvidence } from "./tdd-verification-evidence.js";
|
|
12
13
|
import { buildTraceMatrix } from "./trace-matrix.js";
|
|
13
14
|
import { FLOW_STAGES } from "./types.js";
|
|
14
15
|
async function currentStageArtifactExists(projectRoot, stage, track) {
|
|
@@ -36,6 +37,22 @@ async function readArtifactMarkdown(projectRoot, artifactFile) {
|
|
|
36
37
|
}
|
|
37
38
|
return null;
|
|
38
39
|
}
|
|
40
|
+
async function readStageArtifactMarkdown(projectRoot, stage, track) {
|
|
41
|
+
const resolved = await resolveArtifactPath(stage, {
|
|
42
|
+
projectRoot,
|
|
43
|
+
track,
|
|
44
|
+
intent: "read"
|
|
45
|
+
});
|
|
46
|
+
if (!(await exists(resolved.absPath))) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
try {
|
|
50
|
+
return await fs.readFile(resolved.absPath, "utf8");
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
39
56
|
function unique(values) {
|
|
40
57
|
return [...new Set(values)];
|
|
41
58
|
}
|
|
@@ -263,6 +280,12 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
263
280
|
issues.push(`passed gate "${gateId}" is missing guardEvidence entry.`);
|
|
264
281
|
continue;
|
|
265
282
|
}
|
|
283
|
+
if (stage === "tdd" && gateId === "tdd_verified_before_complete") {
|
|
284
|
+
const verification = await validateTddVerificationEvidence(projectRoot, evidence);
|
|
285
|
+
if (!verification.ok) {
|
|
286
|
+
issues.push(`tdd verification gate blocked (${gateId}): ${verification.issues.join(" ")}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
266
289
|
const discoveredCommandIssue = await verifyDiscoveredCommandEvidence(projectRoot, stage, gateId, flowState);
|
|
267
290
|
if (discoveredCommandIssue) {
|
|
268
291
|
issues.push(discoveredCommandIssue);
|
|
@@ -336,7 +359,7 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
336
359
|
if (stage === "design") {
|
|
337
360
|
const researchGateRequired = schema.requiredGates.some((gate) => gate.id === "design_research_complete" && gate.tier === "required");
|
|
338
361
|
if (researchGateRequired) {
|
|
339
|
-
const designMarkdown = await
|
|
362
|
+
const designMarkdown = await readStageArtifactMarkdown(projectRoot, "design", flowState.track);
|
|
340
363
|
const inlineResearchBody = designMarkdown
|
|
341
364
|
? extractMarkdownSectionBody(designMarkdown, "Research Fleet Synthesis")
|
|
342
365
|
: null;
|
|
@@ -354,7 +377,7 @@ export async function verifyCurrentStageGateEvidence(projectRoot, flowState) {
|
|
|
354
377
|
const inlineResearchComplete = inlineResearchLines.length > 0;
|
|
355
378
|
const researchMarkdown = await readArtifactMarkdown(projectRoot, "02a-research.md");
|
|
356
379
|
if (!inlineResearchComplete && !researchMarkdown) {
|
|
357
|
-
issues.push("design research gate blocked (design_research_complete): fill `Research Fleet Synthesis` in
|
|
380
|
+
issues.push("design research gate blocked (design_research_complete): fill `Research Fleet Synthesis` in the active design artifact, or write `.cclaw/artifacts/02a-research.md` for deep/high-risk research.");
|
|
358
381
|
}
|
|
359
382
|
else if (researchMarkdown) {
|
|
360
383
|
const missingSections = [];
|
|
@@ -37,6 +37,12 @@ export type SubagentFallback =
|
|
|
37
37
|
export type ShimKind = "command" | "skill";
|
|
38
38
|
export interface HarnessAdapter {
|
|
39
39
|
id: HarnessId;
|
|
40
|
+
reality: {
|
|
41
|
+
declaredSupport: "full" | "generic" | "partial" | "none";
|
|
42
|
+
runtimeLaunch: string;
|
|
43
|
+
proofRequired: string;
|
|
44
|
+
proofSource: string;
|
|
45
|
+
};
|
|
40
46
|
/**
|
|
41
47
|
* Root directory where cclaw writes `/cc*` entry points.
|
|
42
48
|
*
|
package/dist/harness-adapters.js
CHANGED
|
@@ -71,6 +71,12 @@ export function harnessShimSkillNames() {
|
|
|
71
71
|
export const HARNESS_ADAPTERS = {
|
|
72
72
|
claude: {
|
|
73
73
|
id: "claude",
|
|
74
|
+
reality: {
|
|
75
|
+
declaredSupport: "full",
|
|
76
|
+
runtimeLaunch: "native Task launch",
|
|
77
|
+
proofRequired: "spanId+dispatchId or workerRunId+ACK for isolated completion",
|
|
78
|
+
proofSource: ".cclaw/state/delegation-events.jsonl plus delegation-log.json"
|
|
79
|
+
},
|
|
74
80
|
commandDir: ".claude/commands",
|
|
75
81
|
shimKind: "command",
|
|
76
82
|
capabilities: {
|
|
@@ -82,6 +88,12 @@ export const HARNESS_ADAPTERS = {
|
|
|
82
88
|
},
|
|
83
89
|
cursor: {
|
|
84
90
|
id: "cursor",
|
|
91
|
+
reality: {
|
|
92
|
+
declaredSupport: "generic",
|
|
93
|
+
runtimeLaunch: "generic Task/Subagent launch with cclaw role prompt",
|
|
94
|
+
proofRequired: "spanId+dispatchId/evidenceRefs for generic-dispatch completion",
|
|
95
|
+
proofSource: ".cclaw/state/delegation-events.jsonl plus artifact evidenceRefs"
|
|
96
|
+
},
|
|
85
97
|
commandDir: ".cursor/commands",
|
|
86
98
|
shimKind: "command",
|
|
87
99
|
capabilities: {
|
|
@@ -97,6 +109,12 @@ export const HARNESS_ADAPTERS = {
|
|
|
97
109
|
},
|
|
98
110
|
opencode: {
|
|
99
111
|
id: "opencode",
|
|
112
|
+
reality: {
|
|
113
|
+
declaredSupport: "full",
|
|
114
|
+
runtimeLaunch: "prompt-level launch via Task or @agent against generated .opencode/agents",
|
|
115
|
+
proofRequired: "spanId+dispatchId+ackTs+completedTs before isolated completion",
|
|
116
|
+
proofSource: ".opencode/agents/<agent>.md and .cclaw/state/delegation-events.jsonl"
|
|
117
|
+
},
|
|
100
118
|
commandDir: ".opencode/commands",
|
|
101
119
|
shimKind: "command",
|
|
102
120
|
capabilities: {
|
|
@@ -119,6 +137,12 @@ export const HARNESS_ADAPTERS = {
|
|
|
119
137
|
},
|
|
120
138
|
codex: {
|
|
121
139
|
id: "codex",
|
|
140
|
+
reality: {
|
|
141
|
+
declaredSupport: "full",
|
|
142
|
+
runtimeLaunch: "prompt-level launch by asking Codex to spawn generated custom agents",
|
|
143
|
+
proofRequired: "spanId+dispatchId+ackTs+completedTs before isolated completion",
|
|
144
|
+
proofSource: ".codex/agents/<agent>.toml and .cclaw/state/delegation-events.jsonl"
|
|
145
|
+
},
|
|
122
146
|
// Codex CLI reads skills from the universal `.agents/skills/` path
|
|
123
147
|
// (OpenAI Codex 0.89, Jan 2026). It does NOT have a native
|
|
124
148
|
// `.codex/commands/*` slash-command discovery — cclaw installs
|
|
@@ -155,9 +179,9 @@ export function harnessDispatchSurface(harnessId) {
|
|
|
155
179
|
case "cursor":
|
|
156
180
|
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.";
|
|
157
181
|
case "opencode":
|
|
158
|
-
return "Use OpenCode subagents: invoke the generated .opencode/agents/<agent>.md agent via Task or @<agent
|
|
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\".";
|
|
159
183
|
case "codex":
|
|
160
|
-
return "Use Codex native subagents: ask Codex to spawn the generated .codex/agents/<agent>.toml agent(s) by name
|
|
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\".";
|
|
161
185
|
}
|
|
162
186
|
}
|
|
163
187
|
export function harnessDispatchFallback(harnessId) {
|
|
@@ -597,7 +621,7 @@ async function cleanupLegacyCodexSurfaces(projectRoot) {
|
|
|
597
621
|
}
|
|
598
622
|
}
|
|
599
623
|
function codexAgentToml(agent) {
|
|
600
|
-
const instructions = `${agent
|
|
624
|
+
const instructions = `${agentMarkdown(agent)}\n\n${enhancedAgentInstruction(agent.name)}`.trim();
|
|
601
625
|
const sandboxMode = agent.tools.some((tool) => ["Write", "Edit", "Bash"].includes(tool))
|
|
602
626
|
? "workspace-write"
|
|
603
627
|
: "read-only";
|
|
@@ -625,7 +649,7 @@ permission:
|
|
|
625
649
|
${agentMarkdown(agent)}`;
|
|
626
650
|
}
|
|
627
651
|
function enhancedAgentInstruction(agentName) {
|
|
628
|
-
return
|
|
652
|
+
return `## Worker ACK Contract\n\nYou are the cclaw ${agentName} subagent. Follow the parent prompt as the task boundary. ACK first with JSON containing spanId, dispatchId or workerRunId, dispatchSurface, agentDefinitionPath, ackTs, and status: "ACK". Finish with the strict return schema plus the same spanId+dispatchId proof so the parent can append .cclaw/state/delegation-events.jsonl and .cclaw/state/delegation-log.json. Do not let the parent claim isolated completion without matching ACK/result proof. Do not recursively orchestrate other agents unless the parent explicitly asks.`;
|
|
629
653
|
}
|
|
630
654
|
async function syncAgentFiles(projectRoot, harnesses) {
|
|
631
655
|
const agentsDir = path.join(projectRoot, RUNTIME_ROOT, "agents");
|
package/dist/install.js
CHANGED
|
@@ -13,7 +13,7 @@ import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-co
|
|
|
13
13
|
import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
|
|
14
14
|
import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
|
|
15
15
|
import { ironLawRuntimeDocument, ironLawsSkillMarkdown } from "./content/iron-laws.js";
|
|
16
|
-
import { stageCompleteScript, startFlowScript, runHookCmdScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
16
|
+
import { stageCompleteScript, startFlowScript, runHookCmdScript, delegationRecordScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
|
|
17
17
|
import { nodeHookRuntimeScript } from "./content/node-hooks.js";
|
|
18
18
|
import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
|
|
19
19
|
import { ARTIFACT_TEMPLATES, CURSOR_WORKFLOW_RULE_MDC, RULEBOOK_MARKDOWN, buildRulesJson } from "./content/templates.js";
|
|
@@ -23,6 +23,7 @@ import { stageSkillFolder, stageSkillMarkdown } from "./content/skills.js";
|
|
|
23
23
|
import { LANGUAGE_RULE_PACK_DIR, LANGUAGE_RULE_PACK_FILES, LANGUAGE_RULE_PACK_GENERATORS, LEGACY_LANGUAGE_RULE_PACK_FOLDERS } from "./content/utility-skills.js";
|
|
24
24
|
import { RESEARCH_PLAYBOOKS } from "./content/research-playbooks.js";
|
|
25
25
|
import { SUBAGENT_CONTEXT_SKILLS } from "./content/subagent-context-skills.js";
|
|
26
|
+
import { CCLAW_AGENTS } from "./content/core-agents.js";
|
|
26
27
|
import { createInitialFlowState } from "./flow-state.js";
|
|
27
28
|
import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
|
|
28
29
|
import { ensureGitignore, removeGitignorePatterns } from "./gitignore.js";
|
|
@@ -883,6 +884,7 @@ async function writeHooks(projectRoot, config) {
|
|
|
883
884
|
compoundRecurrenceThreshold: config.compound?.recurrenceThreshold
|
|
884
885
|
}));
|
|
885
886
|
await writeFileSafe(path.join(hooksDir, "run-hook.cmd"), runHookCmdScript());
|
|
887
|
+
await writeFileSafe(path.join(hooksDir, "delegation-record.mjs"), delegationRecordScript());
|
|
886
888
|
const opencodePluginSource = opencodePluginJs();
|
|
887
889
|
await writeFileSafe(path.join(hooksDir, "opencode-plugin.mjs"), opencodePluginSource);
|
|
888
890
|
try {
|
|
@@ -891,6 +893,7 @@ async function writeHooks(projectRoot, config) {
|
|
|
891
893
|
"start-flow.mjs",
|
|
892
894
|
"run-hook.mjs",
|
|
893
895
|
"run-hook.cmd",
|
|
896
|
+
"delegation-record.mjs",
|
|
894
897
|
"opencode-plugin.mjs"
|
|
895
898
|
]) {
|
|
896
899
|
await fs.chmod(path.join(hooksDir, script), 0o755);
|
|
@@ -1339,15 +1342,7 @@ export async function uninstallCclaw(projectRoot) {
|
|
|
1339
1342
|
}
|
|
1340
1343
|
await removeIfEmpty(codexSkillsRoot);
|
|
1341
1344
|
await removeIfEmpty(path.join(projectRoot, ".agents"));
|
|
1342
|
-
const managedAgentNames =
|
|
1343
|
-
"planner",
|
|
1344
|
-
"product-manager",
|
|
1345
|
-
"critic",
|
|
1346
|
-
"reviewer",
|
|
1347
|
-
"security-reviewer",
|
|
1348
|
-
"test-author",
|
|
1349
|
-
"doc-updater"
|
|
1350
|
-
];
|
|
1345
|
+
const managedAgentNames = CCLAW_AGENTS.map((agent) => agent.name);
|
|
1351
1346
|
for (const agentName of managedAgentNames) {
|
|
1352
1347
|
await removeBestEffort(path.join(projectRoot, ".opencode/agents", `${agentName}.md`));
|
|
1353
1348
|
await removeBestEffort(path.join(projectRoot, ".codex/agents", `${agentName}.toml`));
|