cclaw-cli 0.48.5 → 0.48.7

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.
Files changed (37) hide show
  1. package/dist/artifact-linter.js +32 -0
  2. package/dist/config.d.ts +1 -1
  3. package/dist/config.js +44 -5
  4. package/dist/content/hooks.d.ts +2 -2
  5. package/dist/content/hooks.js +293 -89
  6. package/dist/content/ideate-command.js +11 -0
  7. package/dist/content/iron-laws.d.ts +142 -0
  8. package/dist/content/iron-laws.js +191 -0
  9. package/dist/content/meta-skill.js +1 -0
  10. package/dist/content/next-command.js +12 -0
  11. package/dist/content/observe.js +555 -45
  12. package/dist/content/ops-command.js +11 -0
  13. package/dist/content/session-hooks.js +3 -1
  14. package/dist/content/stage-schema.d.ts +16 -0
  15. package/dist/content/stage-schema.js +82 -5
  16. package/dist/content/stages/review.js +4 -4
  17. package/dist/content/stages/tdd.js +7 -7
  18. package/dist/content/start-command.js +12 -0
  19. package/dist/content/subagents.js +26 -0
  20. package/dist/content/templates.js +8 -0
  21. package/dist/content/view-command.js +11 -0
  22. package/dist/doctor.js +6 -2
  23. package/dist/harness-adapters.js +3 -0
  24. package/dist/install.js +11 -1
  25. package/dist/internal/advance-stage.js +14 -2
  26. package/dist/internal/envelope-validate.d.ts +7 -0
  27. package/dist/internal/envelope-validate.js +66 -0
  28. package/dist/internal/knowledge-digest.d.ts +7 -0
  29. package/dist/internal/knowledge-digest.js +93 -0
  30. package/dist/internal/tdd-red-evidence.d.ts +7 -0
  31. package/dist/internal/tdd-red-evidence.js +130 -0
  32. package/dist/knowledge-store.d.ts +8 -0
  33. package/dist/knowledge-store.js +95 -0
  34. package/dist/tdd-cycle.d.ts +7 -0
  35. package/dist/tdd-cycle.js +29 -0
  36. package/dist/types.d.ts +6 -0
  37. package/package.json +1 -1
@@ -33,6 +33,17 @@ Subcommands:
33
33
  - \`rewind\` -> \`${RUNTIME_ROOT}/commands/rewind.md\`
34
34
  3. Unknown subcommand -> print supported values and stop.
35
35
 
36
+ ## Headless mode
37
+
38
+ For skill-to-skill dispatch, emit exactly one JSON envelope:
39
+
40
+ \`\`\`json
41
+ {"version":"1","kind":"stage-output","stage":"ship","payload":{"command":"/cc-ops","subcommand":"archive","status":"completed"},"emittedAt":"<ISO-8601>"}
42
+ \`\`\`
43
+
44
+ Validate envelopes with:
45
+ \`cclaw internal envelope-validate --stdin\`
46
+
36
47
  ## Primary skill
37
48
 
38
49
  **${RUNTIME_ROOT}/skills/${OPS_SKILL_FOLDER}/SKILL.md**
@@ -29,7 +29,8 @@ When a new session begins in any harness:
29
29
  2. **Load knowledge:** Stream the tail of \`.cclaw/knowledge.jsonl\` (strict JSONL store) and surface the most relevant rules/patterns.
30
30
  3. **Check for in-progress work:** If the last stage is incomplete, remind the user and offer to resume.
31
31
  4. **Load suggestion memory:** Read \`.cclaw/state/suggestion-memory.json\` and honor \`enabled=false\` opt-out.
32
- 5. **Read AGENTS.md:** The cclaw block contains routing and rules follow them.
32
+ 5. **Load iron laws:** Read \`.cclaw/state/iron-laws.json\` to know which laws are strict in this repo.
33
+ 6. **Read AGENTS.md:** The cclaw block contains routing and rules — follow them.
33
34
 
34
35
  ### What to show the user at session start
35
36
 
@@ -133,5 +134,6 @@ Session boundary behavior (real hooks inject context automatically; guidelines c
133
134
  - **Resume:** Re-read state, verify artifact, re-load knowledge, continue from last step.
134
135
 
135
136
  Skill: \`.cclaw/skills/session/SKILL.md\`
137
+ Policy: \`.cclaw/skills/iron-laws/SKILL.md\`
136
138
  `;
137
139
  }
@@ -1,6 +1,22 @@
1
1
  import type { FlowStage, FlowTrack, TransitionRule } from "../types.js";
2
2
  import type { StageAutoSubagentDispatch, StageSchema } from "./stages/schema-types.js";
3
3
  export type { ArtifactValidation, CrossStageTrace, ReviewSection, StageAutoSubagentDispatch, StageGate, StageSchema, StageSchemaInput } from "./stages/schema-types.js";
4
+ export declare const SKILL_ENVELOPE_KINDS: readonly ["stage-output", "gate-result", "delegation-record"];
5
+ export type SkillEnvelopeKind = (typeof SKILL_ENVELOPE_KINDS)[number];
6
+ export interface SkillEnvelope {
7
+ version: "1";
8
+ kind: SkillEnvelopeKind;
9
+ stage: FlowStage;
10
+ payload: unknown;
11
+ emittedAt: string;
12
+ agent?: string;
13
+ }
14
+ export interface SkillEnvelopeValidation {
15
+ ok: boolean;
16
+ errors: string[];
17
+ }
18
+ export declare function validateSkillEnvelope(value: unknown): SkillEnvelopeValidation;
19
+ export declare function parseSkillEnvelope(raw: string): SkillEnvelope | null;
4
20
  /** Transition guard: agents with `mode: "mandatory"` in auto-subagent dispatch for this stage. */
5
21
  export declare function mandatoryDelegationsForStage(stage: FlowStage): string[];
6
22
  export declare function stageSchema(stage: FlowStage, track?: FlowTrack): StageSchema;
@@ -2,6 +2,65 @@ import { FLOW_STAGES, FLOW_TRACKS, TRACK_STAGES } from "../types.js";
2
2
  import { STAGE_TO_SKILL_FOLDER } from "../constants.js";
3
3
  import { BRAINSTORM, SCOPE, DESIGN, SPEC, PLAN, TDD, REVIEW, SHIP } from "./stages/index.js";
4
4
  import { tddStageForTrack } from "./stages/tdd.js";
5
+ // ---------------------------------------------------------------------------
6
+ // NOTE: The former QUESTION_FORMAT_SPEC / ERROR_BUDGET_SPEC exports were
7
+ // hoisted into `src/content/meta-skill.ts` (Shared Decision + Tool-Use
8
+ // Protocol). They are no longer re-exported from here to avoid duplication
9
+ // and drift. Stage skills cite the meta-skill by path instead.
10
+ // ---------------------------------------------------------------------------
11
+ export const SKILL_ENVELOPE_KINDS = [
12
+ "stage-output",
13
+ "gate-result",
14
+ "delegation-record"
15
+ ];
16
+ const FLOW_STAGE_SET = new Set(FLOW_STAGES);
17
+ const SKILL_ENVELOPE_KIND_SET = new Set(SKILL_ENVELOPE_KINDS);
18
+ function asRecord(value) {
19
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
20
+ return null;
21
+ }
22
+ return value;
23
+ }
24
+ export function validateSkillEnvelope(value) {
25
+ const errors = [];
26
+ const record = asRecord(value);
27
+ if (!record) {
28
+ return { ok: false, errors: ["envelope must be a JSON object"] };
29
+ }
30
+ if (record.version !== "1") {
31
+ errors.push('envelope.version must equal "1".');
32
+ }
33
+ if (typeof record.kind !== "string" || !SKILL_ENVELOPE_KIND_SET.has(record.kind)) {
34
+ errors.push(`envelope.kind must be one of: ${SKILL_ENVELOPE_KINDS.join(", ")}.`);
35
+ }
36
+ if (typeof record.stage !== "string" || !FLOW_STAGE_SET.has(record.stage)) {
37
+ errors.push(`envelope.stage must be one of: ${FLOW_STAGES.join(", ")}.`);
38
+ }
39
+ if (!Object.prototype.hasOwnProperty.call(record, "payload")) {
40
+ errors.push("envelope.payload is required.");
41
+ }
42
+ if (typeof record.emittedAt !== "string" || Number.isNaN(Date.parse(record.emittedAt))) {
43
+ errors.push("envelope.emittedAt must be an ISO-8601 timestamp string.");
44
+ }
45
+ if (record.agent !== undefined && typeof record.agent !== "string") {
46
+ errors.push("envelope.agent must be a string when present.");
47
+ }
48
+ return { ok: errors.length === 0, errors };
49
+ }
50
+ export function parseSkillEnvelope(raw) {
51
+ let parsed;
52
+ try {
53
+ parsed = JSON.parse(raw);
54
+ }
55
+ catch {
56
+ return null;
57
+ }
58
+ const validation = validateSkillEnvelope(parsed);
59
+ if (!validation.ok) {
60
+ return null;
61
+ }
62
+ return parsed;
63
+ }
5
64
  const ARTIFACT_STAGE_BY_PATH = {
6
65
  ".cclaw/artifacts/01-brainstorm.md": "brainstorm",
7
66
  ".cclaw/artifacts/02-scope.md": "scope",
@@ -191,9 +250,26 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
191
250
  {
192
251
  agent: "test-author",
193
252
  mode: "mandatory",
194
- when: "Always during TDD cycle (RED → GREEN → REFACTOR).",
195
- purpose: "Guarantee failing tests, traceable implementation, and full-suite verification.",
196
- requiresUserGate: false
253
+ when: "Always during TDD cycle (RED phase).",
254
+ purpose: "Produce failing RED tests only; no production writes.",
255
+ requiresUserGate: false,
256
+ skill: "tdd-red-phase"
257
+ },
258
+ {
259
+ agent: "test-author",
260
+ mode: "mandatory",
261
+ when: "Always during TDD cycle (GREEN phase).",
262
+ purpose: "Implement minimum production changes to satisfy RED and prove full-suite GREEN.",
263
+ requiresUserGate: false,
264
+ skill: "tdd-green-phase"
265
+ },
266
+ {
267
+ agent: "test-author",
268
+ mode: "mandatory",
269
+ when: "Always during TDD cycle (REFACTOR phase).",
270
+ purpose: "Refactor only after GREEN proof, preserving behavior and test pass state.",
271
+ requiresUserGate: false,
272
+ skill: "tdd-refactor-phase"
197
273
  },
198
274
  {
199
275
  agent: "doc-updater",
@@ -208,8 +284,9 @@ const STAGE_AUTO_SUBAGENT_DISPATCH = {
208
284
  agent: "reviewer",
209
285
  mode: "mandatory",
210
286
  when: "Always in review stage.",
211
- purpose: "Run spec compliance and code-quality passes with file evidence.",
212
- requiresUserGate: false
287
+ purpose: "Layer 1 spec compliance pass plus coordination of parallel Layer 2 fan-out (correctness, performance, architecture, external-safety) with source-tagged findings.",
288
+ requiresUserGate: false,
289
+ skill: "review-spec-pass"
213
290
  },
214
291
  {
215
292
  agent: "security-reviewer",
@@ -16,8 +16,7 @@ export const REVIEW = {
16
16
  ],
17
17
  whenNotToUse: [
18
18
  "There is no implementation diff to review",
19
- "TDD stage evidence is missing or stale",
20
- "The goal is direct release execution without layered quality checks"
19
+ "TDD stage evidence is missing or stale"
21
20
  ],
22
21
  checklist: [
23
22
  "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.",
@@ -54,7 +53,7 @@ export const REVIEW = {
54
53
  "Layer 2b: check security — validation, auth, secrets, injection.",
55
54
  "Layer 2c: check performance — queries, memory, caching, hot paths.",
56
55
  "Layer 2d: check architecture fit — design compliance, coupling, interfaces.",
57
- "Reconcile multi-agent findings into `.cclaw/artifacts/07-review-army.json` (dedup + confidence + conflict notes).",
56
+ "Reconcile multi-agent findings into `.cclaw/artifacts/07-review-army.json` (dedup + confidence + conflict notes + source tags from spec/correctness/security/performance/architecture/external-safety passes).",
58
57
  "Classify and prioritize all findings.",
59
58
  "Write review report artifact with explicit verdict.",
60
59
  "If verdict is BLOCKED, include the remediation route token `ROUTE_BACK_TO_TDD` and the rewind command payload."
@@ -62,6 +61,7 @@ export const REVIEW = {
62
61
  requiredGates: [
63
62
  { id: "review_layer1_spec_compliance", description: "Spec compliance check completed with per-criterion verdict." },
64
63
  { id: "review_layer2_security", description: "Security review completed." },
64
+ { id: "review_layer_coverage_complete", description: "Layer coverage map in 07-review-army.json confirms spec/correctness/security/performance/architecture/external-safety passes." },
65
65
  { id: "review_criticals_resolved", description: "No unresolved critical blockers remain." },
66
66
  { id: "review_army_json_valid", description: "07-review-army.json passes schema validation (validateReviewArmy)." },
67
67
  { id: "review_trace_matrix_clean", description: "Trace matrix has no orphaned criteria/tasks/test slices for the active run." }
@@ -202,7 +202,7 @@ export const REVIEW = {
202
202
  artifactValidation: [
203
203
  { section: "Layer 1 Verdict", required: true, validationRule: "Per-criterion pass/fail with references." },
204
204
  { section: "Layer 2 Findings", required: false, validationRule: "Each finding has severity, description, and resolution status. Security coverage must include either explicit security findings or `NO_CHANGE_ATTESTATION: <reason>` when no security-relevant changes were found." },
205
- { section: "Review Army Contract", required: true, validationRule: "Structured findings include id/severity/confidence/fingerprint/reportedBy/status with dedup reconciliation summary." },
205
+ { section: "Review Army Contract", required: true, validationRule: "Structured findings include id/severity/confidence/fingerprint/reportedBy/status and source tags from {spec, correctness, security, performance, architecture, external-safety} with dedup reconciliation summary." },
206
206
  { section: "Review Readiness Dashboard", required: false, validationRule: "Includes a per-pass table (Layer 1 / Layer 2 / Adversarial / Schema) with a 'Completed at' column, a Delegation log snapshot block (path .cclaw/state/delegation-log.json with required/completed/waived/pending), a Staleness signal block (commit at last review pass and current commit), and a Headline with open critical blockers + ship recommendation. At minimum, the section text must contain the substrings 'Completed at', 'delegation-log.json', 'commit at last review pass', and 'Ship recommendation'." },
207
207
  { section: "Completeness Score", required: false, validationRule: "Records AC coverage, task coverage, test-slice coverage, and adversarial-review pass status as numeric or boolean values. At minimum, a line like 'AC coverage: N/M' or 'AC coverage: 100%'." },
208
208
  { 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." },
@@ -22,13 +22,13 @@ export const TDD = {
22
22
  checklist: [
23
23
  "Select plan slice — pick one task from the plan. Do not batch multiple tasks.",
24
24
  "Map to acceptance criterion — identify the specific spec criterion this test proves.",
25
- "Dispatch mandatory `test-author` subagent in `TEST_RED_ONLY` mode — produce failing behavior tests and RED evidence only (no production edits).",
25
+ "Dispatch mandatory `tdd-red` execution (or `test-author` in TEST_RED_ONLY mode) — produce failing behavior tests and RED evidence only (no production edits). Set `CCLAW_ACTIVE_AGENT=tdd-red` when supported.",
26
26
  "RED: Capture failure output — copy the exact failure output as RED evidence. Record in artifact.",
27
- "Dispatch `test-author` subagent in `BUILD_GREEN_REFACTOR` mode — minimal implementation + full-suite GREEN + refactor notes.",
27
+ "Dispatch `tdd-green` execution (or `test-author` in BUILD_GREEN_REFACTOR mode) — minimal implementation + full-suite GREEN. Set `CCLAW_ACTIVE_AGENT=tdd-green` when supported.",
28
28
  "GREEN: Run full suite — execute ALL tests, not just the ones you wrote. The full suite must be GREEN.",
29
29
  "GREEN: Verify no regressions — if any existing test breaks, fix the regression before proceeding.",
30
30
  "Run verification-before-completion discipline for the slice — capture a fresh test command, commit SHA, and explicit PASS/FAIL status before completion claims.",
31
- "REFACTOR: Improve code quality without changing behavior. Document what you changed and why.",
31
+ "REFACTOR: Dispatch `tdd-refactor` execution (or dedicated refactor mode) to improve code quality without behavior changes. Set `CCLAW_ACTIVE_AGENT=tdd-refactor` when supported.",
32
32
  "Record evidence — capture RED failure, GREEN output, and REFACTOR notes in the TDD artifact.",
33
33
  "Annotate traceability — link to plan task ID and spec criterion.",
34
34
  "Per-Slice Review (conditional) — if `.cclaw/config.yaml::sliceReview.enabled` is true and the slice meets any trigger (touchCount >= filesChangedThreshold, touchPaths match touchTriggers, or highRisk=true), append a `## Per-Slice Review` entry for this slice before moving on (see the dedicated section below).",
@@ -36,7 +36,7 @@ export const TDD = {
36
36
  ],
37
37
  interactionProtocol: [
38
38
  "Pick one planned slice at a time.",
39
- "Controller owns orchestration; execution runs through the mandatory `test-author` delegation for RED then GREEN/REFACTOR modes.",
39
+ "Controller owns orchestration; execution runs through phase-specific delegation (`tdd-red` -> `tdd-green` -> `tdd-refactor`) or equivalent `test-author` modes.",
40
40
  "Write behavior-focused tests before changing implementation (RED).",
41
41
  "Capture and store failing output as RED evidence.",
42
42
  "Apply minimal change to satisfy RED tests (GREEN).",
@@ -49,12 +49,12 @@ export const TDD = {
49
49
  ],
50
50
  process: [
51
51
  "Select slice and map to acceptance criterion.",
52
- "Dispatch `test-author` in TEST_RED_ONLY mode and produce failing test(s) for expected reason (RED).",
52
+ "Dispatch `tdd-red` (or `test-author` TEST_RED_ONLY mode) and produce failing test(s) for expected reason (RED).",
53
53
  "Run tests and capture failure output.",
54
- "Dispatch `test-author` in BUILD_GREEN_REFACTOR mode and implement smallest change needed for GREEN.",
54
+ "Dispatch `tdd-green` (or `test-author` BUILD_GREEN_REFACTOR mode) and implement smallest change needed for GREEN.",
55
55
  "Run full tests and build checks.",
56
56
  "Run a fresh verification-before-completion check and capture command + commit SHA + PASS/FAIL in guard evidence.",
57
- "Perform refactor pass preserving behavior.",
57
+ "Dispatch `tdd-refactor` pass preserving behavior.",
58
58
  "Record RED, GREEN, and REFACTOR evidence in artifact.",
59
59
  "Annotate traceability to plan task and spec criterion; on `sliceReview` triggers, append a Per-Slice Review entry before closing the slice."
60
60
  ],
@@ -100,6 +100,18 @@ If during any stage the agent discovers evidence that contradicts the initial Ph
100
100
  2. If flow state is missing → run \`cclaw init\` guidance and stop.
101
101
  3. Behave exactly like \`/cc-next\`: check current stage gates, resume if incomplete, advance if complete.
102
102
 
103
+ ## Headless mode
104
+
105
+ When called by another skill or subagent in machine mode, emit exactly one
106
+ JSON envelope (no prose) and stop:
107
+
108
+ \`\`\`json
109
+ {"version":"1","kind":"stage-output","stage":"brainstorm","payload":{"command":"/cc","track":"standard","action":"start_or_resume"},"emittedAt":"<ISO-8601>"}
110
+ \`\`\`
111
+
112
+ Validate envelopes with:
113
+ \`cclaw internal envelope-validate --stdin\`
114
+
103
115
  ## Primary skill
104
116
 
105
117
  **${RUNTIME_ROOT}/skills/${START_SKILL_FOLDER}/SKILL.md**
@@ -39,6 +39,32 @@ For cclaw flow stages, machine-only specialist work should auto-dispatch without
39
39
 
40
40
  Human input remains mandatory only at explicit approval gates (plan approval, user challenge resolution, release finalization mode).
41
41
 
42
+ ### Review parallel fan-out protocol
43
+
44
+ In review stage, prefer a fixed six-pass fan-out before reconciliation:
45
+
46
+ 1. \`review-spec\` (Layer 1)
47
+ 2. \`review-correctness\` (Layer 2a)
48
+ 3. \`review-security\` (Layer 2b)
49
+ 4. \`review-performance\` (Layer 2c)
50
+ 5. \`review-architecture\` (Layer 2d)
51
+ 6. \`review-external-safety\` (Layer 2e)
52
+
53
+ Dispatch these in parallel where the harness supports isolated workers, then run
54
+ one reconciliation pass that merges findings into \`.cclaw/artifacts/07-review-army.json\`
55
+ with explicit source tags per finding.
56
+
57
+ ### TDD phase fan-out protocol
58
+
59
+ Treat RED, GREEN, and REFACTOR as separate delegated intents:
60
+
61
+ - \`tdd-red\`: tests only, no production writes
62
+ - \`tdd-green\`: minimal production implementation, no new RED tests
63
+ - \`tdd-refactor\`: cleanup only after GREEN is proven
64
+
65
+ Set \`CCLAW_ACTIVE_AGENT\` to the active phase name when possible so workflow-guard
66
+ can enforce phase-appropriate write boundaries.
67
+
42
68
  ## Model & Harness Routing Notes
43
69
 
44
70
  ### Harness routing
@@ -609,6 +609,14 @@ inputs_hash: sha256:pending
609
609
  "duplicatesCollapsed": 0,
610
610
  "conflicts": [],
611
611
  "multiSpecialistConfirmed": [],
612
+ "layerCoverage": {
613
+ "spec": false,
614
+ "correctness": false,
615
+ "security": false,
616
+ "performance": false,
617
+ "architecture": false,
618
+ "external-safety": false
619
+ },
612
620
  "shipBlockers": []
613
621
  }
614
622
  }
@@ -27,6 +27,17 @@ Subcommands:
27
27
  - \`diff\` -> load \`${RUNTIME_ROOT}/commands/diff.md\` + \`${RUNTIME_ROOT}/skills/flow-diff/SKILL.md\`
28
28
  3. Unknown subcommand -> print supported values and stop.
29
29
 
30
+ ## Headless mode
31
+
32
+ For machine orchestration, emit one JSON envelope:
33
+
34
+ \`\`\`json
35
+ {"version":"1","kind":"stage-output","stage":"review","payload":{"command":"/cc-view","subcommand":"status","summary":"<short>"},"emittedAt":"<ISO-8601>"}
36
+ \`\`\`
37
+
38
+ Validate envelopes with:
39
+ \`cclaw internal envelope-validate --stdin\`
40
+
30
41
  ## Primary skill
31
42
 
32
43
  **${RUNTIME_ROOT}/skills/${VIEW_SKILL_FOLDER}/SKILL.md**
package/dist/doctor.js CHANGED
@@ -673,6 +673,7 @@ export async function doctorChecks(projectRoot, options = {}) {
673
673
  }
674
674
  // Hook scripts
675
675
  for (const script of [
676
+ "_lib.sh",
676
677
  "session-start.sh",
677
678
  "stop-checkpoint.sh",
678
679
  "run-hook.cmd",
@@ -877,6 +878,7 @@ export async function doctorChecks(projectRoot, options = {}) {
877
878
  const codexStopCmds = collectHookCommands(codexHooks.Stop);
878
879
  const codexWiringOk = codexSessionCmds.some((cmd) => cmd.includes("session-start.sh")) &&
879
880
  codexUserPromptCmds.some((cmd) => cmd.includes("prompt-guard.sh")) &&
881
+ codexUserPromptCmds.some((cmd) => cmd.includes("workflow-guard.sh")) &&
880
882
  codexUserPromptCmds.some((cmd) => cmd.includes("verify-current-state --quiet")) &&
881
883
  codexPreCmds.some((cmd) => cmd.includes("prompt-guard.sh")) &&
882
884
  codexPreCmds.some((cmd) => cmd.includes("workflow-guard.sh")) &&
@@ -885,7 +887,7 @@ export async function doctorChecks(projectRoot, options = {}) {
885
887
  checks.push({
886
888
  name: "hook:wiring:codex",
887
889
  ok: codexWiringOk,
888
- details: `${codexHooksFile} must wire SessionStart, UserPromptSubmit(prompt-guard + verify-current-state), PreToolUse(prompt/workflow), PostToolUse(context-monitor), and Stop(stop-checkpoint). PreToolUse/PostToolUse run Bash-only in Codex v0.114+`
890
+ details: `${codexHooksFile} must wire SessionStart, UserPromptSubmit(prompt/workflow/verify-current-state), PreToolUse(prompt/workflow), PostToolUse(context-monitor), and Stop(stop-checkpoint). PreToolUse/PostToolUse run Bash-only in Codex v0.114+`
889
891
  });
890
892
  // Feature flag warning: Codex ignores `.codex/hooks.json` unless the
891
893
  // user has `[features] codex_hooks = true` in `~/.codex/config.toml`.
@@ -965,10 +967,12 @@ export async function doctorChecks(projectRoot, options = {}) {
965
967
  content.includes("workflow-guard.sh") &&
966
968
  content.includes("context-monitor.sh") &&
967
969
  content.includes("pre-compact.sh") &&
970
+ content.includes('"session.created"') &&
968
971
  content.includes('"session.idle"') &&
969
972
  content.includes('"session.resumed"') &&
970
973
  content.includes('"session.compacted"') &&
971
974
  content.includes('"session.cleared"') &&
975
+ content.includes('"session.updated"') &&
972
976
  content.includes('"experimental.chat.system.transform"');
973
977
  singleHandlerPathOk =
974
978
  !content.includes('eventType === "tool.execute.before"') &&
@@ -982,7 +986,7 @@ export async function doctorChecks(projectRoot, options = {}) {
982
986
  checks.push({
983
987
  name: "lifecycle:opencode:rehydration_events",
984
988
  ok,
985
- details: `${file} must include event lifecycle handler, tool.execute.before/after with prompt/workflow/context hooks, session.idle checkpoint, and transform rehydration`
989
+ details: `${file} must include event lifecycle handler, session.created/updated/resumed/cleared/compacted rehydration, tool.execute.before/after with prompt/workflow/context hooks, session.idle checkpoint, and transform rehydration`
986
990
  });
987
991
  checks.push({
988
992
  name: "hook:opencode:single_tool_handler_path",
@@ -2,6 +2,7 @@ import fs from "node:fs/promises";
2
2
  import path from "node:path";
3
3
  import { RUNTIME_ROOT } from "./constants.js";
4
4
  import { CCLAW_AGENTS, agentMarkdown } from "./content/core-agents.js";
5
+ import { ironLawsAgentsMdBlock } from "./content/iron-laws.js";
5
6
  import { ensureDir, exists, writeFileSafe } from "./fs-utils.js";
6
7
  export const CCLAW_MARKER_START = "<!-- cclaw-start -->";
7
8
  export const CCLAW_MARKER_END = "<!-- cclaw-end -->";
@@ -200,6 +201,8 @@ Before responding to a coding request:
200
201
  2. Use \`/cc\` to start or \`/cc-next\` to continue the flow.
201
202
  3. If no stage applies, respond normally.
202
203
 
204
+ ${ironLawsAgentsMdBlock()}
205
+
203
206
  ### Task Classification (before \`/cc\`)
204
207
 
205
208
  | Class | Examples | Route |
package/dist/install.js CHANGED
@@ -23,7 +23,8 @@ import { archiveCommandContract, archiveCommandSkillMarkdown } from "./content/a
23
23
  import { rewindCommandContract, rewindCommandSkillMarkdown } from "./content/rewind-command.js";
24
24
  import { subagentDrivenDevSkill, parallelAgentsSkill } from "./content/subagents.js";
25
25
  import { sessionHooksSkillMarkdown } from "./content/session-hooks.js";
26
- import { sessionStartScript, stopCheckpointScript, runHookDispatcherScript, stageCompleteScript, preCompactScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
26
+ import { ironLawRuntimeDocument, ironLawsSkillMarkdown } from "./content/iron-laws.js";
27
+ import { hookLibScript, sessionStartScript, stopCheckpointScript, runHookDispatcherScript, stageCompleteScript, preCompactScript, opencodePluginJs, claudeHooksJson, codexHooksJson, cursorHooksJson } from "./content/hooks.js";
27
28
  import { contextMonitorScript, promptGuardScript, workflowGuardScript } from "./content/observe.js";
28
29
  import { META_SKILL_NAME, usingCclawSkillMarkdown } from "./content/meta-skill.js";
29
30
  import { decisionProtocolMarkdown, completionProtocolMarkdown, ethosProtocolMarkdown } from "./content/protocols.js";
@@ -255,6 +256,7 @@ async function writeSkills(projectRoot, config) {
255
256
  await writeFileSafe(runtimePath(projectRoot, "skills", "subagent-dev", "SKILL.md"), subagentDrivenDevSkill());
256
257
  await writeFileSafe(runtimePath(projectRoot, "skills", "parallel-dispatch", "SKILL.md"), parallelAgentsSkill());
257
258
  await writeFileSafe(runtimePath(projectRoot, "skills", "session", "SKILL.md"), sessionHooksSkillMarkdown());
259
+ await writeFileSafe(runtimePath(projectRoot, "skills", "iron-laws", "SKILL.md"), ironLawsSkillMarkdown());
258
260
  await writeFileSafe(runtimePath(projectRoot, "skills", META_SKILL_NAME, "SKILL.md"), usingCclawSkillMarkdown());
259
261
  await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "decision.md"), decisionProtocolMarkdown());
260
262
  await writeFileSafe(runtimePath(projectRoot, "references", "protocols", "completion.md"), completionProtocolMarkdown());
@@ -621,7 +623,14 @@ async function writeMergedHookJson(projectRoot, hookFilePath, generatedJson) {
621
623
  async function writeHooks(projectRoot, config) {
622
624
  const harnesses = config.harnesses;
623
625
  const hooksDir = runtimePath(projectRoot, "hooks");
626
+ const stateDir = runtimePath(projectRoot, "state");
624
627
  await ensureDir(hooksDir);
628
+ await ensureDir(stateDir);
629
+ await writeFileSafe(runtimePath(projectRoot, "state", "iron-laws.json"), `${JSON.stringify(ironLawRuntimeDocument({
630
+ mode: config.ironLaws?.mode,
631
+ strictLaws: config.ironLaws?.strictLaws
632
+ }), null, 2)}\n`);
633
+ await writeFileSafe(path.join(hooksDir, "_lib.sh"), hookLibScript());
625
634
  await writeFileSafe(path.join(hooksDir, "session-start.sh"), sessionStartScript());
626
635
  await writeFileSafe(path.join(hooksDir, "stop-checkpoint.sh"), stopCheckpointScript());
627
636
  await writeFileSafe(path.join(hooksDir, "run-hook.cmd"), runHookDispatcherScript());
@@ -641,6 +650,7 @@ async function writeHooks(projectRoot, config) {
641
650
  await writeFileSafe(path.join(hooksDir, "opencode-plugin.mjs"), opencodePluginSource);
642
651
  try {
643
652
  for (const script of [
653
+ "_lib.sh",
644
654
  "session-start.sh",
645
655
  "stop-checkpoint.sh",
646
656
  "run-hook.cmd",
@@ -10,6 +10,9 @@ import { getAvailableTransitions, getTransitionGuards, isFlowTrack } from "../fl
10
10
  import { appendKnowledge } from "../knowledge-store.js";
11
11
  import { readFlowState, writeFlowState } from "../runs.js";
12
12
  import { FLOW_STAGES } from "../types.js";
13
+ import { runEnvelopeValidateCommand } from "./envelope-validate.js";
14
+ import { runKnowledgeDigestCommand } from "./knowledge-digest.js";
15
+ import { runTddRedEvidenceCommand } from "./tdd-red-evidence.js";
13
16
  function unique(values) {
14
17
  return [...new Set(values)];
15
18
  }
@@ -621,7 +624,7 @@ async function runVerifyCurrentState(projectRoot, args, io) {
621
624
  export async function runInternalCommand(projectRoot, argv, io) {
622
625
  const [subcommand, ...tokens] = argv;
623
626
  if (!subcommand) {
624
- io.stderr.write("cclaw internal requires a subcommand: advance-stage | verify-flow-state-diff | verify-current-state\n");
627
+ io.stderr.write("cclaw internal requires a subcommand: advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence\n");
625
628
  return 1;
626
629
  }
627
630
  try {
@@ -634,7 +637,16 @@ export async function runInternalCommand(projectRoot, argv, io) {
634
637
  if (subcommand === "verify-current-state") {
635
638
  return await runVerifyCurrentState(projectRoot, parseVerifyCurrentStateArgs(tokens), io);
636
639
  }
637
- io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | verify-flow-state-diff | verify-current-state\n`);
640
+ if (subcommand === "knowledge-digest") {
641
+ return await runKnowledgeDigestCommand(projectRoot, tokens, io);
642
+ }
643
+ if (subcommand === "envelope-validate") {
644
+ return await runEnvelopeValidateCommand(projectRoot, tokens, io);
645
+ }
646
+ if (subcommand === "tdd-red-evidence") {
647
+ return await runTddRedEvidenceCommand(projectRoot, tokens, io);
648
+ }
649
+ io.stderr.write(`Unknown internal subcommand: ${subcommand}. Expected advance-stage | verify-flow-state-diff | verify-current-state | knowledge-digest | envelope-validate | tdd-red-evidence\n`);
638
650
  return 1;
639
651
  }
640
652
  catch (err) {
@@ -0,0 +1,7 @@
1
+ import type { Writable } from "node:stream";
2
+ interface InternalIo {
3
+ stdout: Writable;
4
+ stderr: Writable;
5
+ }
6
+ export declare function runEnvelopeValidateCommand(_projectRoot: string, tokens: string[], io: InternalIo): Promise<number>;
7
+ export {};
@@ -0,0 +1,66 @@
1
+ import fs from "node:fs/promises";
2
+ import { parseSkillEnvelope, validateSkillEnvelope } from "../content/stage-schema.js";
3
+ function parseArgs(tokens) {
4
+ const args = { stdin: false, quiet: false };
5
+ for (const token of tokens) {
6
+ if (token === "--stdin") {
7
+ args.stdin = true;
8
+ continue;
9
+ }
10
+ if (token === "--quiet") {
11
+ args.quiet = true;
12
+ continue;
13
+ }
14
+ if (token.startsWith("--json=")) {
15
+ args.json = token.replace("--json=", "");
16
+ continue;
17
+ }
18
+ if (token.startsWith("--file=")) {
19
+ args.file = token.replace("--file=", "");
20
+ continue;
21
+ }
22
+ throw new Error(`Unknown flag for envelope-validate: ${token}`);
23
+ }
24
+ return args;
25
+ }
26
+ async function readStdin() {
27
+ const chunks = [];
28
+ for await (const chunk of process.stdin) {
29
+ chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));
30
+ }
31
+ return Buffer.concat(chunks).toString("utf8");
32
+ }
33
+ export async function runEnvelopeValidateCommand(_projectRoot, tokens, io) {
34
+ const args = parseArgs(tokens);
35
+ let raw = "";
36
+ if (args.json !== undefined) {
37
+ raw = args.json;
38
+ }
39
+ else if (args.file !== undefined) {
40
+ raw = await fs.readFile(args.file, "utf8");
41
+ }
42
+ else if (args.stdin) {
43
+ raw = await readStdin();
44
+ }
45
+ else {
46
+ throw new Error("Provide one source: --json=<payload> | --file=<path> | --stdin");
47
+ }
48
+ const parsed = parseSkillEnvelope(raw);
49
+ if (parsed) {
50
+ if (!args.quiet) {
51
+ io.stdout.write(`${JSON.stringify(parsed, null, 2)}\n`);
52
+ }
53
+ return 0;
54
+ }
55
+ let candidate;
56
+ try {
57
+ candidate = JSON.parse(raw);
58
+ }
59
+ catch (error) {
60
+ io.stderr.write(`Invalid JSON: ${error instanceof Error ? error.message : String(error)}\n`);
61
+ return 1;
62
+ }
63
+ const validation = validateSkillEnvelope(candidate);
64
+ io.stderr.write(`Invalid envelope: ${validation.errors.join(" ")}\n`);
65
+ return 1;
66
+ }
@@ -0,0 +1,7 @@
1
+ import type { Writable } from "node:stream";
2
+ interface InternalIo {
3
+ stdout: Writable;
4
+ stderr: Writable;
5
+ }
6
+ export declare function runKnowledgeDigestCommand(projectRoot: string, tokens: string[], io: InternalIo): Promise<number>;
7
+ export {};