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.
- package/dist/artifact-linter.js +32 -0
- package/dist/config.d.ts +1 -1
- package/dist/config.js +44 -5
- package/dist/content/hooks.d.ts +2 -2
- package/dist/content/hooks.js +293 -89
- package/dist/content/ideate-command.js +11 -0
- package/dist/content/iron-laws.d.ts +142 -0
- package/dist/content/iron-laws.js +191 -0
- package/dist/content/meta-skill.js +1 -0
- package/dist/content/next-command.js +12 -0
- package/dist/content/observe.js +555 -45
- package/dist/content/ops-command.js +11 -0
- package/dist/content/session-hooks.js +3 -1
- package/dist/content/stage-schema.d.ts +16 -0
- package/dist/content/stage-schema.js +82 -5
- package/dist/content/stages/review.js +4 -4
- package/dist/content/stages/tdd.js +7 -7
- package/dist/content/start-command.js +12 -0
- package/dist/content/subagents.js +26 -0
- package/dist/content/templates.js +8 -0
- package/dist/content/view-command.js +11 -0
- package/dist/doctor.js +6 -2
- package/dist/harness-adapters.js +3 -0
- package/dist/install.js +11 -1
- package/dist/internal/advance-stage.js +14 -2
- package/dist/internal/envelope-validate.d.ts +7 -0
- package/dist/internal/envelope-validate.js +66 -0
- package/dist/internal/knowledge-digest.d.ts +7 -0
- package/dist/internal/knowledge-digest.js +93 -0
- package/dist/internal/tdd-red-evidence.d.ts +7 -0
- package/dist/internal/tdd-red-evidence.js +130 -0
- package/dist/knowledge-store.d.ts +8 -0
- package/dist/knowledge-store.js +95 -0
- package/dist/tdd-cycle.d.ts +7 -0
- package/dist/tdd-cycle.js +29 -0
- package/dist/types.d.ts +6 -0
- 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. **
|
|
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
|
|
195
|
-
purpose: "
|
|
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: "
|
|
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`
|
|
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`
|
|
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:
|
|
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
|
|
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`
|
|
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`
|
|
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
|
-
"
|
|
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
|
|
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",
|
package/dist/harness-adapters.js
CHANGED
|
@@ -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 {
|
|
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
|
-
|
|
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,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
|
+
}
|