cclaw-cli 0.48.9 → 0.48.11

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.
@@ -58,7 +58,7 @@ Knowledge capture is now stage-native:
58
58
  - Allowed payloads:
59
59
  - \`- None this stage.\` (explicit no-op)
60
60
  - JSON bullets with required keys \`type\`, \`trigger\`, \`action\`, \`confidence\` (optional keys may mirror the full JSONL schema fields).
61
- - During \`bash .cclaw/hooks/stage-complete.sh <stage>\`, cclaw:
61
+ - During \`node .cclaw/hooks/stage-complete.mjs <stage>\`, cclaw:
62
62
  1. validates \`## Learnings\`,
63
63
  2. appends deduped entries to \`${KNOWLEDGE_PATH}\`,
64
64
  3. writes a harvest marker into the artifact.
@@ -157,7 +157,7 @@ Manage the project knowledge store. One canonical file, strict JSONL:
157
157
  - \`${KNOWLEDGE_ARCHIVE_PATH}\` — soft-archive target written only by curate.
158
158
 
159
159
  Stage-native pipeline:
160
- - During \`stage-complete.sh\`, cclaw harvests \`## Learnings\` from the current
160
+ - During \`stage-complete.mjs\`, cclaw harvests \`## Learnings\` from the current
161
161
  stage artifact into \`${KNOWLEDGE_PATH}\` automatically.
162
162
  - Use \`/cc-learn\` for query, backfill, and curation workflows.
163
163
 
@@ -58,7 +58,7 @@ This is the only progression command the user needs to drive the entire flow. St
58
58
 
59
59
  → Load **\`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`** and **\`${RUNTIME_ROOT}/commands/<currentStage>.md\`** for the current stage.
60
60
  → Execute that stage's protocol. The stage skill handles the full interaction including STOP points and gate tracking.
61
- → Stage completion must use \`bash .cclaw/hooks/stage-complete.sh <currentStage>\` (canonical), which validates delegations + gate evidence before mutating \`flow-state.json\`.
61
+ → Stage completion must use \`node .cclaw/hooks/stage-complete.mjs <currentStage>\` (canonical), which validates delegations + gate evidence before mutating \`flow-state.json\`.
62
62
 
63
63
  ### Path B: Current stage IS complete (all gates passed, all delegations satisfied)
64
64
 
@@ -188,7 +188,7 @@ Load the current stage's skill and command contract:
188
188
  - \`${RUNTIME_ROOT}/skills/<skillFolder>/SKILL.md\`
189
189
  - \`${RUNTIME_ROOT}/commands/<currentStage>.md\`
190
190
 
191
- Execute the stage protocol. The stage skill handles interaction, STOP points, gate tracking, and stage completion via \`bash .cclaw/hooks/stage-complete.sh <stage>\` (canonical flow-state mutation path).
191
+ Execute the stage protocol. The stage skill handles interaction, STOP points, gate tracking, and stage completion via \`node .cclaw/hooks/stage-complete.mjs <stage>\` (canonical flow-state mutation path).
192
192
 
193
193
  Special-case for review: if \`review_criticals_resolved\` is in \`blocked\`, route to rework instead of looping review forever — recommend \`/cc-ops rewind tdd "review_blocked_by_critical"\`.
194
194
 
@@ -101,6 +101,7 @@ async function readStdin() {
101
101
 
102
102
  async function runCclawInternal(root, args) {
103
103
  return await new Promise((resolve) => {
104
+ const isWindows = process.platform === "win32";
104
105
  let settled = false;
105
106
  let stderr = "";
106
107
  const finalize = (value) => {
@@ -110,17 +111,21 @@ async function runCclawInternal(root, args) {
110
111
  };
111
112
  let child;
112
113
  try {
113
- child = spawn("cclaw", ["internal", ...args], {
114
+ child = spawn(
115
+ isWindows ? "cmd.exe" : "cclaw",
116
+ isWindows ? ["/d", "/s", "/c", "cclaw", "internal", ...args] : ["internal", ...args],
117
+ {
114
118
  cwd: root,
115
119
  env: process.env,
116
120
  stdio: ["ignore", "ignore", "pipe"]
117
- });
121
+ }
122
+ );
118
123
  } catch (error) {
119
124
  const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
120
125
  finalize({
121
126
  code: 1,
122
127
  stderr,
123
- missingBinary: code === "ENOENT"
128
+ missingBinary: code === "ENOENT" || (isWindows && code === "EINVAL")
124
129
  });
125
130
  return;
126
131
  }
@@ -135,7 +140,7 @@ async function runCclawInternal(root, args) {
135
140
  finalize({
136
141
  code: 1,
137
142
  stderr,
138
- missingBinary: code === "ENOENT"
143
+ missingBinary: code === "ENOENT" || (isWindows && code === "EINVAL")
139
144
  });
140
145
  });
141
146
  child.on("close", (code, signal) => {
@@ -147,10 +152,14 @@ async function runCclawInternal(root, args) {
147
152
  });
148
153
  return;
149
154
  }
155
+ const stderrLower = stderr.toLowerCase();
156
+ const missingBinary = isWindows
157
+ ? stderrLower.includes("is not recognized as an internal or external command")
158
+ : false;
150
159
  finalize({
151
160
  code: typeof code === "number" ? code : 1,
152
161
  stderr,
153
- missingBinary: false
162
+ missingBinary
154
163
  });
155
164
  });
156
165
  });
@@ -177,12 +186,12 @@ async function detectRoot(env) {
177
186
  try {
178
187
  const runtimePath = path.join(candidate, RUNTIME_ROOT);
179
188
  const stat = await fs.stat(runtimePath);
180
- if (stat.isDirectory()) return candidate;
189
+ if (stat.isDirectory()) return { root: candidate, foundRuntime: true };
181
190
  } catch {
182
191
  // continue
183
192
  }
184
193
  }
185
- return candidates[0] || process.cwd();
194
+ return { root: candidates[0] || process.cwd(), foundRuntime: false };
186
195
  }
187
196
 
188
197
  function toLower(value) {
@@ -1509,7 +1518,7 @@ async function handleVerifyCurrentState(runtime) {
1509
1518
  : DEFAULT_WORKFLOW_GUARD_MODE;
1510
1519
  const result = await runCclawInternal(runtime.root, ["verify-current-state", "--quiet"]);
1511
1520
  if (result.missingBinary) {
1512
- process.stderr.write("[cclaw] codex hook: cclaw binary is required for verify-current-state\\n");
1521
+ process.stderr.write("[cclaw] hook: cclaw binary is required for verify-current-state\\n");
1513
1522
  return 1;
1514
1523
  }
1515
1524
  if (mode === "strict") {
@@ -1523,12 +1532,12 @@ async function handleVerifyCurrentState(runtime) {
1523
1532
 
1524
1533
  function normalizeHookName(rawName) {
1525
1534
  const value = normalizeText(rawName).toLowerCase();
1526
- if (value === "session-start" || value === "session-start.sh") return "session-start";
1527
- if (value === "stop-checkpoint" || value === "stop-checkpoint.sh") return "stop-checkpoint";
1528
- if (value === "pre-compact" || value === "pre-compact.sh") return "pre-compact";
1529
- if (value === "prompt-guard" || value === "prompt-guard.sh") return "prompt-guard";
1530
- if (value === "workflow-guard" || value === "workflow-guard.sh") return "workflow-guard";
1531
- if (value === "context-monitor" || value === "context-monitor.sh") return "context-monitor";
1535
+ if (value === "session-start") return "session-start";
1536
+ if (value === "stop-checkpoint") return "stop-checkpoint";
1537
+ if (value === "pre-compact") return "pre-compact";
1538
+ if (value === "prompt-guard") return "prompt-guard";
1539
+ if (value === "workflow-guard") return "workflow-guard";
1540
+ if (value === "context-monitor") return "context-monitor";
1532
1541
  if (value === "verify-current-state") return "verify-current-state";
1533
1542
  return "";
1534
1543
  }
@@ -1546,7 +1555,14 @@ async function main() {
1546
1555
  }
1547
1556
 
1548
1557
  const harness = detectHarness(process.env);
1549
- const root = await detectRoot(process.env);
1558
+ const { root, foundRuntime } = await detectRoot(process.env);
1559
+ if (!foundRuntime) {
1560
+ // No .cclaw/ runtime in any candidate root — this directory is not
1561
+ // initialized for cclaw. Exit 0 silently so hooks never block harnesses
1562
+ // that run in unrelated repos; users initialize with \`cclaw init\`.
1563
+ process.exitCode = 0;
1564
+ return;
1565
+ }
1550
1566
  const inputRaw = await readStdin();
1551
1567
  const inputData = safeParseJson(inputRaw, {});
1552
1568
  const runtime = {
@@ -1,41 +1,3 @@
1
- /**
2
- * Hook helper scripts and harness hook JSON generators.
3
- *
4
- * This module still provides prompt/workflow/context guard scripts and
5
- * cross-harness hook wiring. Observation pipeline scripts are retained only
6
- * for backward compatibility and are not wired by default runtime generation.
7
- */
8
- export interface PromptGuardOptions {
9
- strictMode?: boolean;
10
- }
11
- export declare function promptGuardScript(options?: PromptGuardOptions): string;
12
- export interface WorkflowGuardOptions {
13
- workflowGuardMode?: "advisory" | "strict";
14
- tddEnforcementMode?: "advisory" | "strict";
15
- tddTestPathPatterns?: string[];
16
- tddProductionPathPatterns?: string[];
17
- }
18
- export declare function workflowGuardScript(options?: WorkflowGuardOptions): string;
19
- export declare function contextMonitorScript(): string;
20
1
  export declare function claudeHooksJsonWithObservation(): string;
21
2
  export declare function cursorHooksJsonWithObservation(): string;
22
- /**
23
- * Codex CLI ≥ v0.114 hooks. Differences vs. the Claude shape:
24
- *
25
- * - `SessionStart` matcher is limited to `startup|resume` — Codex does
26
- * not emit `clear` or `compact` lifecycle phases.
27
- * - `PreToolUse` / `PostToolUse` fire **only for the `Bash` tool**
28
- * (documented Codex limitation, v0.114/v0.115). We match both `Bash`
29
- * and `bash` variants to tolerate casing drift across Codex builds.
30
- * - `UserPromptSubmit` is supported and is the closest analogue to
31
- * Cursor's `preToolUse` for non-Bash tooling — we run prompt-guard
32
- * there so workflow/prompt checks still fire when the tool being
33
- * used is `Write` or `Edit` rather than `Bash`.
34
- * - There is no `PreCompact` event in Codex CLI — pre-compact
35
- * semantics are carried by the agent itself inside `/cc-ops retro`.
36
- *
37
- * The entire file is inert unless the user opts into
38
- * `[features] codex_hooks = true` in `~/.codex/config.toml`; cclaw
39
- * doctor and the init prompt handle that flag.
40
- */
41
3
  export declare function codexHooksJsonWithObservation(): string;