cclaw-cli 0.48.8 → 0.48.9

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.
@@ -99,6 +99,63 @@ async function readStdin() {
99
99
  });
100
100
  }
101
101
 
102
+ async function runCclawInternal(root, args) {
103
+ return await new Promise((resolve) => {
104
+ let settled = false;
105
+ let stderr = "";
106
+ const finalize = (value) => {
107
+ if (settled) return;
108
+ settled = true;
109
+ resolve(value);
110
+ };
111
+ let child;
112
+ try {
113
+ child = spawn("cclaw", ["internal", ...args], {
114
+ cwd: root,
115
+ env: process.env,
116
+ stdio: ["ignore", "ignore", "pipe"]
117
+ });
118
+ } catch (error) {
119
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
120
+ finalize({
121
+ code: 1,
122
+ stderr,
123
+ missingBinary: code === "ENOENT"
124
+ });
125
+ return;
126
+ }
127
+ child.stderr?.on("data", (chunk) => {
128
+ stderr += String(chunk ?? "");
129
+ if (stderr.length > 8000) {
130
+ stderr = stderr.slice(-8000);
131
+ }
132
+ });
133
+ child.on("error", (error) => {
134
+ const code = error && typeof error === "object" && "code" in error ? String(error.code) : "";
135
+ finalize({
136
+ code: 1,
137
+ stderr,
138
+ missingBinary: code === "ENOENT"
139
+ });
140
+ });
141
+ child.on("close", (code, signal) => {
142
+ if (signal) {
143
+ finalize({
144
+ code: 1,
145
+ stderr,
146
+ missingBinary: false
147
+ });
148
+ return;
149
+ }
150
+ finalize({
151
+ code: typeof code === "number" ? code : 1,
152
+ stderr,
153
+ missingBinary: false
154
+ });
155
+ });
156
+ });
157
+ }
158
+
102
159
  function detectHarness(env) {
103
160
  if (env.CLAUDE_PROJECT_DIR) return "claude";
104
161
  if (env.CURSOR_PROJECT_DIR || env.CURSOR_PROJECT_ROOT) return "cursor";
@@ -1446,6 +1503,24 @@ async function handleContextMonitor(runtime) {
1446
1503
  return 0;
1447
1504
  }
1448
1505
 
1506
+ async function handleVerifyCurrentState(runtime) {
1507
+ const mode = process.env.CCLAW_WORKFLOW_GUARD_MODE === "strict"
1508
+ ? "strict"
1509
+ : DEFAULT_WORKFLOW_GUARD_MODE;
1510
+ const result = await runCclawInternal(runtime.root, ["verify-current-state", "--quiet"]);
1511
+ if (result.missingBinary) {
1512
+ process.stderr.write("[cclaw] codex hook: cclaw binary is required for verify-current-state\\n");
1513
+ return 1;
1514
+ }
1515
+ if (mode === "strict") {
1516
+ if (result.code !== 0 && result.stderr.trim().length > 0) {
1517
+ process.stderr.write(result.stderr);
1518
+ }
1519
+ return result.code === 0 ? 0 : 1;
1520
+ }
1521
+ return 0;
1522
+ }
1523
+
1449
1524
  function normalizeHookName(rawName) {
1450
1525
  const value = normalizeText(rawName).toLowerCase();
1451
1526
  if (value === "session-start" || value === "session-start.sh") return "session-start";
@@ -1454,6 +1529,7 @@ function normalizeHookName(rawName) {
1454
1529
  if (value === "prompt-guard" || value === "prompt-guard.sh") return "prompt-guard";
1455
1530
  if (value === "workflow-guard" || value === "workflow-guard.sh") return "workflow-guard";
1456
1531
  if (value === "context-monitor" || value === "context-monitor.sh") return "context-monitor";
1532
+ if (value === "verify-current-state") return "verify-current-state";
1457
1533
  return "";
1458
1534
  }
1459
1535
 
@@ -1463,7 +1539,7 @@ async function main() {
1463
1539
  process.stderr.write(
1464
1540
  "[cclaw] run-hook: usage: node " +
1465
1541
  RUNTIME_ROOT +
1466
- "/hooks/run-hook.mjs <session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor>\\n"
1542
+ "/hooks/run-hook.mjs <session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor|verify-current-state>\\n"
1467
1543
  );
1468
1544
  process.exitCode = 1;
1469
1545
  return;
@@ -1508,6 +1584,10 @@ async function main() {
1508
1584
  process.exitCode = await handleContextMonitor(runtime);
1509
1585
  return;
1510
1586
  }
1587
+ if (hookName === "verify-current-state") {
1588
+ process.exitCode = await handleVerifyCurrentState(runtime);
1589
+ return;
1590
+ }
1511
1591
  process.stderr.write("[cclaw] run-hook: unsupported hook " + hookName + "\\n");
1512
1592
  process.exitCode = 1;
1513
1593
  } catch (error) {
@@ -1660,7 +1660,7 @@ exit 0
1660
1660
  * Updated hooks.json generators with PreToolUse/PostToolUse observation.
1661
1661
  */
1662
1662
  function hookDispatcherCommand(scriptName) {
1663
- return `bash ${RUNTIME_ROOT}/hooks/run-hook.cmd ${scriptName}`;
1663
+ return `node ${RUNTIME_ROOT}/hooks/run-hook.mjs ${scriptName}`;
1664
1664
  }
1665
1665
  export function claudeHooksJsonWithObservation() {
1666
1666
  return JSON.stringify({
@@ -1799,7 +1799,7 @@ export function codexHooksJsonWithObservation() {
1799
1799
  command: hookDispatcherCommand("workflow-guard.sh")
1800
1800
  }, {
1801
1801
  type: "command",
1802
- command: "bash -lc 'if ! command -v cclaw >/dev/null 2>&1; then echo \"[cclaw] codex hook: cclaw binary is required for verify-current-state\" >&2; exit 1; fi; MODE=\"${CCLAW_WORKFLOW_GUARD_MODE:-advisory}\"; if [ \"$MODE\" = \"strict\" ]; then cclaw internal verify-current-state --quiet >/dev/null; else cclaw internal verify-current-state --quiet >/dev/null || true; fi'"
1802
+ command: hookDispatcherCommand("verify-current-state")
1803
1803
  }]
1804
1804
  }],
1805
1805
  PreToolUse: [{
@@ -272,9 +272,9 @@ export default function cclawPlugin(ctx) {
272
272
  });
273
273
  }
274
274
 
275
- async function runHookScript(scriptFileName, payload = {}) {
275
+ async function runHookScript(hookName, payload = {}) {
276
276
  const { spawn } = await import("node:child_process");
277
- const scriptPath = join(root, "${RUNTIME_ROOT}/hooks/" + scriptFileName);
277
+ const hookRuntimePath = join(root, "${RUNTIME_ROOT}/hooks/run-hook.mjs");
278
278
  const input = typeof payload === "string" ? payload : JSON.stringify(payload ?? {});
279
279
  return scheduleHookTask(() => new Promise((resolve) => {
280
280
  let stderr = "";
@@ -287,7 +287,7 @@ export default function cclawPlugin(ctx) {
287
287
 
288
288
  let child;
289
289
  try {
290
- child = spawn("bash", [scriptPath], {
290
+ child = spawn(process.execPath, [hookRuntimePath, hookName], {
291
291
  cwd: root,
292
292
  stdio: ["pipe", "ignore", "pipe"]
293
293
  });
@@ -299,7 +299,7 @@ export default function cclawPlugin(ctx) {
299
299
  const timer = setTimeout(() => {
300
300
  child.kill("SIGKILL");
301
301
  if (stderr.length > 0) {
302
- console.error("[cclaw] opencode hook timeout: " + scriptFileName + " stderr=" + stderr.slice(-1200));
302
+ console.error("[cclaw] opencode hook timeout: " + hookName + " stderr=" + stderr.slice(-1200));
303
303
  }
304
304
  finish(false);
305
305
  }, 20_000);
@@ -318,7 +318,7 @@ export default function cclawPlugin(ctx) {
318
318
  clearTimeout(timer);
319
319
  const ok = code === 0;
320
320
  if (!ok && stderr.length > 0) {
321
- console.error("[cclaw] opencode hook failed: " + scriptFileName + " stderr=" + stderr.slice(-1200));
321
+ console.error("[cclaw] opencode hook failed: " + hookName + " stderr=" + stderr.slice(-1200));
322
322
  }
323
323
  finish(ok);
324
324
  });
package/dist/doctor.js CHANGED
@@ -879,7 +879,7 @@ export async function doctorChecks(projectRoot, options = {}) {
879
879
  const codexWiringOk = codexSessionCmds.some((cmd) => cmd.includes("session-start.sh")) &&
880
880
  codexUserPromptCmds.some((cmd) => cmd.includes("prompt-guard.sh")) &&
881
881
  codexUserPromptCmds.some((cmd) => cmd.includes("workflow-guard.sh")) &&
882
- codexUserPromptCmds.some((cmd) => cmd.includes("verify-current-state --quiet")) &&
882
+ codexUserPromptCmds.some((cmd) => cmd.includes("verify-current-state")) &&
883
883
  codexPreCmds.some((cmd) => cmd.includes("prompt-guard.sh")) &&
884
884
  codexPreCmds.some((cmd) => cmd.includes("workflow-guard.sh")) &&
885
885
  codexPostCmds.some((cmd) => cmd.includes("context-monitor.sh")) &&
package/dist/install.js CHANGED
@@ -1356,12 +1356,13 @@ function stripManagedHookCommands(value) {
1356
1356
  function isManagedRuntimeHookCommand(command) {
1357
1357
  const normalized = command.trim().replace(/\s+/gu, " ");
1358
1358
  if (/(^|\s)(?:bash\s+)?(?:\.\/)?\.cclaw\/hooks\/(?:session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor)\.sh(?:\s|$)/u.test(normalized) ||
1359
- /(^|\s)(?:bash\s+)?(?:\.\/)?\.cclaw\/hooks\/run-hook\.cmd\s+(?:session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor)(?:\.sh)?(?:\s|$)/u.test(normalized)) {
1359
+ /(^|\s)(?:bash\s+)?(?:\.\/)?\.cclaw\/hooks\/run-hook\.cmd\s+(?:session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor)(?:\.sh)?(?:\s|$)/u.test(normalized) ||
1360
+ /(^|\s)(?:node\s+)?(?:"|')?(?:\.\/)?\.cclaw\/hooks\/run-hook\.mjs(?:"|')?\s+(?:session-start|stop-checkpoint|pre-compact|prompt-guard|workflow-guard|context-monitor|verify-current-state)(?:\.sh)?(?:\s|$)/u.test(normalized)) {
1360
1361
  return true;
1361
1362
  }
1362
1363
  // Codex UserPromptSubmit non-blocking state nudge:
1363
- // bash -lc '... cclaw internal verify-current-state --quiet ...'
1364
- return /internal verify-current-state --quiet/u.test(normalized);
1364
+ // legacy shell and newer Node wrappers call this internal command.
1365
+ return /internal verify-current-state(?:\s|$)/u.test(normalized);
1365
1366
  }
1366
1367
  async function removeManagedHookEntries(hookFilePath) {
1367
1368
  if (!(await exists(hookFilePath)))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cclaw-cli",
3
- "version": "0.48.8",
3
+ "version": "0.48.9",
4
4
  "description": "Installer-first flow toolkit for coding agents",
5
5
  "type": "module",
6
6
  "bin": {