@solongate/proxy 0.81.4 → 0.81.5

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.
@@ -6787,8 +6787,14 @@ function blockTool(reason) {
6787
6787
  }));
6788
6788
  sgFinish(0);
6789
6789
  } else {
6790
- process.stderr.write(reason);
6791
- sgFinish(2);
6790
+ process.stdout.write(JSON.stringify({
6791
+ hookSpecificOutput: {
6792
+ hookEventName: "PreToolUse",
6793
+ permissionDecision: "deny",
6794
+ permissionDecisionReason: reason
6795
+ }
6796
+ }));
6797
+ sgFinish(0);
6792
6798
  }
6793
6799
  }
6794
6800
  function allowTool() {
@@ -7845,8 +7851,8 @@ if (!REFRESH_MODE) {
7845
7851
  process.stdout.write(JSON.stringify({ decision: "deny", reason: ghostHit }));
7846
7852
  sgFinish(0);
7847
7853
  }
7848
- process.stderr.write(ghostHit);
7849
- sgFinish(2);
7854
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: ghostHit } }));
7855
+ sgFinish(0);
7850
7856
  }
7851
7857
  if (AGENT_TYPE !== "gemini-cli" && toolName === "Bash") {
7852
7858
  const rw = ghostListingRewrite(args, securityCfg.ghost);
package/hooks/guard.mjs CHANGED
@@ -326,9 +326,20 @@ function blockTool(reason) {
326
326
  }));
327
327
  sgFinish(0);
328
328
  } else {
329
- // Claude Code — exit code 2
330
- process.stderr.write(reason);
331
- sgFinish(2);
329
+ // Claude Code — signal the block via the JSON PreToolUse protocol on stdout,
330
+ // NOT exit code 2. On Windows the hook runs through PowerShell, which does not
331
+ // reliably propagate a native process's exit code back to Claude Code, so an
332
+ // `exit 2` block was being seen as a "non-blocking status code" and the tool ran
333
+ // anyway. Claude only processes this JSON on exit 0 (JSON is ignored if you exit
334
+ // 2), so we MUST exit 0 here. This is shell-independent and works everywhere.
335
+ process.stdout.write(JSON.stringify({
336
+ hookSpecificOutput: {
337
+ hookEventName: 'PreToolUse',
338
+ permissionDecision: 'deny',
339
+ permissionDecisionReason: reason,
340
+ },
341
+ }));
342
+ sgFinish(0);
332
343
  }
333
344
  }
334
345
 
@@ -1640,8 +1651,10 @@ if (!REFRESH_MODE) { try { input += readFileSync(0, 'utf-8'); } catch {} }
1640
1651
  } catch {}
1641
1652
  await maybeSelfUpdate();
1642
1653
  if (AGENT_TYPE === 'gemini-cli') { process.stdout.write(JSON.stringify({ decision: 'deny', reason: ghostHit })); sgFinish(0); }
1643
- process.stderr.write(ghostHit);
1644
- sgFinish(2);
1654
+ // Claude Code: JSON deny on stdout + exit 0 (see blockTool — exit codes are
1655
+ // not reliable through PowerShell on Windows; JSON is ignored on exit 2).
1656
+ process.stdout.write(JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: 'deny', permissionDecisionReason: ghostHit } }));
1657
+ sgFinish(0);
1645
1658
  }
1646
1659
  // No direct hit: if this is a listing command, rewrite it so hidden
1647
1660
  // entries are filtered out of its output (Claude Code only).
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solongate/proxy",
3
- "version": "0.81.4",
3
+ "version": "0.81.5",
4
4
  "description": "AI tool security proxy: protect any AI tool server with customizable policies, path/command constraints, rate limiting, and audit logging. No code changes required.",
5
5
  "type": "module",
6
6
  "bin": {