cclaw-cli 0.51.22 → 0.51.23

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 CHANGED
@@ -344,8 +344,9 @@ The `tdd` stage is not prose guidance. It requires:
344
344
  - optional **REFACTOR** pass with coverage preservation
345
345
 
346
346
  `/cc-next` will not advance past `tdd` until the delegation log shows the
347
- subagent as `completed` (or, on Codex / OpenCode, role-switched with
348
- `evidenceRefs` see [Harness support](#harness-support)).
347
+ subagent as `completed`. Codex and OpenCode use generated native subagents
348
+ by default; a role-switch row is only a degraded fallback and must include
349
+ `evidenceRefs` — see [Harness support](#harness-support).
349
350
 
350
351
  ---
351
352
 
@@ -391,25 +392,25 @@ closes every real gap with a documented fallback — not a silent waiver.
391
392
  |---|---|---|---|---|
392
393
  | Claude Code | full (named subagents) | `native` | full | `AskUserQuestion` |
393
394
  | Cursor | generic Task dispatcher | `generic-dispatch` | full | `AskQuestion` |
394
- | OpenCode | plugin / in-session | `role-switch` | plugin | `question` (permission-gated; `permission.question: "allow"`) |
395
- | OpenAI Codex | in-session only | `role-switch` (evidenceRefs required) | limited (Bash-only `PreToolUse`/`PostToolUse`; requires `codex_hooks` feature flag) | `request_user_input` (experimental; Plan / Collaboration mode) |
395
+ | OpenCode | native subagents (`.opencode/agents`) | `native` | plugin | `question` (permission-gated; `permission.question: "allow"`) |
396
+ | OpenAI Codex | native parallel subagents (`.codex/agents`) | `native` | limited (Bash-only `PreToolUse`/`PostToolUse`; requires `codex_hooks` feature flag) | `request_user_input` (experimental; Plan / Collaboration mode) |
396
397
 
397
398
  What the fallbacks mean:
398
399
 
399
- - `native` — Claude runs mandatory delegations in isolated subagent
400
- workers; cclaw records them with `fulfillmentMode: "isolated"`.
400
+ - `native` — Claude, OpenCode, and Codex run mandatory delegations in
401
+ isolated subagent workers; cclaw records them with `fulfillmentMode: "isolated"`.
401
402
  - `generic-dispatch` — Cursor has a real Task tool with a fixed
402
403
  vocabulary of `subagent_type`s (`explore`, `generalPurpose`, …).
403
404
  cclaw maps each named agent (planner / reviewer / test-author /
404
405
  security-reviewer / doc-updater) onto the generic dispatcher with a
405
406
  structured role prompt.
406
- - `role-switch` — OpenCode and Codex lack an isolated worker primitive.
407
- The agent announces the role in-session, performs the work, and
408
- records a delegation row with `fulfillmentMode: "role-switch"` and at
409
- least one `evidenceRef` pointing at the artifact section that
410
- captures the output. Under role-switch, a `completed` row **without**
411
- evidenceRefs is classified as `missingEvidence` by `cclaw doctor` and
412
- blocks stage completion.
407
+ - `role-switch` — degraded fallback only when the active runtime cannot
408
+ expose its declared dispatch surface. The agent announces
409
+ `## cclaw role-switch: <stage>/<agent> (<mode>)`, loads
410
+ `.cclaw/agents/<agent>.md`, writes outputs to the stage artifact, and
411
+ records a delegation row with `fulfillmentMode: "role-switch"` plus at
412
+ least one `evidenceRef`. A role-switch `completed` row without
413
+ evidenceRefs is classified as `missingEvidence` by `cclaw doctor`.
413
414
  - `waiver` — reserved. Only fires auto-waivers if every installed
414
415
  harness declares it. Currently unused — v0.33 removed the old
415
416
  Codex-only auto-waiver path.
@@ -726,8 +726,8 @@ const DOMAIN_LABELS = {
726
726
  export const RESEARCH_FLEET_USAGE_EXAMPLE = [
727
727
  "Before drafting `03-design.md`, run `research/research-fleet.md` once and",
728
728
  "capture all four lenses in `.cclaw/artifacts/02a-research.md`.",
729
- "Dispatch semantics by harness: Claude/Cursor = parallel subagents in one turn;",
730
- "OpenCode/Codex = sequential role-switch with explicit announcements.",
729
+ "Dispatch semantics by harness: Claude/OpenCode/Codex = native subagents;",
730
+ "Cursor = generic-dispatch Task mapping; role-switch only as degraded fallback.",
731
731
  "Design must include a `Research Fleet Synthesis` section that maps each",
732
732
  "lens to concrete architecture decisions and risks."
733
733
  ].join(" ");
@@ -74,10 +74,7 @@ export const HOOK_MANIFEST = [
74
74
  bindings: {
75
75
  claude: [{ event: "PreToolUse", matcher: "Write|Edit|MultiEdit|NotebookEdit|Bash" }],
76
76
  cursor: [{ event: "preToolUse", matcher: "*" }],
77
- codex: [
78
- { event: "UserPromptSubmit" },
79
- { event: "PreToolUse", matcher: "Bash|bash" }
80
- ]
77
+ codex: [{ event: "PreToolUse", matcher: "Bash|bash" }]
81
78
  }
82
79
  },
83
80
  {
@@ -78,7 +78,7 @@ Do not invent alternate stores (no markdown mirror, no SQLite, no per-stage file
78
78
 
79
79
  Exactly one JSON object per line. Required fields must appear in the order:
80
80
  \`type, trigger, action, confidence, domain, stage, origin_stage, origin_run, frequency, universality, maturity, created, first_seen_ts, last_seen_ts, project\`.
81
- Optional fields \`source\` and \`severity\` may be appended after \`project\`.
81
+ Optional fields \`source\`, \`severity\`, \`supersedes\`, and \`superseded_by\` may be appended after \`project\`.
82
82
 
83
83
  \`\`\`json
84
84
  {"type":"pattern","trigger":"when reviewing external payloads","action":"parse through zod before touching service layer","confidence":"high","domain":"api","stage":"review","origin_stage":"review","origin_run":"payload-hardening","frequency":1,"universality":"project","maturity":"raw","created":"2026-04-14T12:00:00Z","first_seen_ts":"2026-04-14T12:00:00Z","last_seen_ts":"2026-04-14T12:00:00Z","project":"cclaw"}
@@ -103,12 +103,15 @@ Optional fields \`source\` and \`severity\` may be appended after \`project\`.
103
103
  | \`project\` | string \\| null | yes | Repo or scope name. Use \`null\` when the entry crosses projects. |
104
104
  | \`source\` | \`"stage" \\| "retro" \\| "compound" \\| "ideate" \\| "manual" \\| null\` | no | Origin channel for the entry when known. |
105
105
  | \`severity\` | \`"critical" \\| "important" \\| "suggestion"\` | no | Priority signal for compound lifts; \`critical\` enables single-hit override in compound readiness analysis. |
106
+ | \`supersedes\` | string[] | no | Non-empty IDs/slugs of older entries this entry refreshes. Use only for clear replacements discovered during compound closeout or curation. |
107
+ | \`superseded_by\` | string | no | Non-empty ID/slug of the newer entry that refreshes this one. Use only when marking stale guidance as replaced. |
106
108
 
107
109
  Rules:
108
110
  - No other fields beyond the table above. Extra keys are forbidden and MUST be rejected by any writer.
109
111
  - Every required-null field must be emitted explicitly as \`null\` (not omitted). This keeps the file grep-friendly.
110
112
  - Append-only: never rewrite or delete a historical line. Corrections are new
111
- entries whose \`trigger\` clearly supersedes the earlier one.
113
+ entries whose \`trigger\` clearly supersedes the earlier one; add \`supersedes\` /
114
+ \`superseded_by\` only when the replacement relationship is clear.
112
115
  - Keep each entry one line. No pretty-printing. No trailing commas.
113
116
 
114
117
  ## Curation policy (target: ≤ 50 active entries)
@@ -1,2 +1,3 @@
1
1
  export declare const META_SKILL_NAME = "using-cclaw";
2
+ export declare const META_SKILL_GENERATED_HELPER_SKILLS: readonly ["subagent-dev", "parallel-dispatch", "session", "iron-laws"];
2
3
  export declare function usingCclawSkillMarkdown(): string;
@@ -1,6 +1,15 @@
1
1
  import { conversationLanguagePolicyMarkdown } from "./language-policy.js";
2
2
  import { CLOSEOUT_CHAIN, closeoutChainInline, closeoutFlowMapSentence, closeoutProtocolBehaviorSentence } from "./closeout-guidance.js";
3
3
  export const META_SKILL_NAME = "using-cclaw";
4
+ export const META_SKILL_GENERATED_HELPER_SKILLS = [
5
+ "subagent-dev",
6
+ "parallel-dispatch",
7
+ "session",
8
+ "iron-laws"
9
+ ];
10
+ function generatedHelperSkillList() {
11
+ return META_SKILL_GENERATED_HELPER_SKILLS.map((name) => `\`${name}\``).join(", ");
12
+ }
4
13
  export function usingCclawSkillMarkdown() {
5
14
  return `---
6
15
  name: using-cclaw
@@ -25,7 +34,7 @@ ${conversationLanguagePolicyMarkdown()}
25
34
  If \`.cclaw/state/flow-state.json\` exists and \`currentStage\` is set,
26
35
  load the matching stage SKILL before producing **substantive** work
27
36
  (artifact edits, code, structured clarifying questions). Do not improvise
28
- from memory. Load only generated helper surfaces that actually exist in this install: \`subagent-dev\`, \`parallel-dispatch\`, \`session\`, \`iron-laws\`, research playbooks, review prompts, or enabled language rule packs under \`.cclaw/rules/lang/\`. Do not invent helper-skill names beyond those generated surfaces.
37
+ from memory. Load only generated helper surfaces that actually exist in this install: ${generatedHelperSkillList()}, research playbooks, review prompts, or enabled language rule packs under \`.cclaw/rules/lang/\`. Do not invent helper-skill names beyond those generated surfaces.
29
38
 
30
39
  Substantive vs. non-substantive:
31
40
 
@@ -1636,7 +1636,7 @@ async function handleVerifyCurrentState(runtime) {
1636
1636
  process.stderr.write(result.stderr.trim().length > 0
1637
1637
  ? result.stderr
1638
1638
  : "[cclaw] hook: local Node runtime entrypoint is required for verify-current-state\\n");
1639
- return 1;
1639
+ return mode === "strict" ? 1 : 0;
1640
1640
  }
1641
1641
  if (mode === "strict") {
1642
1642
  if (result.code !== 0) {
@@ -61,23 +61,24 @@ function autoSubagentDispatchBlock(stage, track) {
61
61
  const rules = stageAutoSubagentDispatch(stage);
62
62
  if (rules.length === 0)
63
63
  return "";
64
+ const schema = stageSchema(stage, track);
64
65
  const rows = rules
65
66
  .map((rule) => {
66
67
  const userGate = rule.requiresUserGate ? "required" : "not required";
67
- return `| ${rule.agent} | ${rule.mode} | ${userGate} | ${rule.when} |`;
68
+ return `| ${rule.agent} | ${rule.mode} | ${userGate} | ${rule.when} | ${rule.purpose} |`;
68
69
  })
69
70
  .join("\n");
70
- const mandatory = stageSchema(stage, track).mandatoryDelegations;
71
+ const mandatory = schema.mandatoryDelegations;
71
72
  const mandatoryList = mandatory.length > 0 ? mandatory.map((a) => `\`${a}\``).join(", ") : "none";
72
73
  const delegationLogRel = `${RUNTIME_ROOT}/state/delegation-log.json`;
74
+ const artifactRef = `${RUNTIME_ROOT}/artifacts/${schema.artifactRules.artifactFile}`;
73
75
  return `## Automatic Subagent Dispatch
74
-
75
- | Agent | Mode | User Gate | Trigger |
76
- |---|---|---|---|
76
+ | Agent | Mode | User Gate | Trigger | Purpose |
77
+ |---|---|---|---|---|
77
78
  ${rows}
78
-
79
- Mandatory delegations for this stage: ${mandatoryList}.
80
- Record completion/waiver in \`${delegationLogRel}\` before stage completion.
79
+ Mandatory: ${mandatoryList}. Record completion/waiver in \`${delegationLogRel}\` before completion.
80
+ ### Harness Dispatch Contract
81
+ Use true harness dispatch: Claude native Task, Cursor generic dispatch, OpenCode \`.opencode/agents/<agent>.md\`, Codex \`.codex/agents/<agent>.toml\`. Run independent read-only/review agents in parallel where safe, write evidence into \`${artifactRef}\`, then append \`${delegationLogRel}\` rows with matching \`fulfillmentMode: "isolated"\` or \`"generic-dispatch"\`. Do not collapse OpenCode or Codex to role-switch by default; role-switch is degraded fallback and must carry non-empty \`evidenceRefs\`. Missing evidence blocks completion.
81
82
  `;
82
83
  }
83
84
  function researchPlaybooksBlock(playbooks) {
@@ -160,15 +161,18 @@ function batchExecutionModeBlock(stage, track) {
160
161
  const schema = stageSchema(stage, track);
161
162
  if (!schema.batchExecutionAllowed)
162
163
  return "";
164
+ const orderingGuidance = track === "quick"
165
+ ? "Use spec acceptance items / bug reproduction slices for ordering."
166
+ : "Use plan slices for ordering.";
163
167
  return `## Batch Execution Mode
164
168
 
165
169
  Execute the current dependency batch task-by-task (RED -> GREEN -> REFACTOR).
166
170
  Stop on BLOCKED status or when user input is required.
167
- Apply concise turn announces: one announce per batch boundary (or when risk/plan
171
+ Apply concise turn announces: one announce per batch boundary (or when risk/source
168
172
  changes materially), then execute tasks without repetitive boilerplate.
169
173
 
170
174
  Detailed walkthrough:
171
- Use the active track's upstream artifact for ordering: plan slices on standard/medium, or spec acceptance items / bug reproduction slices on quick. Keep RED -> GREEN -> REFACTOR evidence in the TDD artifact.
175
+ ${orderingGuidance} Keep RED -> GREEN -> REFACTOR evidence in the TDD artifact.
172
176
  `;
173
177
  }
174
178
  function crossStageTraceBlock(trace) {
@@ -0,0 +1,2 @@
1
+ import type { FlowStage } from "../types.js";
2
+ export declare function stageCommandShimMarkdown(stage: FlowStage): string;
@@ -0,0 +1,17 @@
1
+ import { RUNTIME_ROOT } from "../constants.js";
2
+ import { stageSkillFolder } from "./skills.js";
3
+ export function stageCommandShimMarkdown(stage) {
4
+ const skillPath = `${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md`;
5
+ return `# /cc-${stage}
6
+
7
+ This is a thin compatibility shim for the \`${stage}\` flow stage.
8
+
9
+ Load and follow the authoritative stage skill:
10
+
11
+ - \`${skillPath}\`
12
+
13
+ Normal stage resume and advancement uses \`/cc-next\`. Use \`/cc-next\` to read
14
+ \`.cclaw/state/flow-state.json\`, select the active stage, and advance only after
15
+ that stage's gates pass. Do not duplicate the stage protocol here.
16
+ `;
17
+ }
@@ -35,8 +35,8 @@ export const REVIEW = {
35
35
  "Diff Scope — Run `git diff` against base branch. If no diff, exit early with APPROVED (no changes to review). Scope the review to changed files unless blast-radius analysis requires wider inspection.",
36
36
  "Change-Size Check — ~100 lines = normal. ~300 lines = consider splitting. ~1000+ lines = strongly recommend stacked PRs. Flag large diffs to the user.",
37
37
  "Risk-Based Second Opinion — compute changed-line count, files-touched count, and trust-boundary movement. Dispatch an adversarial reviewer only when trust boundaries changed, Critical/Important ambiguity remains, or the diff is both large and high-risk; otherwise record `not triggered`.",
38
- "Load upstream evidence — read TDD artifact (RED + GREEN + REFACTOR), spec, and plan when present. On quick track, use spec acceptance items / bug reproduction slices instead of nonexistent plan artifacts.",
39
- "Run traceability matrix when plan artifacts exist or the active track enforces it; on quick, confirm spec acceptance/reproduction slices are covered without requiring plan-task coverage.",
38
+ "Load upstream evidence — read TDD artifact (RED + GREEN + REFACTOR), spec, and the active track's upstream source items.",
39
+ "Run traceability matrix when the active track enforces it; otherwise confirm spec acceptance/reproduction slices are covered directly.",
40
40
  "Layer 1: Spec Compliance — check every acceptance criterion against implementation. Verdict: pass/fail per criterion.",
41
41
  "Layer 2: Integrated findings — one structured pass tagged by category: correctness, security, performance, architecture, external-safety.",
42
42
  "Security sweep — mandatory dedicated security-reviewer pass across diff + touched modules. A zero-finding pass must include `NO_CHANGE_ATTESTATION` with rationale.",
@@ -72,12 +72,12 @@ export const REVIEW = {
72
72
  { id: "review_layer_coverage_complete", description: "Layer coverage map in 07-review-army.json confirms spec/correctness/security/performance/architecture/external-safety tags were considered." },
73
73
  { id: "review_criticals_resolved", description: "Normal APPROVED or APPROVED_WITH_CONCERNS path only: no unresolved critical blockers remain. BLOCKED routes use review_verdict_blocked instead." },
74
74
  { id: "review_army_json_valid", description: "07-review-army.json passes schema validation (validateReviewArmy)." },
75
- { id: "review_trace_matrix_clean", description: "Trace matrix has no orphaned criteria/tasks/test slices for the active run, and evidence cites a discovered real test command before ship handoff." }
75
+ { id: "review_trace_matrix_clean", description: "Trace matrix has no orphaned source items or test slices for the active run, and evidence cites a discovered real test command before ship handoff." }
76
76
  ],
77
77
  requiredEvidence: [
78
78
  "Artifact written to `.cclaw/artifacts/07-review.md`.",
79
79
  "Artifact written to `.cclaw/artifacts/07-review-army.json`.",
80
- "Traceability matrix run recorded (no orphaned criteria/tasks/tests for enforced tracks).",
80
+ "Traceability matrix run recorded (no orphaned source items or tests for enforced tracks).",
81
81
  "Layer 1 verdict captured with per-criterion pass/fail.",
82
82
  "Layer 2 sections completed across correctness, security, performance, architecture, and external-safety findings.",
83
83
  "Severity log includes critical/important/suggestion buckets.",
@@ -85,7 +85,7 @@ export const REVIEW = {
85
85
  "Fresh verification command discovery recorded, and the command cited in `review_trace_matrix_clean` evidence before ship handoff.",
86
86
  "If BLOCKED: include explicit remediation route (`ROUTE_BACK_TO_TDD`) with blocking finding IDs."
87
87
  ],
88
- inputs: ["implementation diff", "spec and plan artifacts", "test/build evidence"],
88
+ inputs: ["implementation diff", "upstream artifacts", "test/build evidence"],
89
89
  requiredContext: ["spec criteria", "tdd artifact", "rulebook constraints"],
90
90
  blockers: [
91
91
  "layer 1 failed",
@@ -118,9 +118,9 @@ export const REVIEW = {
118
118
  { section: "Layer 2 Findings", required: false, validationRule: "Each finding has severity, description, and resolution status across correctness, security, performance, architecture, and external-safety. Security coverage must include either explicit security findings or `NO_CHANGE_ATTESTATION: <reason>` when no security-relevant changes were found." },
119
119
  { section: "Review Findings Contract", required: true, validationRule: "Structured findings in 07-review-army.json include id/severity/confidence/fingerprint/reportedBy/status and source tags from {spec, correctness, security, performance, architecture, external-safety} with dedup reconciliation summary." },
120
120
  { section: "Review Readiness Snapshot", required: false, validationRule: "Optional compact summary: completed checks, delegation-log status, staleness signal, open critical blockers, and ship recommendation." },
121
- { section: "Completeness Snapshot", required: false, validationRule: "Optional compact coverage summary for AC coverage, task coverage, test-slice coverage, and adversarial-review status when triggered." },
121
+ { section: "Completeness Snapshot", required: false, validationRule: "Optional compact coverage summary for AC coverage, source item coverage, test-slice coverage, and adversarial-review status when triggered." },
122
122
  { section: "Incoming Feedback Queue", required: false, validationRule: "When external review feedback exists, include a queue summary with per-item disposition (resolved / accepted-risk / rejected-with-evidence) and evidence refs." },
123
- { section: "Trace Matrix Check", required: false, validationRule: "Records criteria/tasks/tests orphan counts (all zero on enforced tracks) with command output reference." },
123
+ { section: "Trace Matrix Check", required: false, validationRule: "Records source-item/test orphan counts (all zero on enforced tracks) with command output reference." },
124
124
  { section: "Blocked Route", required: false, validationRule: "When Final Verdict is BLOCKED: includes `ROUTE_BACK_TO_TDD`, rewind target `tdd`, and blocked finding IDs." },
125
125
  { section: "Severity Summary", required: true, validationRule: "Per-severity count lines for critical, important, and suggestion buckets." },
126
126
  { section: "Final Verdict", required: true, validationRule: "Exactly one of: APPROVED, APPROVED_WITH_CONCERNS, BLOCKED." }
@@ -65,7 +65,7 @@ export const SCOPE = {
65
65
  "If the user says no but cannot name the change, offer concrete moves: keep scope, add one obvious adjacent capability, reduce to wedge, or re-open stack/product direction.",
66
66
  `Before final approval, record outside-voice findings and a \`## Scope Outside Voice Loop\` table using ${reviewLoopPolicySummary("scope")}`,
67
67
  "**STOP.** Wait for explicit user approval of the scope mode and scope contract before writing final approval language or advancing.",
68
- "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived. If no isolated planner is available, use `--waive-delegation=planner --waiver-reason=\"role-switch scope self-review completed\"`. Then close with `node .cclaw/hooks/stage-complete.mjs scope --passed=scope_mode_selected,scope_contract_written,scope_user_approved --evidence-json '{\"scope_mode_selected\":\"<user-approved mode + rationale>\",\"scope_contract_written\":\"<artifact path + sections>\",\"scope_user_approved\":\"<explicit user approval quote or summary>\"}'`. `scope_user_approved` must cite the user's approval; review-loop evidence alone is not approval."
68
+ "**STOP BEFORE ADVANCE.** Mandatory delegation `planner` must be completed or explicitly waived for a real blocker. If the active harness cannot isolate a planner, run a role-switch planner pass instead: announce `## cclaw role-switch: scope/planner (mandatory)`, write the planner output/evidence into the scope artifact, and append a completed delegation row with `fulfillmentMode: \"role-switch\"` plus non-empty `evidenceRefs`. Then close with `node .cclaw/hooks/stage-complete.mjs scope --passed=scope_mode_selected,scope_contract_written,scope_user_approved --evidence-json '{\"scope_mode_selected\":\"<user-approved mode + rationale>\",\"scope_contract_written\":\"<artifact path + sections>\",\"scope_user_approved\":\"<explicit user approval quote or summary>\"}'`. `scope_user_approved` must cite the user's approval; review-loop evidence alone is not approval."
69
69
  ],
70
70
  process: [
71
71
  "Run configured pre-scope audit only when enabled.",
@@ -241,7 +241,12 @@ function tddStageVariantForTrack(track) {
241
241
  skillDescription: renderTrackTerminology(TDD.skillDescription, renderContext),
242
242
  philosophy: {
243
243
  ...TDD.philosophy,
244
- hardGate: renderTrackTerminology(TDD.philosophy.hardGate, renderContext)
244
+ hardGate: renderTrackTerminology(TDD.philosophy.hardGate, renderContext),
245
+ purpose: renderTrackTerminology(TDD.philosophy.purpose, renderContext),
246
+ whenToUse: TDD.philosophy.whenToUse.map((value) => renderTrackTerminology(value, renderContext)),
247
+ whenNotToUse: TDD.philosophy.whenNotToUse.map((value) => renderTrackTerminology(value, renderContext)),
248
+ commonRationalizations: TDD.philosophy.commonRationalizations
249
+ .map((value) => renderTrackTerminology(value, renderContext))
245
250
  },
246
251
  executionModel: {
247
252
  ...TDD.executionModel,
@@ -258,7 +263,9 @@ function tddStageVariantForTrack(track) {
258
263
  requiredEvidence: TDD.executionModel.requiredEvidence
259
264
  .map((value) => renderTrackTerminology(value, renderContext)),
260
265
  inputs: TDD.executionModel.inputs.map((value) => renderTrackTerminology(value, renderContext)),
261
- requiredContext: [renderContext.upstreamArtifactLabel, "existing test patterns", "affected contracts and state boundaries"]
266
+ requiredContext: [renderContext.upstreamArtifactLabel, "existing test patterns", "affected contracts and state boundaries"],
267
+ blockers: TDD.executionModel.blockers.map((value) => renderTrackTerminology(value, renderContext)),
268
+ exitCriteria: TDD.executionModel.exitCriteria.map((value) => renderTrackTerminology(value, renderContext))
262
269
  },
263
270
  reviewLens: {
264
271
  ...TDD.reviewLens,
@@ -286,7 +293,7 @@ function tddStageVariantForTrack(track) {
286
293
  if (row.section === "Traceability") {
287
294
  return {
288
295
  ...row,
289
- validationRule: "Spec acceptance item IDs and, for bug fixes, reproduction slice IDs are linked to RED/GREEN evidence. No plan artifact is required on quick."
296
+ validationRule: "Spec acceptance item IDs and, for bug fixes, reproduction slice IDs are linked to RED/GREEN evidence."
290
297
  };
291
298
  }
292
299
  return {
@@ -83,16 +83,26 @@ can enforce phase-appropriate write boundaries. Use separate workers only when t
83
83
  |---|---|---|---|---|
84
84
  | Claude | \`native\` | Task (named subagent_type) | AskUserQuestion | \`npx cclaw-cli doctor\` |
85
85
  | Cursor | \`generic-dispatch\` | Task (generic subagent_type: explore/generalPurpose/…) | AskQuestion | \`npx cclaw-cli doctor\` |
86
- | OpenCode | \`role-switch\` | plugin dispatch _or_ in-session role-switch | \`question\` (permission-gated; \`permission.question: "allow"\`) | \`npx cclaw-cli doctor\` |
87
- | Codex | \`role-switch\` | in-session role-switch (mandatory evidenceRefs) | \`request_user_input\` (experimental; Plan / Collaboration mode) | \`npx cclaw-cli doctor\` |
86
+ | OpenCode | \`native\` | generated \`.opencode/agents/<agent>.md\` subagents via Task / \`@agent\` mention | \`question\` (permission-gated; \`permission.question: "allow"\`) | \`npx cclaw-cli doctor\` |
87
+ | Codex | \`native\` | generated \`.codex/agents/<agent>.toml\` custom agents via native parallel subagent spawning | \`request_user_input\` (experimental; Plan / Collaboration mode) | \`npx cclaw-cli doctor\` |
88
88
 
89
89
  **Dispatch rules driven by \`subagentFallback\`:**
90
90
 
91
91
  - \`native\` — use the harness's own named subagent primitive; delegation entry uses \`fulfillmentMode: "isolated"\`.
92
92
  - \`generic-dispatch\` — map each cclaw agent onto the generic dispatcher with a role prompt; delegation entry uses \`fulfillmentMode: "generic-dispatch"\`.
93
- - \`role-switch\` — announce the role in-session, perform the work, append a delegation row with \`fulfillmentMode: "role-switch"\` and ≥1 \`evidenceRef\`. Without evidenceRefs the \`delegation:mandatory:current_stage\` check reports \`missingEvidence\` and blocks stage completion.
93
+ - \`role-switch\` — degraded fallback only when the active runtime cannot expose its declared dispatch surface. Announce the role in-session, perform the work, append a delegation row with \`fulfillmentMode: "role-switch"\` and ≥1 \`evidenceRef\`. Without evidenceRefs the \`delegation:mandatory:current_stage\` check reports \`missingEvidence\` and blocks stage completion.
94
94
 
95
- The only time a \`harness_limitation\` waiver fires automatically is when every installed harness declares \`subagentFallback: "waiver"\`. cclaw 0.33 no longer maps Codex onto auto-waiver — the agent must role-switch with evidence.
95
+ ### Native dispatch contract
96
+
97
+ Use real harness subagents for OpenCode and Codex:
98
+
99
+ 1. OpenCode: invoke the generated \`.opencode/agents/<agent>.md\` subagent via Task or \`@<agent>\`. Built-in \`general\` / \`explore\` remain fallback subagent types for ad hoc tasks, but cclaw's core roles are generated by name.
100
+ 2. Codex: ask Codex to spawn the generated \`.codex/agents/<agent>.toml\` custom agent(s) by name; for review-style independent lanes, request parallel spawning and wait for all results before reconciliation.
101
+ 3. Claude: use the native named Task subagent. Cursor: map the cclaw role onto the generic Task/Subagent surface with a self-contained prompt.
102
+ 4. Produce stage output in the current artifact, with anchors suitable for \`evidenceRefs\`.
103
+ 5. Append delegation ledger rows with \`stage\`, \`agent\`, \`mode\`, \`status: "completed"\`, and \`fulfillmentMode\` matching the dispatch mode (\`"isolated"\` for Claude/OpenCode/Codex, \`"generic-dispatch"\` for Cursor).
104
+
105
+ The only time a \`harness_limitation\` waiver fires automatically is when every installed harness declares \`subagentFallback: "waiver"\`. Do not map Codex or OpenCode onto auto-waiver or default role-switch; they have true subagent surfaces.
96
106
 
97
107
  ### Model routing
98
108
 
@@ -645,7 +645,7 @@ Execution rule: complete and verify each batch before starting the next batch.
645
645
  # TDD Artifact
646
646
 
647
647
  ## Upstream Handoff
648
- - Source artifacts: \`04-spec.md\`; \`05-plan.md\` when present. Quick track uses spec acceptance items / bug reproduction slices instead of nonexistent plan tasks.
648
+ - Source artifacts: \`04-spec.md\` plus the active track's upstream source item (plan slice on standard/medium, spec acceptance item or bug reproduction slice on quick).
649
649
  - Decisions carried forward:
650
650
  - Constraints carried forward:
651
651
  - Open questions:
@@ -672,11 +672,11 @@ Execution rule: complete and verify each batch before starting the next batch.
672
672
  | S-1 | | | |
673
673
 
674
674
  ## Acceptance Mapping
675
- | Slice | Plan task ID or quick source | Spec criterion ID |
675
+ | Slice | Source item ID | Spec criterion ID |
676
676
  |---|---|---|
677
- | S-1 | T-1 / QS-1 | AC-1 |
677
+ | S-1 | SRC-1 | AC-1 |
678
678
 
679
- > On quick track, map to the \`Quick Reproduction Contract\` bug slice or spec acceptance item. Do not invent a plan task just to satisfy this table.
679
+ > Map each slice to the active track's source item: plan slice on standard/medium, or the \`Quick Reproduction Contract\` bug slice / spec acceptance item on quick.
680
680
 
681
681
  ## Failure Analysis
682
682
  | Slice | Expected missing behavior | Actual failure reason |
@@ -693,7 +693,7 @@ Execution rule: complete and verify each batch before starting the next batch.
693
693
  - Behavior preserved:
694
694
 
695
695
  ## Traceability
696
- - Plan task IDs:
696
+ - Source item IDs:
697
697
  - Spec criterion IDs:
698
698
 
699
699
 
@@ -729,7 +729,7 @@ Execution rule: complete and verify each batch before starting the next batch.
729
729
  # Review Artifact
730
730
 
731
731
  ## Upstream Handoff
732
- - Source artifacts: \`04-spec.md\`, \`06-tdd.md\`; \`05-plan.md\` only when present. Quick track reviews spec acceptance items / bug reproduction slices without requiring plan-task coverage.
732
+ - Source artifacts: \`04-spec.md\`, \`06-tdd.md\`, plus the active track's upstream source item when available.
733
733
  - Decisions carried forward:
734
734
  - Constraints carried forward:
735
735
  - Open questions:
@@ -766,15 +766,15 @@ Execution rule: complete and verify each batch before starting the next batch.
766
766
 
767
767
  ## Completeness Snapshot
768
768
  - AC coverage: <N>/<M> (<percent>%)
769
- - Task coverage (tasks backed by ≥1 test slice): <N>/<M> or \`N/A - quick track has no plan artifact\`
769
+ - Source item coverage (source items backed by ≥1 test slice): <N>/<M> or \`N/A - direct spec/reproduction coverage\`
770
770
  - Slice coverage (slices linked to ≥1 AC or bug reproduction slice): <N>/<M>
771
771
  - Adversarial review: not triggered | pass | fail
772
772
  - Overall: complete | concerns | blocked
773
773
 
774
774
  ## Trace Matrix Check
775
- - Command: \`cclaw internal trace-matrix\` when plan artifacts exist or the active track enforces it; quick track may record direct AC/reproduction-slice coverage instead.
775
+ - Command: \`cclaw internal trace-matrix\` when the active track enforces it; otherwise record direct AC/reproduction-slice coverage.
776
776
  - Orphaned criteria: 0
777
- - Orphaned tasks: 0 or \`N/A - quick track\`
777
+ - Orphaned source items: 0 or \`N/A - direct spec/reproduction coverage\`
778
778
  - Orphaned tests: 0
779
779
  - Evidence ref:
780
780
 
@@ -34,8 +34,15 @@ export function renderTrackTerminology(value, context) {
34
34
  }
35
35
  return value
36
36
  .replace(/\btask from the plan\b/giu, `${context.traceabilitySourceNoun} from the spec`)
37
+ .replace(/\bplan confirmation\b/giu, "spec approval")
38
+ .replace(/\bplan approval\b/giu, "spec approval")
39
+ .replace(/\bapproved plan slice\b/giu, `approved ${context.traceabilitySliceNoun}`)
40
+ .replace(/\bplanned slice\b/giu, context.traceabilitySliceNoun)
37
41
  .replace(/\bplan task ID\b/giu, context.traceabilityIdNoun)
38
42
  .replace(/\bplan task\b/giu, context.traceabilitySourceNoun)
43
+ .replace(/\bplan-task\b/giu, "acceptance-criterion")
44
+ .replace(/\btask coverage\b/giu, "source item coverage")
45
+ .replace(/\borphaned tasks\b/giu, "orphaned source items")
39
46
  .replace(/\bplan row\b/giu, "acceptance row")
40
47
  .replace(/\btraceable to plan slice\b/giu, `traceable to ${context.traceabilitySliceNoun}`)
41
48
  .replace(/\bplan slice\b/giu, context.traceabilitySliceNoun)
@@ -6,8 +6,8 @@ export type DelegationStatus = "scheduled" | "completed" | "failed" | "waived";
6
6
  * How a delegation was actually fulfilled. Advisory — mirrors the harness
7
7
  * `subagentFallback` that was in effect when the entry was recorded.
8
8
  *
9
- * - `isolated` — Claude-style isolated subagent worker.
10
- * - `generic-dispatch` — Cursor-style Task dispatch mapped to a named role.
9
+ * - `isolated` — native isolated subagent worker (Claude/OpenCode/Codex).
10
+ * - `generic-dispatch` — generic Task/Subagent dispatch mapped to a named role.
11
11
  * - `role-switch` — performed in-session with explicit role announce.
12
12
  * - `harness-waiver` — auto-waived due to missing dispatch capability.
13
13
  */
@@ -18,6 +18,13 @@ function delegationLockPath(projectRoot) {
18
18
  function createSpanId() {
19
19
  return `dspan-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
20
20
  }
21
+ function activeHarnessSubagentFallback() {
22
+ const activeHarness = process.env.CCLAW_ACTIVE_HARNESS;
23
+ if (!activeHarness)
24
+ return undefined;
25
+ return HARNESS_ADAPTERS[activeHarness]
26
+ ?.capabilities.subagentFallback;
27
+ }
21
28
  async function resolveReviewDiffBase(projectRoot) {
22
29
  let head = "";
23
30
  try {
@@ -229,10 +236,7 @@ export async function appendDelegation(projectRoot, entry) {
229
236
  stamped.evidenceRefs = [];
230
237
  }
231
238
  if (stamped.status === "completed" && stamped.fulfillmentMode === undefined) {
232
- const activeFallback = process.env.CCLAW_ACTIVE_HARNESS
233
- ? HARNESS_ADAPTERS[process.env.CCLAW_ACTIVE_HARNESS]
234
- ?.capabilities.subagentFallback
235
- : undefined;
239
+ const activeFallback = activeHarnessSubagentFallback();
236
240
  if (activeFallback) {
237
241
  stamped.fulfillmentMode = expectedFulfillmentMode([activeFallback]);
238
242
  }
@@ -291,8 +295,9 @@ export async function checkMandatoryDelegations(projectRoot, stage, options = {}
291
295
  const missingEvidence = [];
292
296
  const config = await readConfig(projectRoot).catch(() => null);
293
297
  const harnesses = config?.harnesses ?? [];
294
- const fallbacks = harnesses.map((h) => HARNESS_ADAPTERS[h].capabilities.subagentFallback);
295
- const expectedMode = expectedFulfillmentMode(fallbacks);
298
+ const configuredFallbacks = harnesses.map((h) => HARNESS_ADAPTERS[h].capabilities.subagentFallback);
299
+ const activeFallback = activeHarnessSubagentFallback();
300
+ const expectedMode = expectedFulfillmentMode(activeFallback ? [activeFallback] : configuredFallbacks);
296
301
  for (const agent of mandatory) {
297
302
  const rows = forRun.filter((e) => e.agent === agent);
298
303
  const completedRows = rows.filter((e) => e.status === "completed");
@@ -307,9 +312,11 @@ export async function checkMandatoryDelegations(projectRoot, stage, options = {}
307
312
  if (hasWaived) {
308
313
  waived.push(agent);
309
314
  }
310
- // Evidence is required for any non-isolated completion mode. Legacy rows
311
- // without fulfillmentMode are inferred to `isolated` during parse.
312
- const evidenceRequired = completedRows.some((e) => (e.fulfillmentMode ?? "isolated") !== "isolated");
315
+ // Evidence is required for non-isolated completions and for explicit
316
+ // degraded role-switch rows. Native OpenCode/Codex/Claude isolated
317
+ // dispatch is accepted as true subagent work; role-switch remains a
318
+ // fallback that must point at artifact evidence.
319
+ const evidenceRequired = expectedMode !== "isolated" || completedRows.some((e) => (e.fulfillmentMode ?? "isolated") !== "isolated");
313
320
  if (hasCompleted &&
314
321
  evidenceRequired &&
315
322
  !completedRows.some((e) => Array.isArray(e.evidenceRefs) && e.evidenceRefs.length > 0)) {
@@ -39,7 +39,7 @@ const RULES = [
39
39
  }
40
40
  },
41
41
  {
42
- test: /^(dir:|command:|utility_command:|skill:|utility_skill:|agent:|harness_tool_ref:|harness_ref:|stage_examples_ref:|doctor_ref:)/,
42
+ test: /^(dir:|command:|utility_command:|stage_command:|skill:|utility_skill:|agent:|harness_tool_ref:|harness_ref:|stage_examples_ref:|doctor_ref:)/,
43
43
  metadata: {
44
44
  severity: "error",
45
45
  summary: "Generated runtime surface presence check.",
package/dist/doctor.js CHANGED
@@ -18,6 +18,7 @@ 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";
20
20
  import { stageSkillFolder } from "./content/skills.js";
21
+ import { stageCommandShimMarkdown } from "./content/stage-command.js";
21
22
  import { doctorCheckMetadata } from "./doctor-registry.js";
22
23
  import { resolveTrackFromPrompt } from "./track-heuristics.js";
23
24
  import { classifyCodexHooksFlag, codexConfigPath, readCodexConfig } from "./codex-feature-flag.js";
@@ -289,17 +290,23 @@ function normalizeOpenCodePluginEntry(entry) {
289
290
  }
290
291
  return null;
291
292
  }
292
- async function opencodeRegistrationCheck(projectRoot) {
293
- const expected = ".opencode/plugins/cclaw-plugin.mjs";
294
- const candidates = [
293
+ const OPENCODE_PLUGIN_REL_PATH = ".opencode/plugins/cclaw-plugin.mjs";
294
+ function opencodeConfigCandidates(projectRoot) {
295
+ return [
295
296
  path.join(projectRoot, "opencode.json"),
296
297
  path.join(projectRoot, "opencode.jsonc"),
297
298
  path.join(projectRoot, ".opencode/opencode.json"),
298
299
  path.join(projectRoot, ".opencode/opencode.jsonc")
299
300
  ];
301
+ }
302
+ function openCodeConfigRegistersPlugin(parsed) {
303
+ const plugins = Array.isArray(parsed.plugin) ? parsed.plugin : [];
304
+ return plugins.some((entry) => normalizeOpenCodePluginEntry(entry) === OPENCODE_PLUGIN_REL_PATH);
305
+ }
306
+ async function opencodeRegistrationCheck(projectRoot) {
300
307
  const mismatches = [];
301
308
  let foundAnyConfig = false;
302
- for (const configPath of candidates) {
309
+ for (const configPath of opencodeConfigCandidates(projectRoot)) {
303
310
  if (!(await exists(configPath))) {
304
311
  continue;
305
312
  }
@@ -309,17 +316,49 @@ async function opencodeRegistrationCheck(projectRoot) {
309
316
  mismatches.push(`${path.relative(projectRoot, configPath)} is unreadable or invalid JSON`);
310
317
  continue;
311
318
  }
312
- const plugins = Array.isArray(parsed.plugin) ? parsed.plugin : [];
313
- const registered = plugins.some((entry) => normalizeOpenCodePluginEntry(entry) === expected);
314
- if (registered) {
315
- return { ok: true, details: `${path.relative(projectRoot, configPath)} registers ${expected}` };
319
+ if (openCodeConfigRegistersPlugin(parsed)) {
320
+ return { ok: true, details: `${path.relative(projectRoot, configPath)} registers ${OPENCODE_PLUGIN_REL_PATH}` };
316
321
  }
317
- mismatches.push(`${path.relative(projectRoot, configPath)} missing plugin ${expected}`);
322
+ mismatches.push(`${path.relative(projectRoot, configPath)} missing plugin ${OPENCODE_PLUGIN_REL_PATH}`);
318
323
  }
319
324
  if (foundAnyConfig) {
320
325
  return { ok: false, details: mismatches.join(" | ") };
321
326
  }
322
- return { ok: false, details: `No opencode.json/opencode.jsonc found with plugin ${expected}` };
327
+ return { ok: false, details: `No opencode.json/opencode.jsonc found with plugin ${OPENCODE_PLUGIN_REL_PATH}` };
328
+ }
329
+ async function opencodeQuestionPermissionCheck(projectRoot) {
330
+ const mismatches = [];
331
+ for (const configPath of opencodeConfigCandidates(projectRoot)) {
332
+ if (!(await exists(configPath)))
333
+ continue;
334
+ const parsed = await readHookDocument(configPath);
335
+ if (!parsed || !openCodeConfigRegistersPlugin(parsed))
336
+ continue;
337
+ const permission = toObject(parsed.permission) ?? {};
338
+ if (permission.question === "allow") {
339
+ return {
340
+ ok: true,
341
+ details: `${path.relative(projectRoot, configPath)} sets permission.question to "allow" for structured questions`
342
+ };
343
+ }
344
+ mismatches.push(`${path.relative(projectRoot, configPath)} registers ${OPENCODE_PLUGIN_REL_PATH} but must set permission.question to "allow"`);
345
+ }
346
+ if (mismatches.length > 0) {
347
+ return { ok: false, details: mismatches.join(" | ") };
348
+ }
349
+ return {
350
+ ok: false,
351
+ details: `No opencode config with ${OPENCODE_PLUGIN_REL_PATH} registration found; cannot verify permission.question = "allow"`
352
+ };
353
+ }
354
+ function opencodeQuestionEnvCheck() {
355
+ if (process.env.OPENCODE_ENABLE_QUESTION_TOOL === "1") {
356
+ return { ok: true, details: "OPENCODE_ENABLE_QUESTION_TOOL=1 is set for ACP question tooling" };
357
+ }
358
+ return {
359
+ ok: false,
360
+ details: "Set OPENCODE_ENABLE_QUESTION_TOOL=1 for OpenCode ACP clients so permission-gated structured questions can use the question tool."
361
+ };
323
362
  }
324
363
  async function initRecoveryCheck(projectRoot) {
325
364
  const sentinelPath = path.join(projectRoot, RUNTIME_ROOT, "state", ".init-in-progress");
@@ -667,7 +706,6 @@ export async function doctorChecks(projectRoot, options = {}) {
667
706
  ok: agentsBlockOk,
668
707
  details: `${agentsFile} must contain the managed cclaw marker block with routing, verification, and minimal detail pointer`
669
708
  });
670
- // User-facing entry commands only. Stage and view subcommands live in skills.
671
709
  for (const cmd of ["start", "next", "ideate", "view"]) {
672
710
  const cmdPath = path.join(projectRoot, RUNTIME_ROOT, "commands", `${cmd}.md`);
673
711
  checks.push({
@@ -676,6 +714,19 @@ export async function doctorChecks(projectRoot, options = {}) {
676
714
  details: cmdPath
677
715
  });
678
716
  }
717
+ for (const stage of FLOW_STAGES) {
718
+ const cmdPath = path.join(projectRoot, RUNTIME_ROOT, "commands", `${stage}.md`);
719
+ let stageCommandOk = false;
720
+ if (await exists(cmdPath)) {
721
+ const content = await fs.readFile(cmdPath, "utf8");
722
+ stageCommandOk = content === stageCommandShimMarkdown(stage);
723
+ }
724
+ checks.push({
725
+ name: `stage_command:${stage}`,
726
+ ok: stageCommandOk,
727
+ details: `${cmdPath} must be a thin shim to ${RUNTIME_ROOT}/skills/${stageSkillFolder(stage)}/SKILL.md and /cc-next`
728
+ });
729
+ }
679
730
  // Utility skills
680
731
  for (const [folder, label] of [
681
732
  ["learnings", "learnings"],
@@ -942,7 +993,6 @@ export async function doctorChecks(projectRoot, options = {}) {
942
993
  const codexStopCmds = collectHookCommands(codexHooks.Stop);
943
994
  const codexWiringOk = codexSessionCmds.some((cmd) => cmd.includes("session-start")) &&
944
995
  codexUserPromptCmds.some((cmd) => cmd.includes("prompt-guard")) &&
945
- codexUserPromptCmds.some((cmd) => cmd.includes("workflow-guard")) &&
946
996
  codexUserPromptCmds.some((cmd) => cmd.includes("verify-current-state")) &&
947
997
  codexPreCmds.some((cmd) => cmd.includes("prompt-guard")) &&
948
998
  codexPreCmds.some((cmd) => cmd.includes("workflow-guard")) &&
@@ -951,7 +1001,7 @@ export async function doctorChecks(projectRoot, options = {}) {
951
1001
  checks.push({
952
1002
  name: "hook:wiring:codex",
953
1003
  ok: codexWiringOk,
954
- details: `${codexHooksFile} must wire SessionStart, UserPromptSubmit(prompt/workflow/verify-current-state), PreToolUse(prompt/workflow), PostToolUse(context-monitor), and Stop(stop-handoff). PreToolUse/PostToolUse run Bash-only in Codex v0.114+`
1004
+ details: `${codexHooksFile} must wire SessionStart, UserPromptSubmit(prompt/verify-current-state), Bash-only PreToolUse(prompt/workflow), Bash-only PostToolUse(context-monitor), and Stop(stop-handoff). Codex workflow-guard is intentionally strict Bash-only.`
955
1005
  });
956
1006
  // Feature flag warning: Codex ignores `.codex/hooks.json` unless the
957
1007
  // user has `[features] codex_hooks = true` in `~/.codex/config.toml`.
@@ -1074,6 +1124,18 @@ export async function doctorChecks(projectRoot, options = {}) {
1074
1124
  ok: registration.ok,
1075
1125
  details: registration.details
1076
1126
  });
1127
+ const questionPermission = await opencodeQuestionPermissionCheck(projectRoot);
1128
+ checks.push({
1129
+ name: "hook:opencode:question_permission",
1130
+ ok: questionPermission.ok,
1131
+ details: questionPermission.details
1132
+ });
1133
+ const questionEnv = opencodeQuestionEnvCheck();
1134
+ checks.push({
1135
+ name: "warning:opencode:question_tool_env",
1136
+ ok: questionEnv.ok,
1137
+ details: questionEnv.details
1138
+ });
1077
1139
  }
1078
1140
  const nodeVersion = await commandVersion("node");
1079
1141
  const nodeMajor = parseNodeMajor(nodeVersion.output);
@@ -1,19 +1,20 @@
1
- import type { HarnessId } from "./types.js";
1
+ import { type HarnessId } from "./types.js";
2
2
  export declare const CCLAW_MARKER_START = "<!-- cclaw-start -->";
3
3
  export declare const CCLAW_MARKER_END = "<!-- cclaw-end -->";
4
4
  export type SubagentFallback =
5
- /** Harness has real, isolated subagent dispatch; no fallback needed. */
5
+ /** Harness has real, isolated named subagent dispatch; no fallback needed. */
6
6
  "native"
7
7
  /**
8
- * Harness has generic dispatch (e.g. Cursor's Task tool with
9
- * `subagent_type`) but not user-defined named subagents; cclaw maps each
10
- * named agent to the generic dispatcher with a structured role prompt.
8
+ * Harness has a real dispatcher but not cclaw-named agents. cclaw maps each
9
+ * named role to the available built-in/generic subagent surface with a
10
+ * structured role prompt.
11
11
  */
12
12
  | "generic-dispatch"
13
13
  /**
14
14
  * No isolated dispatch — the agent performs the named subagent's role
15
15
  * in-session with an explicit role announce + delegation-log entry
16
- * carrying evidenceRefs. Accepted as `completed` in delegation checks.
16
+ * carrying evidenceRefs. Accepted as `completed` only when no true dispatch
17
+ * surface exists.
17
18
  */
18
19
  | "role-switch"
19
20
  /**
@@ -50,11 +51,11 @@ export interface HarnessAdapter {
50
51
  capabilities: {
51
52
  /**
52
53
  * Level of native subagent dispatch:
53
- * - `full` — isolated workers + user-defined named subagents (Claude).
54
- * - `generic` — generic dispatcher (Task) without named agents (Cursor).
55
- * - `partial` — plugin-based dispatch, not a first-class primitive
56
- * (OpenCode).
57
- * - `none` — no dispatch primitive at all (Codex).
54
+ * - `full` — isolated workers + user-defined named subagents (Claude,
55
+ * OpenCode, Codex custom agents).
56
+ * - `generic` — generic dispatcher without cclaw-named agents (Cursor).
57
+ * - `partial` — limited or plugin-only dispatch surface.
58
+ * - `none` — no dispatch primitive at all.
58
59
  */
59
60
  nativeSubagentDispatch: "full" | "generic" | "partial" | "none";
60
61
  hookSurface: "full" | "plugin" | "limited" | "none";
@@ -87,6 +88,8 @@ export declare function harnessShimFileNames(): string[];
87
88
  /** Skill folder names cclaw writes under `<commandDir>` for skill-kind harnesses. */
88
89
  export declare function harnessShimSkillNames(): string[];
89
90
  export declare const HARNESS_ADAPTERS: Record<HarnessId, HarnessAdapter>;
91
+ export declare function harnessDispatchSurface(harnessId: HarnessId): string;
92
+ export declare function harnessDispatchFallback(harnessId: HarnessId): string;
90
93
  export type HarnessTier = "tier1" | "tier2" | "tier3";
91
94
  export declare function harnessTier(harnessId: HarnessId): HarnessTier;
92
95
  /**
@@ -1,10 +1,11 @@
1
1
  import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
- import { RUNTIME_ROOT } from "./constants.js";
3
+ import { RUNTIME_ROOT, STAGE_TO_SKILL_FOLDER } from "./constants.js";
4
4
  import { conversationLanguagePolicyMarkdown } from "./content/language-policy.js";
5
5
  import { CCLAW_AGENTS, agentMarkdown } from "./content/core-agents.js";
6
6
  import { ironLawsAgentsMdBlock } from "./content/iron-laws.js";
7
7
  import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
8
+ import { FLOW_STAGES } from "./types.js";
8
9
  export const CCLAW_MARKER_START = "<!-- cclaw-start -->";
9
10
  export const CCLAW_MARKER_END = "<!-- cclaw-end -->";
10
11
  function escapeRegExp(value) {
@@ -46,12 +47,26 @@ const LEGACY_CODEX_SKILL_PREFIX = "cclaw-cc";
46
47
  * harness command directories so `/cc-learn` etc. do not linger.
47
48
  */
48
49
  const LEGACY_HARNESS_SHIMS = ["cc-learn.md"];
50
+ function stageShimFileName(stage) {
51
+ return `cc-${stage}.md`;
52
+ }
53
+ function stageShimSkillName(stage) {
54
+ return `cc-${stage}`;
55
+ }
49
56
  export function harnessShimFileNames() {
50
- return ["cc.md", ...UTILITY_SHIMS.map((shim) => shim.fileName)];
57
+ return [
58
+ "cc.md",
59
+ ...UTILITY_SHIMS.map((shim) => shim.fileName),
60
+ ...FLOW_STAGES.map((stage) => stageShimFileName(stage))
61
+ ];
51
62
  }
52
63
  /** Skill folder names cclaw writes under `<commandDir>` for skill-kind harnesses. */
53
64
  export function harnessShimSkillNames() {
54
- return [ENTRY_SHIM_SKILL_NAME, ...UTILITY_SHIMS.map((shim) => shim.skillName)];
65
+ return [
66
+ ENTRY_SHIM_SKILL_NAME,
67
+ ...UTILITY_SHIMS.map((shim) => shim.skillName),
68
+ ...FLOW_STAGES.map((stage) => stageShimSkillName(stage))
69
+ ];
55
70
  }
56
71
  export const HARNESS_ADAPTERS = {
57
72
  claude: {
@@ -85,7 +100,11 @@ export const HARNESS_ADAPTERS = {
85
100
  commandDir: ".opencode/commands",
86
101
  shimKind: "command",
87
102
  capabilities: {
88
- nativeSubagentDispatch: "partial",
103
+ // OpenCode supports project-local markdown subagents under
104
+ // `.opencode/agents/`; primary agents can invoke them via the Task
105
+ // tool or explicit `@agent` mention. cclaw materializes its core
106
+ // roster there, so mandatory delegations are real isolated subagents.
107
+ nativeSubagentDispatch: "full",
89
108
  hookSurface: "plugin",
90
109
  // OpenCode exposes a native `question` tool (header + options +
91
110
  // custom-answer fallback, multi-question navigation). It is
@@ -95,7 +114,7 @@ export const HARNESS_ADAPTERS = {
95
114
  // in generated harness guidance; skills fall back to the shared
96
115
  // plain-text lettered list when the tool is denied or unavailable.
97
116
  structuredAsk: "question",
98
- subagentFallback: "role-switch"
117
+ subagentFallback: "native"
99
118
  }
100
119
  },
101
120
  codex: {
@@ -103,8 +122,10 @@ export const HARNESS_ADAPTERS = {
103
122
  // Codex CLI reads skills from the universal `.agents/skills/` path
104
123
  // (OpenAI Codex 0.89, Jan 2026). It does NOT have a native
105
124
  // `.codex/commands/*` slash-command discovery — cclaw installs
106
- // its entry points as skills here. Since v0.114 (Mar 2026) Codex
107
- // also exposes lifecycle hooks via `.codex/hooks.json`, behind
125
+ // its entry points as skills here. Current Codex releases also support
126
+ // native parallel subagents and project-local `.codex/agents/*.toml`
127
+ // custom agents; cclaw materializes its core roster there. Since v0.114
128
+ // (Mar 2026) Codex also exposes lifecycle hooks via `.codex/hooks.json`, behind
108
129
  // the `[features] codex_hooks = true` feature flag in
109
130
  // `~/.codex/config.toml`. cclaw writes that file on sync and
110
131
  // `hookSurface: "limited"` records the reality: SessionStart /
@@ -113,7 +134,7 @@ export const HARNESS_ADAPTERS = {
113
134
  commandDir: ".agents/skills",
114
135
  shimKind: "skill",
115
136
  capabilities: {
116
- nativeSubagentDispatch: "none",
137
+ nativeSubagentDispatch: "full",
117
138
  hookSurface: "limited",
118
139
  // Codex CLI exposes `request_user_input` — an experimental tool
119
140
  // that asks 1-3 short questions and returns the user's answers.
@@ -123,10 +144,29 @@ export const HARNESS_ADAPTERS = {
123
144
  // it into generated harness guidance. The shared plain-text
124
145
  // lettered list is the documented fallback when the tool is unavailable.
125
146
  structuredAsk: "request_user_input",
126
- subagentFallback: "role-switch"
147
+ subagentFallback: "native"
127
148
  }
128
149
  }
129
150
  };
151
+ export function harnessDispatchSurface(harnessId) {
152
+ switch (harnessId) {
153
+ case "claude":
154
+ return "Use Claude Code Task with the cclaw agent name as subagent_type; record fulfillmentMode: \"isolated\".";
155
+ case "cursor":
156
+ 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
+ case "opencode":
158
+ return "Use OpenCode subagents: invoke the generated .opencode/agents/<agent>.md agent via Task or @<agent>, run independent agents in parallel when safe, then record fulfillmentMode: \"isolated\".";
159
+ case "codex":
160
+ return "Use Codex native subagents: ask Codex to spawn the generated .codex/agents/<agent>.toml agent(s) by name, wait for all results, then record fulfillmentMode: \"isolated\".";
161
+ }
162
+ }
163
+ export function harnessDispatchFallback(harnessId) {
164
+ const adapter = HARNESS_ADAPTERS[harnessId];
165
+ if (adapter.capabilities.subagentFallback !== "role-switch") {
166
+ return "Role-switch is only a degradation path if the active runtime cannot expose the declared dispatch surface; include non-empty evidenceRefs when used.";
167
+ }
168
+ return "Use a visible role-switch pass with non-empty evidenceRefs because this harness has no true dispatch surface.";
169
+ }
130
170
  export function harnessTier(harnessId) {
131
171
  const capabilities = HARNESS_ADAPTERS[harnessId].capabilities;
132
172
  if (capabilities.nativeSubagentDispatch === "full" &&
@@ -223,7 +263,7 @@ If the same approach fails three times in a row (same command, same finding, sam
223
263
  ### Detail Level
224
264
 
225
265
  - This managed AGENTS block is intentionally minimal for cross-project use.
226
- - Harness coverage is tiered: Tier1 (claude), Tier2 (cursor/opencode/codex codex has Bash-only tool hooks), Tier3 (fallback/manual-only).
266
+ - Subagent dispatch coverage: Claude/OpenCode/Codex support native isolated workers; Cursor uses generic Task dispatch. Codex still has Bash-only tool hooks.
227
267
  - Detailed operating procedures live in \`.cclaw/skills/using-cclaw/SKILL.md\`.
228
268
  - Keep preambles brief; re-announce role/stage only when either changes.
229
269
  - Subagent orchestration patterns: \`.cclaw/skills/subagent-dev/SKILL.md\` and \`.cclaw/skills/parallel-dispatch/SKILL.md\`.
@@ -336,6 +376,50 @@ Load and execute:
336
376
  ${utilityShimBehavior(command)}
337
377
  `;
338
378
  }
379
+ function stageShimContent(harness, stage) {
380
+ const shimName = stageShimSkillName(stage);
381
+ const skillPath = `${RUNTIME_ROOT}/skills/${STAGE_TO_SKILL_FOLDER[stage]}/SKILL.md`;
382
+ return `---
383
+ name: ${shimName}
384
+ description: Generated shim for ${harness}. Flow stage pointer; normal advancement uses /cc-next.
385
+ source: generated-by-cclaw
386
+ ---
387
+
388
+ # cclaw ${stage}
389
+
390
+ This is a thin compatibility shim for the \`${stage}\` flow stage.
391
+
392
+ Load and follow the authoritative stage skill:
393
+
394
+ - \`${skillPath}\`
395
+
396
+ Normal stage resume and advancement uses \`/cc-next\`. Use \`/cc-next\` to read
397
+ \`.cclaw/state/flow-state.json\`, select the active stage, and advance only after
398
+ that stage's gates pass. Do not duplicate the stage protocol here.
399
+ `;
400
+ }
401
+ function codexStageSkillMarkdown(stage) {
402
+ const skillName = stageShimSkillName(stage);
403
+ const skillPath = `${RUNTIME_ROOT}/skills/${STAGE_TO_SKILL_FOLDER[stage]}/SKILL.md`;
404
+ return `---
405
+ name: ${skillName}
406
+ description: Thin cclaw stage shim for /cc-${stage}. Load ${skillPath}; normal stage resume and advancement uses /cc-next.
407
+ source: generated-by-cclaw
408
+ ---
409
+
410
+ # cclaw /cc-${stage} (Codex adapter)
411
+
412
+ This is a thin compatibility shim for the \`${stage}\` flow stage.
413
+
414
+ Load and follow the authoritative stage skill:
415
+
416
+ - \`${skillPath}\`
417
+
418
+ Normal stage resume and advancement uses \`/cc-next\`. Use \`/cc-next\` to read
419
+ \`.cclaw/state/flow-state.json\`, select the active stage, and advance only after
420
+ that stage's gates pass. Do not duplicate the stage protocol here.
421
+ `;
422
+ }
339
423
  /**
340
424
  * Frontmatter `description` that triggers the skill when the user types any
341
425
  * of the classic cclaw slash-tokens. Codex's skill matcher runs on the skill
@@ -398,11 +482,12 @@ for the current hook surface and limitations.
398
482
 
399
483
  ## Honest caveats
400
484
 
401
- - Codex has no subagent dispatch primitive. Mandatory delegations
402
- fall back to **role-switch** announce the role, act in-session,
403
- append a completed row with \`evidenceRefs\` to
404
- \`.cclaw/state/delegation-log.json\`. Silent auto-waiver is disabled
405
- (v0.33+).
485
+ - Codex has native parallel subagents. cclaw writes project custom agents
486
+ under \`.codex/agents/*.toml\`; ask Codex to spawn the relevant cclaw
487
+ agent(s) by name, wait for their results, write evidence into the active
488
+ artifact, then append completed delegation rows with \`fulfillmentMode:
489
+ "isolated"\`. Use role-switch only if this Codex build has subagents
490
+ unavailable or disabled, and then include non-empty \`evidenceRefs\`.
406
491
  - Codex's \`PreToolUse\` / \`PostToolUse\` hooks currently only intercept
407
492
  the \`Bash\` tool. \`Write\`, \`Edit\`, \`WebSearch\`, and MCP tool calls
408
493
  are **not** gated by hooks — use \`cclaw doctor --explain\` for what cclaw
@@ -432,6 +517,9 @@ async function writeCommandKindShims(commandDir, harness) {
432
517
  for (const shim of UTILITY_SHIMS) {
433
518
  await writeFileSafe(path.join(commandDir, shim.fileName), utilityShimContent(harness, shim.command, shim.skillFolder, shim.commandFile));
434
519
  }
520
+ for (const stage of FLOW_STAGES) {
521
+ await writeFileSafe(path.join(commandDir, stageShimFileName(stage)), stageShimContent(harness, stage));
522
+ }
435
523
  for (const legacy of LEGACY_HARNESS_SHIMS) {
436
524
  const legacyPath = path.join(commandDir, legacy);
437
525
  try {
@@ -448,6 +536,9 @@ async function writeSkillKindShims(commandDir) {
448
536
  for (const shim of UTILITY_SHIMS) {
449
537
  await writeFileSafe(path.join(commandDir, shim.skillName, "SKILL.md"), codexSkillMarkdown(shim.command, shim.skillName, shim.skillFolder, shim.commandFile));
450
538
  }
539
+ for (const stage of FLOW_STAGES) {
540
+ await writeFileSafe(path.join(commandDir, stageShimSkillName(stage), "SKILL.md"), codexStageSkillMarkdown(stage));
541
+ }
451
542
  }
452
543
  /**
453
544
  * Legacy codex surfaces cclaw wrote before v0.39.0 that Codex CLI never
@@ -505,12 +596,57 @@ async function cleanupLegacyCodexSurfaces(projectRoot) {
505
596
  // directory absent or non-empty
506
597
  }
507
598
  }
508
- async function syncAgentFiles(projectRoot) {
599
+ function codexAgentToml(agent) {
600
+ const instructions = `${agent.body}\n\n${enhancedAgentInstruction(agent.name)}`.trim();
601
+ const sandboxMode = agent.tools.some((tool) => ["Write", "Edit", "Bash"].includes(tool))
602
+ ? "workspace-write"
603
+ : "read-only";
604
+ return [
605
+ `name = ${JSON.stringify(agent.name)}`,
606
+ `description = ${JSON.stringify(agent.description)}`,
607
+ `sandbox_mode = ${JSON.stringify(sandboxMode)}`,
608
+ 'developer_instructions = """',
609
+ instructions.replace(/"""/gu, '\"\"\"'),
610
+ '"""',
611
+ ""
612
+ ].join("\n");
613
+ }
614
+ function opencodeAgentMarkdown(agent) {
615
+ const editPermission = agent.tools.some((tool) => ["Write", "Edit"].includes(tool)) ? "ask" : "deny";
616
+ const bashPermission = agent.tools.includes("Bash") ? "ask" : "deny";
617
+ return `---
618
+ description: ${JSON.stringify(agent.description)}
619
+ mode: subagent
620
+ permission:
621
+ edit: ${editPermission}
622
+ bash: ${bashPermission}
623
+ ---
624
+
625
+ ${agentMarkdown(agent)}`;
626
+ }
627
+ function enhancedAgentInstruction(agentName) {
628
+ return `You are the cclaw ${agentName} subagent. Follow the parent prompt as the task boundary, produce evidence suitable for .cclaw/state/delegation-log.json, and do not recursively orchestrate other agents unless the parent explicitly asks.`;
629
+ }
630
+ async function syncAgentFiles(projectRoot, harnesses) {
509
631
  const agentsDir = path.join(projectRoot, RUNTIME_ROOT, "agents");
510
632
  await ensureDir(agentsDir);
511
633
  for (const agent of CCLAW_AGENTS) {
512
634
  await writeFileSafe(path.join(agentsDir, `${agent.name}.md`), agentMarkdown(agent));
513
635
  }
636
+ if (harnesses.includes("opencode")) {
637
+ const opencodeAgentsDir = path.join(projectRoot, ".opencode/agents");
638
+ await ensureDir(opencodeAgentsDir);
639
+ for (const agent of CCLAW_AGENTS) {
640
+ await writeFileSafe(path.join(opencodeAgentsDir, `${agent.name}.md`), opencodeAgentMarkdown(agent));
641
+ }
642
+ }
643
+ if (harnesses.includes("codex")) {
644
+ const codexAgentsDir = path.join(projectRoot, ".codex/agents");
645
+ await ensureDir(codexAgentsDir);
646
+ for (const agent of CCLAW_AGENTS) {
647
+ await writeFileSafe(path.join(codexAgentsDir, `${agent.name}.toml`), codexAgentToml(agent));
648
+ }
649
+ }
514
650
  }
515
651
  export async function syncHarnessShims(projectRoot, harnesses) {
516
652
  // Legacy codex cleanup is unconditional — even installs that never enabled
@@ -529,6 +665,6 @@ export async function syncHarnessShims(projectRoot, harnesses) {
529
665
  await writeCommandKindShims(commandDir, harness);
530
666
  }
531
667
  }
532
- await syncAgentFiles(projectRoot);
668
+ await syncAgentFiles(projectRoot, harnesses);
533
669
  await syncAgentsMd(projectRoot, harnesses);
534
670
  }
package/dist/install.js CHANGED
@@ -6,6 +6,7 @@ import { CCLAW_VERSION, FLOW_VERSION, REQUIRED_DIRS, RUNTIME_ROOT } from "./cons
6
6
  import { writeConfig, createDefaultConfig, readConfig, configPath, detectLanguageRulePacks, detectAdvancedKeys } from "./config.js";
7
7
  import { learnSkillMarkdown } from "./content/learnings.js";
8
8
  import { nextCommandContract, nextCommandSkillMarkdown } from "./content/next-command.js";
9
+ import { stageCommandShimMarkdown } from "./content/stage-command.js";
9
10
  import { ideateCommandContract, ideateCommandSkillMarkdown } from "./content/ideate-command.js";
10
11
  import { startCommandContract, startCommandSkillMarkdown } from "./content/start-command.js";
11
12
  import { viewCommandContract, viewCommandSkillMarkdown } from "./content/view-command.js";
@@ -443,6 +444,9 @@ async function writeEntryCommands(projectRoot) {
443
444
  await writeFileSafe(runtimePath(projectRoot, "commands", "next.md"), nextCommandContract());
444
445
  await writeFileSafe(runtimePath(projectRoot, "commands", "ideate.md"), ideateCommandContract());
445
446
  await writeFileSafe(runtimePath(projectRoot, "commands", "view.md"), viewCommandContract());
447
+ for (const stage of FLOW_STAGES) {
448
+ await writeFileSafe(runtimePath(projectRoot, "commands", `${stage}.md`), stageCommandShimMarkdown(stage));
449
+ }
446
450
  }
447
451
  function toObject(value) {
448
452
  if (!value || typeof value !== "object" || Array.isArray(value)) {
@@ -547,11 +551,20 @@ function mergeOpenCodePluginConfig(existingDoc, pluginRelPath) {
547
551
  if (!normalized.has(pluginRelPath)) {
548
552
  pluginsRaw.push(pluginRelPath);
549
553
  }
550
- const changed = !normalized.has(pluginRelPath) || !Array.isArray(root.plugin);
554
+ const permission = toObject(root.permission) ?? {};
555
+ const permissionChanged = permission.question !== "allow";
556
+ const changed = !normalized.has(pluginRelPath) ||
557
+ !Array.isArray(root.plugin) ||
558
+ permissionChanged ||
559
+ !toObject(root.permission);
551
560
  return {
552
561
  merged: {
553
562
  ...root,
554
- plugin: pluginsRaw
563
+ plugin: pluginsRaw,
564
+ permission: {
565
+ ...permission,
566
+ question: "allow"
567
+ }
555
568
  },
556
569
  changed
557
570
  };
@@ -933,7 +946,6 @@ async function cleanLegacyArtifacts(projectRoot) {
933
946
  await removeBestEffort(legacyPlugin);
934
947
  }
935
948
  for (const legacyRuntimeFile of [
936
- ...FLOW_STAGES.map((stage) => runtimePath(projectRoot, "commands", `${stage}.md`)),
937
949
  ...DEPRECATED_COMMAND_FILES.map((file) => runtimePath(projectRoot, "commands", file)),
938
950
  ...DEPRECATED_SKILL_FILES.map((segments) => runtimePath(projectRoot, "skills", ...segments)),
939
951
  ...DEPRECATED_STATE_FILES.map((file) => runtimePath(projectRoot, "state", file)),
@@ -1248,7 +1260,7 @@ export async function uninstallCclaw(projectRoot) {
1248
1260
  try {
1249
1261
  const entries = await fs.readdir(codexSkillsRoot);
1250
1262
  for (const entry of entries) {
1251
- if (/^(?:cclaw-)?cc(?:-(?:next|view|ops|ideate))?$/u.test(entry)) {
1263
+ if (/^(?:cclaw-)?cc(?:-(?:next|view|ops|ideate|brainstorm|scope|design|spec|plan|tdd|review|ship))?$/u.test(entry)) {
1252
1264
  await fs.rm(path.join(codexSkillsRoot, entry), { recursive: true, force: true });
1253
1265
  }
1254
1266
  }
@@ -1258,6 +1270,17 @@ export async function uninstallCclaw(projectRoot) {
1258
1270
  }
1259
1271
  await removeIfEmpty(codexSkillsRoot);
1260
1272
  await removeIfEmpty(path.join(projectRoot, ".agents"));
1273
+ const managedAgentNames = [
1274
+ "planner",
1275
+ "reviewer",
1276
+ "security-reviewer",
1277
+ "test-author",
1278
+ "doc-updater"
1279
+ ];
1280
+ for (const agentName of managedAgentNames) {
1281
+ await removeBestEffort(path.join(projectRoot, ".opencode/agents", `${agentName}.md`));
1282
+ await removeBestEffort(path.join(projectRoot, ".codex/agents", `${agentName}.toml`));
1283
+ }
1261
1284
  for (const pluginPath of [
1262
1285
  path.join(projectRoot, ".opencode/plugins/viby-plugin.mjs"),
1263
1286
  path.join(projectRoot, ".opencode/plugins/opencode-plugin.mjs"),
@@ -1284,8 +1307,10 @@ export async function uninstallCclaw(projectRoot) {
1284
1307
  ".cursor/rules",
1285
1308
  ".cursor/commands",
1286
1309
  ".cursor",
1310
+ ".codex/agents",
1287
1311
  ".codex/commands",
1288
1312
  ".codex",
1313
+ ".opencode/agents",
1289
1314
  ".opencode/plugins",
1290
1315
  ".opencode/commands",
1291
1316
  ".opencode"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.51.22",
3
+ "version": "0.51.23",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {