@golproductions/check 3.3.5 → 3.3.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/README.md CHANGED
@@ -1,106 +1,106 @@
1
- # Check
2
-
3
- **Less hallucinations.** Check is a command firewall for AI coding agents: it validates commands, packages, and URLs before they run in your project. What isn't real gets denied, what is real passes through. Deterministic, no AI inside.
4
-
5
- **This is the one location.** Everything integrates from here:
6
-
7
- ```
8
- npx @golproductions/check --install # Windows, macOS, Linux
9
- curl check.golproductions.com | sh # Unix shells
10
- ```
11
-
12
- Installing mints a free key bound to your machine. No signup, 120 free checks per day, then $0.0068 AUD each from a prepaid balance, and the same key is shared by every Check tool on the machine. Need more? Get a paid GOL API Key at [the console](https://www.golproductions.com/console.html).
13
-
14
- ---
15
-
16
- ## Integrate it into anything
17
-
18
- Check is one primitive with four faces. Pick the one your environment speaks.
19
-
20
- ### 1. Hook mode: blocks bad commands before they run
21
-
22
- For any tool with pre-execution hooks. `--install` wires these automatically for **Claude Code, Cursor, and Gemini CLI**; wire anything else yourself: pipe the hook JSON to the script, read the verdict.
23
-
24
- ```
25
- echo '{"tool_input":{"command":"some command"}}' | node ~/.check/check.mjs
26
- → {"hookSpecificOutput":{"permissionDecision":"allow"}} # runnable
27
- → {"hookSpecificOutput":{"permissionDecision":"deny", ...}} # hallucinated
28
- ```
29
-
30
- The script auto-detects Claude Code, Cursor, and Gemini hook formats and answers in each tool's own dialect.
31
-
32
- ### 2. MCP server: for anything that speaks Model Context Protocol
33
-
34
- Windsurf, Antigravity, Continue, Amazon Q, Roo Code, Claude Desktop, or your own agent:
35
-
36
- ```json
37
- {
38
- "mcpServers": {
39
- "check": {
40
- "command": "npx",
41
- "args": ["@golproductions/check", "--mcp"],
42
- "env": { "GOL_CLIENT_ID": "your_key" }
43
- }
44
- }
45
- }
46
- ```
47
-
48
- Exposes two tools: `Check` (verdict only) and `CheckAndExecute` (validate, then run if clean). MCP is advisory: the agent calls it when its rules say to. Add one line to your agent's rules: *"Before running any shell command, validate it with the `check` tool."*
49
-
50
- ### 3. CLI: for scripts, CI, git hooks, anything with a shell
51
-
52
- ```
53
- curl check.golproductions.com | sh # installs the `check` command (Unix)
54
- check "netlify-cli deploy --prod" # → invalid (exit 1)
55
- check "netlify deploy --prod" # → runnable (exit 0)
56
- echo "some command" | check # pipe mode
57
- ```
58
-
59
- Exit codes make it composable: gate a CI step, a git pre-push hook, a Docker entrypoint, a cron job.
60
-
61
- ### 4. HTTP: for everything else, in any language
62
-
63
- ```
64
- POST https://triage.golproductions.com/preflight
65
- Header: X-GOL-CLIENT-ID: your_key
66
- Body: {"command": "the command to validate"}
67
-
68
- → {"verdict": "runnable" | "invalid", "reason": "...", "daily_remaining": 119}
69
- ```
70
-
71
- That is the whole contract. Rate limit 60/min. A `402` means the free tier is spent and the balance is empty. Treat it as allow-with-warning, never as a verdict.
72
-
73
- ---
74
-
75
- ## Keys
76
-
77
- | Tier | What | Where |
78
- |------|------|-------|
79
- | **GOL Client ID** (free) | Minted automatically on install, bound to the machine, shared by every tool on it. 120 checks/day, forever. | Nowhere. It just happens |
80
- | **GOL API Key** (paid) | Prepaid balance ($0.0068 AUD/check after the daily 120), spend caps, dashboard, 2FA. | [Console](https://www.golproductions.com/console.html) |
81
-
82
- Same header, same API. The tier is just what the key can do.
83
-
84
- ## Manage
85
-
86
- ```
87
- npx @golproductions/check --status Your key + where Check is installed
88
- npx @golproductions/check --credits Balance and free checks remaining
89
- npx @golproductions/check --uninstall Remove from every tool
90
- ```
91
-
92
- ## Editor extensions
93
-
94
- Thin wrappers over this same API exist on the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=golproductions.gol-check), [Open VSX](https://open-vsx.org/extension/golproductions/gol-check) (also used by Antigravity), and the [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/32408). They validate on demand; the hook and MCP faces above are the real-time gate.
95
-
96
- ## Links
97
-
98
- [Product](https://www.golproductions.com/check.html) · [Pricing](https://www.golproductions.com/pricing.html) · [Console](https://www.golproductions.com/console.html) · [Updates](https://www.golproductions.com/updates.html) · [Blog](https://www.golproductions.com/blog/)
99
-
100
- ## Terms
101
-
102
- By installing or using Check you agree to the [Terms of Service](https://www.golproductions.com/terms.html) and [Privacy Policy](https://www.golproductions.com/privacy.html). The service is provided as is; see the Terms for the full picture, including your Australian Consumer Law rights.
103
-
104
- ## License
105
-
106
- Copyright (c) 2026 GOL Productions. All rights reserved.
1
+ # Check
2
+
3
+ **The anti-hallucination layer.** Check stops hallucinated commands, packages, and URLs before they run in your project. What isn't real gets blocked, what is real passes through. Deterministic, one round trip to the nearest Cloudflare edge, no AI inside.
4
+
5
+ **This is the one location.** Everything integrates from here:
6
+
7
+ ```
8
+ npx @golproductions/check --install # Windows, macOS, Linux
9
+ curl check.golproductions.com | sh # Unix shells
10
+ ```
11
+
12
+ Installing mints a free key bound to your machine. No signup, 120 free checks per day, then $0.0068 AUD each from a prepaid balance, and the same key is shared by every Check tool on the machine. Need more? Get a paid GOL API Key at [the console](https://www.golproductions.com/console.html).
13
+
14
+ ---
15
+
16
+ ## Integrate it into anything
17
+
18
+ Check is one primitive with four faces. Pick the one your environment speaks.
19
+
20
+ ### 1. Hook mode: blocks bad commands before they run
21
+
22
+ For any tool with pre-execution hooks. `--install` wires these automatically for **Claude Code, Cursor, and Gemini CLI**; wire anything else yourself: pipe the hook JSON to the script, read the verdict.
23
+
24
+ ```
25
+ echo '{"tool_input":{"command":"some command"}}' | node ~/.check/check.mjs
26
+ → {"hookSpecificOutput":{"permissionDecision":"allow"}} # runnable
27
+ → {"hookSpecificOutput":{"permissionDecision":"deny", ...}} # hallucinated
28
+ ```
29
+
30
+ The script auto-detects Claude Code, Cursor, and Gemini hook formats and answers in each tool's own dialect.
31
+
32
+ ### 2. MCP server: for anything that speaks Model Context Protocol
33
+
34
+ Windsurf, Antigravity, Continue, Amazon Q, Roo Code, Claude Desktop, or your own agent:
35
+
36
+ ```json
37
+ {
38
+ "mcpServers": {
39
+ "check": {
40
+ "command": "npx",
41
+ "args": ["@golproductions/check", "--mcp"],
42
+ "env": { "GOL_CLIENT_ID": "your_key" }
43
+ }
44
+ }
45
+ }
46
+ ```
47
+
48
+ Exposes two tools: `Check` (verdict only) and `CheckAndExecute` (validate, then run if clean). MCP is advisory: the agent calls it when its rules say to. Add one line to your agent's rules: *"Before running any shell command, validate it with the `check` tool."*
49
+
50
+ ### 3. CLI: for scripts, CI, git hooks, anything with a shell
51
+
52
+ ```
53
+ curl check.golproductions.com | sh # installs the `check` command (Unix)
54
+ check "netlify-cli deploy --prod" # → invalid (exit 1)
55
+ check "netlify deploy --prod" # → runnable (exit 0)
56
+ echo "some command" | check # pipe mode
57
+ ```
58
+
59
+ Exit codes make it composable: gate a CI step, a git pre-push hook, a Docker entrypoint, a cron job.
60
+
61
+ ### 4. HTTP: for everything else, in any language
62
+
63
+ ```
64
+ POST https://triage.golproductions.com/preflight
65
+ Header: X-GOL-CLIENT-ID: your_key
66
+ Body: {"command": "the command to validate"}
67
+
68
+ → {"verdict": "runnable" | "invalid", "reason": "...", "daily_remaining": 119}
69
+ ```
70
+
71
+ That is the whole contract. Rate limit 60/min. A `402` means the free tier is spent and the balance is empty. Treat it as allow-with-warning, never as a verdict.
72
+
73
+ ---
74
+
75
+ ## Keys
76
+
77
+ | Tier | What | Where |
78
+ |------|------|-------|
79
+ | **GOL Client ID** (free) | Minted automatically on install, bound to the machine, shared by every tool on it. 120 checks/day, forever. | Nowhere. It just happens |
80
+ | **GOL API Key** (paid) | Prepaid balance ($0.0068 AUD/check after the daily 120), spend caps, dashboard, 2FA. | [Console](https://www.golproductions.com/console.html) |
81
+
82
+ Same header, same API. The tier is just what the key can do.
83
+
84
+ ## Manage
85
+
86
+ ```
87
+ npx @golproductions/check --status Your key + where Check is installed
88
+ npx @golproductions/check --credits Balance and free checks remaining
89
+ npx @golproductions/check --uninstall Remove from every tool
90
+ ```
91
+
92
+ ## Editor extensions
93
+
94
+ Thin wrappers over this same API exist on the [VS Code Marketplace](https://marketplace.visualstudio.com/items?itemName=golproductions.gol-check), [Open VSX](https://open-vsx.org/extension/golproductions/gol-check) (also used by Antigravity), and the [JetBrains Marketplace](https://plugins.jetbrains.com/plugin/32408). They validate on demand; the hook and MCP faces above are the real-time gate.
95
+
96
+ ## Links
97
+
98
+ [Product](https://www.golproductions.com/check.html) · [Pricing](https://www.golproductions.com/pricing.html) · [Console](https://www.golproductions.com/console.html) · [Updates](https://www.golproductions.com/updates.html) · [Blog](https://www.golproductions.com/blog/)
99
+
100
+ ## Terms
101
+
102
+ By installing or using Check you agree to the [Terms of Service](https://www.golproductions.com/terms.html) and [Privacy Policy](https://www.golproductions.com/privacy.html). The service is provided as is; see the Terms for the full picture, including your Australian Consumer Law rights.
103
+
104
+ ## License
105
+
106
+ Copyright (c) 2026 GOL Productions. All rights reserved.
package/dist/index.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{readFileSync as e,writeFileSync as o,mkdirSync as t,existsSync as n,createWriteStream as s,chmodSync as r,unlinkSync as c,rmSync as i}from"node:fs";import{spawnSync as a,spawn as l}from"node:child_process";import{createServer as h,connect as d}from"node:net";import{fileURLToPath as u}from"node:url";import{join as m}from"node:path";import{homedir as p,platform as f,arch as g}from"node:os";import{pipeline as k}from"node:stream/promises";import{Readable as y}from"node:stream";const w=(()=>{try{return JSON.parse(e(m(u(new URL(".",import.meta.url)),"..","package.json"),"utf8")).version}catch{return"3.3.5"}})(),v="https://triage.golproductions.com/preflight",C=process.env.GOL_CLIENT_ID||"";function T(){if(C)return C;try{const o=e(m(p(),".check","key"),"utf8").trim();if(o)return o}catch{}try{const o=JSON.parse(e(m(p(),".claude","settings.json"),"utf8"));return o.env?.GOL_CLIENT_ID||""}catch{return""}}async function S(){try{const e=await async function(){const{createHash:e}=await import("node:crypto"),{hostname:o,userInfo:t}=await import("node:os");let n="";try{n=t().username||""}catch{}const s=[o(),f(),g(),n].join("|");return e("sha256").update(s).digest("hex")}(),o=await fetch(v.replace("/preflight","/instant-key"),{method:"POST",headers:{"Content-Type":"application/json","User-Agent":"c/"+w},body:JSON.stringify({fingerprint:e,channel:"npm"}),signal:AbortSignal.timeout(1e4)});if(!o.ok)return null;const t=await o.json();return t&&t.client_id?t.client_id:null}catch{return null}}async function x(e){const o="win32"===f()?"win":"darwin"===f()?"macos":"linux",t=`https://pub-e55366a7f5994be9be04f0e205179f4a.r2.dev/releases/truth-gate-v3.0.0-${o}-${"arm64"===g()?"arm64":"x64"}${"win"===o?".exe":""}`,n=await fetch(t);if(!n.ok)throw new Error(`Download failed: ${n.status}`);if(await k(y.fromWeb(n.body),s(e)),"win"!==o)try{r(e,493)}catch{}}async function P(){let n=process.env.GOL_CLIENT_ID||"";n||(n=await S()),n||(console.log("ERROR: could not mint a key (offline, or this network hit its daily limit). Get one at https://golproductions.com/check then re-run with GOL_CLIENT_ID set."),process.exit(1));const s=p(),r=m(s,".check");t(r,{recursive:!0});const c=m(r,"check.mjs").replace(/\\/g,"/");o(c,e(new URL(import.meta.url),"utf8"),"utf8"),o(m(r,"key"),n,"utf8");const i="win32"===f()?".exe":"",a=m(r,"truth-gate"+i).replace(/\\/g,"/");let l=!0;try{await x(a)}catch{l=!1}console.log(`Check is installed.\n\nWhat follows is written for the AI agent that ran this command. If a person ran\nit, hand this to your agent.\n\nCheck is a deterministic gate. It validates every shell command against this\nmachine before it runs, so a fabricated command is denied before it can execute.\nYou install it once; from here, it wires itself in. You know this environment\nbetter than any installer could, so you do the wiring.\n\nAlready in place\n key ${n}\n (saved at ${m(r,"key").replace(/\\/g,"/")})\n gate script ${c}\n outcome check ${l?a:"(download failed; skip the post-execution step)"}\n\nThe contract\n Before each shell command, pipe its hook JSON to node ${c}\n with GOL_CLIENT_ID=${n} in the environment. The output tells you allow or\n deny. Check speaks Claude Code, Cursor, and Gemini natively; for anything\n else, send {"tool_name":"Bash","tool_input":{"command":"<cmd>"}} and read a\n denial as stderr plus exit 2.\n\n No hook system? Add Check as an MCP server instead:\n {"command":"npx","args":["@golproductions/check","--mcp"],"env":{"GOL_CLIENT_ID":"${n}"}}\n and call the check tool before every command.\n\nThree steps\n 1. Identify what you are (Claude Code, Cursor, Gemini CLI, Windsurf, Zed,\n aider, something custom) and where your user-level config lives.\n 2. Wire the contract into that config, before execution. Merge, don't\n overwrite. For Claude Code: hooks.PreToolUse, matcher "Bash|PowerShell",\n command node with the gate script as its argument, plus env.GOL_CLIENT_ID.\n If your environment also has post-execution and stop hooks, wire the same\n script to PostToolUse and Stop: that turns on outcome receipts (real exit\n codes) and the completion gate, which challenges a "done" left sitting on\n a fresh, unaddressed failure.\n 3. Prove it, don't assume it. Run flurbomatic-zzz --init — it must be\n denied. Then run echo check-install-verify — it must pass. Two matching\n receipts mean the gate is live: tell your user "Check is active, 120 free\n checks a day on this machine." If they don't match, read your hook docs\n and fix the wiring. The facts above don't change.\n\nFor your user, if they ask: 120 free checks a day per machine, then $0.0068 AUD\nper check from a prepaid wallet at https://golproductions.com/console.html. If\nthe service is ever unreachable, commands proceed with a warning — the gate\nfails open, so it never becomes the thing that stops your work.`),process.exit(0)}function b(e){return e&&(e.includes(".check")||e.includes("truth-gate"))}function O(e){return!!b(e.command)||!!e.args?.some(e=>b(e))}function _(){console.log(`\n Check v${w}: Anti-hallucination layer for AI coding agents\n\n Usage:\n\n npx @golproductions/check --install <key> Install Check with your Client ID\n npx @golproductions/check --uninstall Remove Check from all tools\n npx @golproductions/check --status Show what is installed and active\n npx @golproductions/check --credits Check your usage and balance\n npx @golproductions/check --docs View documentation\n npx @golproductions/check --help Show this help\n\n Get your key: https://golproductions.com/check\n Pricing: 120 free/day, then $0.0068 AUD per check\n`),process.exit(0)}const I=parseInt(process.env.CHECK_DAEMON_PORT||"17874",10),N=6e5;function U(){try{const e=u(import.meta.url);l(process.execPath,[e,"--daemon"],{detached:!0,stdio:"ignore",windowsHide:!0}).unref()}catch{}}const j=process.argv.slice(2);function E(e,{input:o,cwd:t}={}){return new Promise(s=>{try{const r=function(){if("win32"!==f())return"bash";const e=[process.env.CLAUDE_CODE_GIT_BASH_PATH,"C:/Program Files/Git/bin/bash.exe","C:/Program Files/Git/usr/bin/bash.exe","C:/Program Files (x86)/Git/bin/bash.exe"].filter(Boolean);for(const o of e)try{if(n(o))return o}catch{}return null}();if(!r)return s(null);const c=l(r,e,{cwd:t,windowsHide:!0});let i="",a="";const h=setTimeout(()=>{try{c.kill()}catch{}s(null)},4e3);c.stdout.on("data",e=>{a+=e}),c.stderr.on("data",e=>{i+=e}),c.on("error",()=>{clearTimeout(h),s(null)}),c.on("close",e=>{clearTimeout(h),s({status:e,stderr:i,stdout:a})}),void 0!==o&&c.stdin.write(o),c.stdin.end()}catch{s(null)}})}function L(e,o,t){if(o){const o={cursor:{permission:"allow"},gemini:{decision:"allow"},claude:{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}};process.stdout.write(JSON.stringify(o[e]||o.claude)),process.exit(0)}"gemini"===e&&(process.stdout.write(JSON.stringify({decision:"deny",reason:t})),process.exit(2));const n={cursor:{permission:"deny",agent_message:t},claude:{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:t}}};process.stdout.write(JSON.stringify(n[e]||n.claude)),process.exit(0)}function D(e,s){let r=!0;try{const e=m(p(),".check","credits-warned-"+(new Date).toISOString().slice(0,10));r=n(e),r||(t(m(p(),".check"),{recursive:!0}),o(e,"1"))}catch{r=!1}const c=r?void 0:s;if("gemini"===e&&(process.stdout.write(JSON.stringify({decision:"allow"})),c&&process.stderr.write(c),process.exit(0)),"cursor"===e){const e={permission:"allow"};c&&(e.user_message=c),process.stdout.write(JSON.stringify(e)),process.exit(0)}const i={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};c&&(i.systemMessage=c),process.stdout.write(JSON.stringify(i)),process.exit(0)}j.includes("--daemon")&&(await async function(){let e=setTimeout(()=>process.exit(0),N);const o=h(o=>{clearTimeout(e),e=setTimeout(()=>process.exit(0),N);let t="";o.on("error",()=>{}),o.on("data",async e=>{t+=e;const n=t.indexOf("\n");if(-1===n)return;let s;try{s=JSON.parse(t.slice(0,n))}catch{return void o.destroy()}if(s.v!==w)return o.end(JSON.stringify({gol:1,stale:1})+"\n"),void setTimeout(()=>process.exit(0),50);try{const e=new AbortController,t=setTimeout(()=>e.abort(),5e3),n=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":s.key,"User-Agent":"c/"+w},body:JSON.stringify(s.body),signal:e.signal});clearTimeout(t);const r=await n.text();o.end(JSON.stringify({gol:1,status:n.status,text:r})+"\n")}catch{o.end(JSON.stringify({gol:1,err:1})+"\n")}})});o.on("error",()=>process.exit(0)),o.listen(I,"127.0.0.1")}(),await new Promise(()=>{})),j.includes("--agent")&&await P(),j.includes("--install")&&await async function(){let s=process.argv[3]||process.env.GOL_CLIENT_ID||"";s&&"your_key"!==s||(console.log("\n Activating Check (free, no signup)...\n"),s=await S(),s||(console.log(" Could not activate automatically (offline, or this network hit its daily limit).\n"),console.log(" Get a key the manual way: https://golproductions.com/check\n"),console.log(" Then run: npx @golproductions/check --install YOUR_KEY\n"),process.exit(1)),console.log(" Activated. 120 free checks per day.\n"));const r=await async function(e){try{const o=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":e,"User-Agent":"c/"+w},body:JSON.stringify({command:"echo check-install-verify",cwd:process.cwd(),v:w}),signal:AbortSignal.timeout(1e4)});if(o.ok)return{valid:!0,expired:!1};let t={};try{t=await o.json()}catch{}return{valid:!1,expired:/expir/i.test(t.reason||t.error||t.message||"")}}catch{return{valid:!1,expired:!1}}}(s);r.valid||(r.expired?(console.log("\n Your Client ID has expired: "+s),console.log("\n Renew your key at: https://golproductions.com/check\n")):(console.log("\n Invalid Client ID: "+s),console.log("\n Get a valid key at: https://golproductions.com/check\n")),process.exit(1)),console.log("\n Key verified. Installing Check...\n");const c=p();let i=0;const a=e(new URL(import.meta.url),"utf8"),l=m(c,".check");t(l,{recursive:!0});const h=m(l,"check.mjs").replace(/\\/g,"/");o(h,a,"utf8"),o(m(l,"key"),s,"utf8");const d="win32"===f()?".exe":"",u=m(l,"truth-gate"+d).replace(/\\/g,"/");await x(u);const g={command:"npx",args:["@golproductions/check","--mcp"],env:{GOL_CLIENT_ID:s}},k=[{name:"Claude Code",dir:m(c,".claude"),file:"settings.json",config:e=>(e.hooks=e.hooks||{},e.hooks.PreToolUse=(e.hooks.PreToolUse||[]).filter(e=>!e.hooks?.some(O)),e.hooks.PreToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:"node",args:[h]}]}),e.hooks.PostToolUse=(e.hooks.PostToolUse||[]).filter(e=>!e.hooks?.some(O)),e.hooks.PostToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:"node",args:[h]}]},{matcher:"Bash|PowerShell",hooks:[{type:"command",command:u}]},{matcher:"Edit|Write|NotebookEdit",hooks:[{type:"command",command:u}]}),e.hooks.Stop=(e.hooks.Stop||[]).filter(e=>!e.hooks?.some(O)),e.hooks.Stop.push({hooks:[{type:"command",command:"node",args:[h]}]}),e.env=e.env||{},e.env.GOL_CLIENT_ID=s,e)},{name:"Gemini CLI / Antigravity",dir:m(c,".gemini"),file:"settings.json",config:e=>{e.hooks=e.hooks||{},e.hooks.BeforeTool=e.hooks.BeforeTool||[];const o=e.hooks.BeforeTool;return o.find(e=>e.hooks?.some(e=>"check-gate"===e.name))||o.push({matcher:".*",hooks:[{type:"command",command:"node "+h,name:"check-gate"}]}),e}},{name:"Cursor",dir:m(c,".cursor"),file:"hooks.json",config:e=>(e.version=e.version||1,e.hooks=e.hooks||{},e.hooks.beforeShellExecution=(e.hooks.beforeShellExecution||[]).filter(e=>!b(e.command)),e.hooks.beforeShellExecution.push({command:"node "+h}),e)},{name:"Homebase",dir:m(c,".homebase"),file:"hooks.json",config:e=>(e.hooks=e.hooks||{},e.hooks.PreToolUse=(e.hooks.PreToolUse||[]).filter(e=>!e.hooks?.some(O)),e.hooks.PreToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:"node",args:[h]}]}),e.hooks.PostToolUse=(e.hooks.PostToolUse||[]).filter(e=>!e.hooks?.some(O)),e.hooks.PostToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:u}]}),e.env=e.env||{},e.env.GOL_CLIENT_ID=s,e)},{name:"Windsurf (MCP)",dir:m(c,".codeium","windsurf"),file:"mcp_config.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check=g,e)},{name:"Continue (MCP)",dir:m(process.cwd(),".continue","mcpServers"),file:"check.json",config:()=>({mcpServers:{Check:g}})},{name:"Amazon Q Developer (MCP)",dir:m(c,".aws","amazonq"),file:"mcp.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check={type:"stdio",...g},e)},{name:"Roo Code (project MCP)",dir:m(process.cwd(),".roo"),file:"mcp.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check=g,e)},{name:"Project MCP (shared)",dir:process.cwd(),file:".mcp.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check=g,e)}],y=["Windsurf (MCP)","Continue (MCP)","Amazon Q Developer (MCP)","Roo Code (project MCP)","Project MCP (shared)"];let C=!1;for(const s of k){const r=y.includes(s.name);if(!r&&!n(s.dir))continue;r&&t(s.dir,{recursive:!0});const c=m(s.dir,s.file);let a={};try{a=JSON.parse(e(c,"utf8"))}catch{}const l=s.config(a);o(c,JSON.stringify(l,null,2)+"\n","utf8"),i++,"Claude Code"===s.name&&(C=!0)}if(0===i){t(m(c,".claude"),{recursive:!0});const n=m(c,".claude","settings.json");let s={};try{s=JSON.parse(e(n,"utf8"))}catch{}const r=k[0].config(s);o(n,JSON.stringify(r,null,2)+"\n","utf8"),C=!0}const T=C?"CLAUDE.md":"AGENTS.md",P=m(process.cwd(),T);try{const t=n(P)?e(P,"utf8"):"";if(!t.includes("Never fabricate")){const e=t.length>0?t.trimEnd()+"\n\n":"";o(P,e+"Never fabricate, hallucinate, or invent values. If you don't have it, say you don't have it. No fake keys, no fake IDs, no fake paths, no fake URLs. If unsure, ask. Never guess and present it as fact.\n","utf8")}}catch{}console.log(`\n Your free GOL Client ID (this machine): ${s}`),console.log(" Shared by every editor on this machine. 120 free checks/day."),console.log(" Need more? Get a GOL API Key (paid, prepaid balance) at"),console.log(" https://www.golproductions.com/console"),console.log("\n By using Check you agree to the Terms and Privacy Policy:"),console.log(" golproductions.com/terms | golproductions.com/privacy"),console.log("\nSafe travels wanderer..\n"),process.exit(0)}(),j.includes("--uninstall")&&await async function(){const t=p();let s=0;console.log("\n Removing Check...\n");let r=Promise.resolve();try{const e=T();e&&(r=fetch(v.replace("/preflight","/log"),{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":e,"User-Agent":"c/"+w},body:JSON.stringify({verdict:"uninstall",channel:"npm",os:f(),arch:g(),version:w}),signal:AbortSignal.timeout(3e3)}).catch(()=>{}))}catch{}const a=[{name:"Claude Code",path:m(t,".claude","settings.json"),clean:e=>(e.hooks?.PreToolUse&&(e.hooks.PreToolUse=e.hooks.PreToolUse.filter(e=>!e.hooks?.some(O)),0===e.hooks.PreToolUse.length&&delete e.hooks.PreToolUse),e.hooks?.PostToolUse&&(e.hooks.PostToolUse=e.hooks.PostToolUse.filter(e=>!e.hooks?.some(O)),0===e.hooks.PostToolUse.length&&delete e.hooks.PostToolUse),e.hooks&&0===Object.keys(e.hooks).length&&delete e.hooks,e.env&&(delete e.env.GOL_CLIENT_ID,0===Object.keys(e.env).length&&delete e.env),e)},{name:"Gemini CLI",path:m(t,".gemini","settings.json"),clean:e=>(e.hooks?.BeforeTool&&(e.hooks.BeforeTool=e.hooks.BeforeTool.filter(e=>!e.hooks?.some(e=>"check-gate"===e.name)),0===e.hooks.BeforeTool.length&&delete e.hooks.BeforeTool,0===Object.keys(e.hooks).length&&delete e.hooks),e)},{name:"Cursor",path:m(t,".cursor","hooks.json"),clean:e=>(e.hooks?.beforeShellExecution&&(e.hooks.beforeShellExecution=e.hooks.beforeShellExecution.filter(e=>!b(e.command)),0===e.hooks.beforeShellExecution.length&&delete e.hooks.beforeShellExecution,0===Object.keys(e.hooks).length&&delete e.hooks),e)},{name:"Homebase",path:m(t,".homebase","hooks.json"),clean:e=>(e.hooks?.PreToolUse&&(e.hooks.PreToolUse=e.hooks.PreToolUse.filter(e=>!e.hooks?.some(O)),0===e.hooks.PreToolUse.length&&delete e.hooks.PreToolUse),e.hooks?.PostToolUse&&(e.hooks.PostToolUse=e.hooks.PostToolUse.filter(e=>!e.hooks?.some(O)),0===e.hooks.PostToolUse.length&&delete e.hooks.PostToolUse),e.hooks&&0===Object.keys(e.hooks).length&&delete e.hooks,e.env&&(delete e.env.GOL_CLIENT_ID,0===Object.keys(e.env).length&&delete e.env),e)}],l=[{name:"Windsurf",path:m(t,".codeium","windsurf","mcp_config.json")},{name:"Amazon Q",path:m(t,".aws","amazonq","mcp.json")}],h=[{name:"Continue",path:m(process.cwd(),".continue","mcpServers","check.json"),deleteFile:!0},{name:"Roo Code",path:m(process.cwd(),".roo","mcp.json")},{name:"Project MCP",path:m(process.cwd(),".mcp.json")}];for(const t of a)if(n(t.path))try{const n=JSON.parse(e(t.path,"utf8")),r=t.clean(n);o(t.path,JSON.stringify(r,null,2)+"\n","utf8"),console.log(" removed "+t.name),s++}catch{}for(const t of[...l,...h])if(n(t.path))try{if(t.deleteFile)c(t.path),console.log(" removed "+t.name),s++;else{const n=JSON.parse(e(t.path,"utf8"));n.mcpServers?.Check&&(delete n.mcpServers.Check,0===Object.keys(n.mcpServers).length&&delete n.mcpServers,o(t.path,JSON.stringify(n,null,2)+"\n","utf8"),console.log(" removed "+t.name),s++)}}catch{}const d=m(t,".check");if(n(d))try{i(d,{recursive:!0,force:!0}),console.log(" removed ~/.check/")}catch{}0===s?console.log(" No Check installations found.\n"):(console.log(`\n Removed from ${s} tool${1!==s?"s":""}.`),console.log(" CLAUDE.md / AGENTS.md rules were left in place.\n"));try{await r}catch{}process.exit(0)}(),j.includes("--status")&&await async function(){const o=p(),t=m(o,".check"),s="win32"===f()?".exe":"",r=T();if(console.log(`\n Check v${w}\n`),r){const e=r.length>12?r.slice(0,8)+"..."+r.slice(-4):r;console.log(" Client ID: "+e)}else console.log(" Client ID: not set");const c=m(t,"truth-gate"+s);console.log(" Binary: "+(n(c)?"installed":"not found"));const i=m(t,"check.mjs");console.log(" Script: "+(n(i)?"installed":"not found")),console.log("\n Integrations:\n");const a=[{name:"Claude Code",path:m(o,".claude","settings.json"),test:e=>e.hooks?.PreToolUse?.some(e=>e.hooks?.some(O))||e.hooks?.PostToolUse?.some(e=>e.hooks?.some(O)),detail:e=>{const o=[];return e.hooks?.PreToolUse?.some(e=>e.hooks?.some(O))&&o.push("PreToolUse"),e.hooks?.PostToolUse?.some(e=>e.hooks?.some(O))&&o.push("PostToolUse"),o.join(", ")}},{name:"Gemini CLI",path:m(o,".gemini","settings.json"),test:e=>e.hooks?.BeforeTool?.some(e=>e.hooks?.some(e=>"check-gate"===e.name)),detail:()=>"BeforeTool"},{name:"Cursor",path:m(o,".cursor","hooks.json"),test:e=>e.hooks?.beforeShellExecution?.some(e=>b(e.command)),detail:()=>"beforeShellExecution"},{name:"Homebase",path:m(o,".homebase","hooks.json"),test:e=>e.hooks?.PreToolUse?.some(e=>e.hooks?.some(O))||e.hooks?.PostToolUse?.some(e=>e.hooks?.some(O)),detail:e=>{const o=[];return e.hooks?.PreToolUse?.some(e=>e.hooks?.some(O))&&o.push("PreToolUse"),e.hooks?.PostToolUse?.some(e=>e.hooks?.some(O))&&o.push("PostToolUse"),o.join(", ")}},{name:"Windsurf",path:m(o,".codeium","windsurf","mcp_config.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP"},{name:"Continue",path:m(process.cwd(),".continue","mcpServers","check.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP (project)"},{name:"Amazon Q",path:m(o,".aws","amazonq","mcp.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP"},{name:"Roo Code",path:m(process.cwd(),".roo","mcp.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP (project)"},{name:"Project MCP",path:m(process.cwd(),".mcp.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP (project)"}];for(const o of a)if(n(o.path))try{const t=JSON.parse(e(o.path,"utf8"));o.test(t)?console.log(" + "+o.name.padEnd(14)+" active ("+o.detail(t)+")"):console.log(" - "+o.name.padEnd(14)+" tool installed, Check not configured")}catch{console.log(" ? "+o.name.padEnd(14)+" config unreadable")}else console.log(" - "+o.name.padEnd(14)+" not detected");console.log(""),process.exit(0)}(),j.includes("--credits")&&await async function(){const e=T();e||(console.log("\n No Client ID found. Set GOL_CLIENT_ID or run --install first.\n"),process.exit(1));try{const o=await fetch(v.replace("/preflight","/credits"),{method:"GET",headers:{"X-GOL-CLIENT-ID":e,"User-Agent":"c/"+w},signal:AbortSignal.timeout(1e4)});o.ok||(console.log("\n Could not fetch credits (HTTP "+o.status+").\n"),process.exit(1));const t=await o.json();console.log(`\n Check v${w}: Credits\n`),void 0!==t.free_remaining&&console.log(" Free today: "+t.free_remaining+" / "+(t.free_daily||120)),void 0!==t.calls_today&&console.log(" Calls today: "+t.calls_today),void 0!==t.balance&&console.log(" Balance: $"+t.balance+" AUD"),void 0!==t.total_calls&&console.log(" Total calls: "+t.total_calls),t.plan&&console.log(" Plan: "+t.plan),0===Object.keys(t).length&&console.log(" "+JSON.stringify(t)),console.log("")}catch(e){console.log("\n Could not reach API: "+e.message+"\n")}process.exit(0)}(),j.includes("--docs")&&await async function(){console.log(`\n Check v${w}: Documentation\n\n Install: npx @golproductions/check --install <key>\n Uninstall: npx @golproductions/check --uninstall\n Status: npx @golproductions/check --status\n Credits: npx @golproductions/check --credits\n\n How it works:\n\n Check installs hooks into your AI coding tools. Every time\n the AI runs a command or edits a file, Check verifies it\n before or after execution.\n\n PreToolUse Validates commands before they run.\n PostToolUse Verifies results after execution.\n MCP Server Provides Check and CheckAndExecute tools.\n CLAUDE.md Adds anti-fabrication instructions.\n\n Supported tools:\n\n Claude Code, Gemini CLI, Cursor, Windsurf, Continue,\n Amazon Q Developer, Roo Code, Homebase\n\n Pricing:\n\n 120 free checks per day. Then $0.0068 AUD per check.\n Credits never expire. Pay as you go.\n\n Links:\n\n Website: https://golproductions.com/check\n Docs: https://github.com/golproductions/check\n Support: support@golproductions.com\n`);try{const{execSync:e}=await import("node:child_process"),o="https://github.com/golproductions/check";"win32"===f()?e(`start "" "${o}"`,{stdio:"ignore",shell:!0}):"darwin"===f()?e(`open "${o}"`,{stdio:"ignore"}):e(`xdg-open "${o}"`,{stdio:"ignore"})}catch{}process.exit(0)}(),(j.includes("--help")||j.includes("-h"))&&_(),j.includes("--mcp")&&(await import("./mcp.js"),process.exit(0)),!j.some(e=>e.startsWith("-"))&&process.stdin.isTTY&&_(),async function(){let t="claude";try{let s,r="";for await(const e of process.stdin)r+=e;r=r.replace(/^/,"").trim(),r||await P();try{s=JSON.parse(r)}catch{return void L("claude",!1,"check: unreadable hook input (not JSON), command blocked")}if("Stop"===s.hook_event_name){try{const o=m(p(),".check","last-fail.json");if(!s.stop_hook_active&&n(o)){const t=JSON.parse(e(o,"utf8")),n=6e5;t&&t.ts&&Date.now()-t.ts<n&&(c(o),process.stdout.write(JSON.stringify({decision:"block",reason:"Check: the last shell command exited "+t.code+" ("+(t.cmd||"").slice(0,120)+(t.stderr?" - "+t.stderr.slice(0,160):"")+"). Address the failure or tell the user about it before finishing."})),process.exit(0)),c(o)}}catch{}process.exit(0)}if("PostToolUse"===s.hook_event_name){try{const e=s.tool_response||{},t=(s.tool_input&&s.tool_input.command||"").slice(0,4e3),r=T();if(t&&r){const s="number"==typeof e.exit_code?e.exit_code:"number"==typeof e.exitCode?e.exitCode:null,i=null!==s?0!==s:!0===e.interrupted||!!e.error,a="string"==typeof e.stderr?e.stderr:"",l=(t.trim().split(/\s+/)[0]||"").split(/[\\/]/).pop(),h=new Set(["grep","egrep","fgrep","rg","test","[","[[","diff","cmp"]),d=1===s&&h.has(l),u=i&&!d;try{const e=m(p(),".check","last-fail.json");u?o(e,JSON.stringify({ts:Date.now(),code:s,cmd:t.slice(0,200),stderr:a.slice(0,200)}),"utf8"):!i&&n(e)&&c(e)}catch{}await fetch(v.replace("/preflight","/log"),{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":r,"User-Agent":"c/"+w},body:JSON.stringify({verdict:u?"outcome_fail":"outcome_ok",reason:u?"exit "+(null!==s?s:"?")+(a?": "+a.slice(0,200):""):"",binary:(t.trim().split(/\s+/)[0]||"").slice(0,64),cmd:t,channel:"return-path"}),signal:AbortSignal.timeout(3e3)})}}catch{}return void process.exit(0)}t=function(e){return"string"!=typeof e.command||e.tool_input?"BeforeTool"===e.hook_event_name||e.toolCall?.argumentsJson?"gemini":"claude":"cursor"}(s);const i=function(e,o){if("cursor"===o)return e.command;if("gemini"===o){if(e.tool_input?.command)return e.tool_input.command;if(e.toolCall?.argumentsJson)try{const o="string"==typeof e.toolCall.argumentsJson?JSON.parse(e.toolCall.argumentsJson):e.toolCall.argumentsJson;return o.command||o.CommandLine||o.command_line}catch{return null}return null}return e.tool_input?.command}(s,t);if(!i)return void L(t,!0);const a=T();if(!a)return void L(t,!1,"check: GOL_CLIENT_ID not set. Get your key at https://golproductions.com/check");const l=async e=>{try{const o=await function(e,o){return new Promise((t,n)=>{const s=d(I,"127.0.0.1"),r=setTimeout(()=>{s.destroy(),n(new Error("daemon timeout"))},3e3);let c="";s.on("error",e=>{clearTimeout(r),n(e)}),s.on("connect",()=>{s.write(JSON.stringify({v:w,key:e,body:o})+"\n")}),s.on("data",e=>{c+=e;const o=c.indexOf("\n");if(-1!==o){clearTimeout(r),s.destroy();try{const e=JSON.parse(c.slice(0,o));if(1!==e.gol)return n(new Error("not our daemon"));if(e.stale)return U(),n(new Error("stale daemon replaced"));if(e.err)return n(new Error("daemon upstream error"));t({status:e.status,text:e.text})}catch(e){n(e)}}}),s.on("close",()=>{clearTimeout(r),n(new Error("daemon closed"))})})}(a,e);return{res:{ok:o.status>=200&&o.status<300,status:o.status},d:JSON.parse(o.text)}}catch{U()}const o=new AbortController,t=setTimeout(()=>o.abort(),5e3),n=await fetch(v,{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":a,"User-Agent":"c/"+w},body:JSON.stringify(e),signal:o.signal});return clearTimeout(t),{res:n,d:await n.json()}},h={command:i,cwd:s.cwd||process.cwd(),platform:t,channel:"npm",tool_name:s.tool_name,v:w,probe_ok:1,path_ok:1,git_ok:1},u="Bash"===s.tool_name?async function(e){try{const o=await E(["-n"],{input:e});if(null===o)return null;if(0===o.status){const e=(o.stderr||"").split("\n").find(e=>/here-document.*end-of-file/i.test(e));return!!e&&e.replace(/^[^:]*bash[^:]*:\s*/i,"").trim()}return((o.stderr||"").split("\n").find(e=>e.trim())||"syntax error").replace(/^[^:]*bash[^:]*:\s*/i,"").trim()}catch{return null}}(i):Promise.resolve(null),f=l(h).catch(e=>{throw e});f.catch(()=>{});const g=await u;if("string"==typeof g)return void L(t,!1,`check: syntax error. ${g}`);let{res:k,d:y}=await f;if(k.ok&&"probe"===y.verdict&&Array.isArray(y.probe)){const e=y.probe.slice(0,8).filter(e=>"string"==typeof e&&e&&e.length<=500),o=await Promise.all(e.map(e=>{if(e.startsWith("path:")){const o=e.slice(5),t=/^([A-Za-z]:[\\/]|\/)/.test(o)?o:m(s.cwd||process.cwd(),o);try{return Promise.resolve({exists:n(t),why:""})}catch{return Promise.resolve(null)}}return e.startsWith("gitcmd:")?async function(e,o){try{const t=await E(["-c","command -v git >/dev/null 2>&1 || exit 3; git --list-cmds=builtins,others 2>/dev/null || exit 4; git config --get-regexp '^alias\\.' 2>/dev/null"],{cwd:o});if(null===t||3===t.status||4===t.status)return null;const n=new Set;for(const e of(t.stdout||"").split(/\r?\n/)){const o=e.trim();if(o)if(o.startsWith("alias.")){const e=o.slice(6).split(/\s/)[0];e&&n.add(e)}else n.add(o.split(/\s/)[0])}return 0===n.size?null:n.has(e)?{exists:!0}:{exists:!1,why:"'"+e+"' is not a git command"}}catch{return null}}(e.slice(7),s.cwd||process.cwd()):e.startsWith("gitref:")?async function(e,o){try{const t=await E(["-c",'command -v git >/dev/null 2>&1 || exit 3; git rev-parse --verify --quiet "$1" >/dev/null 2>&1 && exit 0; git ls-files --error-unmatch -- "$1" >/dev/null 2>&1 && exit 0; exit 1',"check",e],{cwd:o});return null===t||3===t.status?null:0===t.status?{exists:!0}:1===t.status?{exists:!1,why:"'"+e+"' is not a ref or tracked path in this repo"}:null}catch{return null}}(e.slice(7),s.cwd||process.cwd()):async function(e,o){try{const t=await E(["-c",'type -- "$1"',"check",e],{cwd:o});if(null===t)return null;if(0===t.status)return{exists:!0};const n=(t.stderr||"").split("\n").find(o=>o.includes(e))||"";return{exists:!1,why:n.slice(n.indexOf(e)).trim()||e+": not found"}}catch{return null}}(e,s.cwd||process.cwd())})),t={},r={};e.forEach((e,n)=>{const s=o[n];null!==s&&(t[e]=s.exists,!s.exists&&s.why&&(r[e]=s.why))}),({res:k,d:y}=await l({...h,probe:t,probe_why:r}))}if(!k.ok){if(429===k.status||/rate limit/i.test(y.reason||y.error||""))return void D(t,"check: rate limited (60 checks/min), command ran unverified. Check resumes automatically.");if(k.status>=500)return void D(t,"check: validation service error (HTTP "+k.status+"), command ran unverified. Check resumes automatically.");if(402===k.status){return void D(t,`check: you've used all ${y.daily_free||120} free checks for today and your balance is empty. Check is off until tomorrow, commands run unverified. Credits you buy never expire. ${y.upgrade||"Top up at https://www.golproductions.com/console.html"}`)}return void L(t,!1,y.reason||y.error||"check: request not authorized, command blocked")}"runnable"===y.verdict?L(t,!0):L(t,!1,y.reason||"denied. Address the issue before continuing.")}catch{D(t,"check: validation service unreachable, command ran unverified. Check resumes automatically.")}}();
2
+ import{readFileSync as e,writeFileSync as o,mkdirSync as t,existsSync as n,createWriteStream as s,chmodSync as r,unlinkSync as c,rmSync as i}from"node:fs";import{spawnSync as l,spawn as a}from"node:child_process";import{join as h}from"node:path";import{homedir as u,platform as d,arch as m}from"node:os";import{pipeline as p}from"node:stream/promises";import{Readable as f}from"node:stream";const k="3.3.7",g="https://triage.golproductions.com/preflight",y=process.env.GOL_CLIENT_ID||"";function v(){if(y)return y;try{const o=e(h(u(),".check","key"),"utf8").trim();if(o)return o}catch{}try{const o=JSON.parse(e(h(u(),".claude","settings.json"),"utf8"));return o.env?.GOL_CLIENT_ID||""}catch{return""}}function w(e){return e&&(e.includes(".check")||e.includes("truth-gate"))}function C(e){return!!w(e.command)||!!e.args?.some(e=>w(e))}function T(){console.log(`\n Check v${k} — Anti-hallucination layer for AI coding agents\n\n Usage:\n\n npx @golproductions/check --install <key> Install Check with your Client ID\n npx @golproductions/check --uninstall Remove Check from all tools\n npx @golproductions/check --status Show what is installed and active\n npx @golproductions/check --credits Check your usage and balance\n npx @golproductions/check --docs View documentation\n npx @golproductions/check --help Show this help\n\n Get your key: https://golproductions.com/check\n Pricing: 120 free/day, then $0.0068 AUD per check\n`),process.exit(0)}const P=process.argv.slice(2);function S(e,o,t){if(o){const o={cursor:{permission:"allow"},gemini:{decision:"allow"},claude:{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}}};return process.stdout.write(JSON.stringify(o[e]||o.claude)),void(process.exitCode=0)}if("gemini"===e)return process.stdout.write(JSON.stringify({decision:"deny",reason:t})),void(process.exitCode=2);const n={cursor:{permission:"deny",agent_message:t},claude:{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:t}}};process.stdout.write(JSON.stringify(n[e]||n.claude)),process.exitCode=0}function b(e,s){let r=!0;try{const e=h(u(),".check","credits-warned-"+(new Date).toISOString().slice(0,10));r=n(e),r||(t(h(u(),".check"),{recursive:!0}),o(e,"1"))}catch{r=!1}const c=r?void 0:s;if("gemini"===e)return process.stdout.write(JSON.stringify({decision:"allow"})),c&&process.stderr.write(c),void(process.exitCode=0);if("cursor"===e){const e={permission:"allow"};return c&&(e.user_message=c),process.stdout.write(JSON.stringify(e)),void(process.exitCode=0)}const i={hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"allow"}};c&&(i.systemMessage=c),process.stdout.write(JSON.stringify(i)),process.exitCode=0}P.includes("--install")&&await async function(){let c=process.argv[3]||process.env.GOL_CLIENT_ID||"";c&&"your_key"!==c||(console.log("\n Activating Check (free, no signup)...\n"),c=await async function(){try{const e=await async function(){const{createHash:e}=await import("node:crypto"),{hostname:o,userInfo:t}=await import("node:os");let n="";try{n=t().username||""}catch{}const s=[o(),d(),m(),n].join("|");return e("sha256").update(s).digest("hex")}(),o=await fetch(g.replace("/preflight","/instant-key"),{method:"POST",headers:{"Content-Type":"application/json","User-Agent":"c/"+k},body:JSON.stringify({fingerprint:e,channel:"npm"}),signal:AbortSignal.timeout(1e4)});if(!o.ok)return null;const t=await o.json();return t&&t.client_id?t.client_id:null}catch{return null}}(),c||(console.log(" Could not activate automatically (offline, or this network hit its daily limit).\n"),console.log(" Get a key the manual way: https://golproductions.com/check\n"),console.log(" Then run: npx @golproductions/check --install YOUR_KEY\n"),process.exit(1)),console.log(" Activated. 120 free checks per day.\n"));const i=await async function(e){try{const o=await fetch(g,{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":e,"User-Agent":"c/"+k},body:JSON.stringify({command:"echo check-install-verify",cwd:process.cwd(),v:k}),signal:AbortSignal.timeout(1e4)});if(o.ok)return{valid:!0,expired:!1};let t={};try{t=await o.json()}catch{}return{valid:!1,expired:/expir/i.test(t.reason||t.error||t.message||"")}}catch{return{valid:!1,expired:!1}}}(c);i.valid||(i.expired?(console.log("\n Your Client ID has expired: "+c),console.log("\n Renew your key at: https://golproductions.com/check\n")):(console.log("\n Invalid Client ID: "+c),console.log("\n Get a valid key at: https://golproductions.com/check\n")),process.exit(1)),console.log("\n Key verified. Installing Check...\n");const l=u();let a=0;const y=e(new URL(import.meta.url),"utf8"),v=h(l,".check");t(v,{recursive:!0});const T=h(v,"check.mjs").replace(/\\/g,"/");o(T,y,"utf8"),o(h(v,"key"),c,"utf8");const P="win32"===d()?".exe":"",S=h(v,"truth-gate"+P).replace(/\\/g,"/");await async function(e){const o="win32"===d()?"win":"darwin"===d()?"macos":"linux",t=`https://pub-e55366a7f5994be9be04f0e205179f4a.r2.dev/releases/truth-gate-v3.0.0-${o}-${"arm64"===m()?"arm64":"x64"}${"win"===o?".exe":""}`,n=await fetch(t);if(!n.ok)throw new Error(`Download failed: ${n.status}`);if(await p(f.fromWeb(n.body),s(e)),"win"!==o)try{r(e,493)}catch{}}(S);const b={command:"npx",args:["@golproductions/check","--mcp"],env:{GOL_CLIENT_ID:c}},$=[{name:"Claude Code",dir:h(l,".claude"),file:"settings.json",config:e=>(e.hooks=e.hooks||{},e.hooks.PreToolUse=(e.hooks.PreToolUse||[]).filter(e=>!e.hooks?.some(C)),e.hooks.PreToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:"node",args:[T]}]}),e.hooks.PostToolUse=(e.hooks.PostToolUse||[]).filter(e=>!e.hooks?.some(C)),e.hooks.PostToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:S}]},{matcher:"Edit|Write|NotebookEdit",hooks:[{type:"command",command:S}]}),e.env=e.env||{},e.env.GOL_CLIENT_ID=c,e)},{name:"Gemini CLI / Antigravity",dir:h(l,".gemini"),file:"settings.json",config:e=>{e.hooks=e.hooks||{},e.hooks.BeforeTool=e.hooks.BeforeTool||[];const o=e.hooks.BeforeTool;return o.find(e=>e.hooks?.some(e=>"check-gate"===e.name))||o.push({matcher:".*",hooks:[{type:"command",command:"node "+T,name:"check-gate"}]}),e}},{name:"Cursor",dir:h(l,".cursor"),file:"hooks.json",config:e=>(e.version=e.version||1,e.hooks=e.hooks||{},e.hooks.beforeShellExecution=(e.hooks.beforeShellExecution||[]).filter(e=>!w(e.command)),e.hooks.beforeShellExecution.push({command:"node "+T}),e)},{name:"Homebase",dir:h(l,".homebase"),file:"hooks.json",config:e=>(e.hooks=e.hooks||{},e.hooks.PreToolUse=(e.hooks.PreToolUse||[]).filter(e=>!e.hooks?.some(C)),e.hooks.PreToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:"node",args:[T]}]}),e.hooks.PostToolUse=(e.hooks.PostToolUse||[]).filter(e=>!e.hooks?.some(C)),e.hooks.PostToolUse.push({matcher:"Bash|PowerShell",hooks:[{type:"command",command:S}]}),e.env=e.env||{},e.env.GOL_CLIENT_ID=c,e)},{name:"Windsurf (MCP)",dir:h(l,".codeium","windsurf"),file:"mcp_config.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check=b,e)},{name:"Continue (MCP)",dir:h(process.cwd(),".continue","mcpServers"),file:"check.json",config:()=>({mcpServers:{Check:b}})},{name:"Amazon Q Developer (MCP)",dir:h(l,".aws","amazonq"),file:"mcp.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check={type:"stdio",...b},e)},{name:"Roo Code (project MCP)",dir:h(process.cwd(),".roo"),file:"mcp.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check=b,e)},{name:"Project MCP (shared)",dir:process.cwd(),file:".mcp.json",config:e=>(e.mcpServers=e.mcpServers||{},e.mcpServers.Check=b,e)}],x=["Windsurf (MCP)","Continue (MCP)","Amazon Q Developer (MCP)","Roo Code (project MCP)","Project MCP (shared)"];let _=!1;for(const s of $){const r=x.includes(s.name);if(!r&&!n(s.dir))continue;r&&t(s.dir,{recursive:!0});const c=h(s.dir,s.file);let i={};try{i=JSON.parse(e(c,"utf8"))}catch{}const l=s.config(i);o(c,JSON.stringify(l,null,2)+"\n","utf8"),a++,"Claude Code"===s.name&&(_=!0)}if(0===a){t(h(l,".claude"),{recursive:!0});const n=h(l,".claude","settings.json");let s={};try{s=JSON.parse(e(n,"utf8"))}catch{}const r=$[0].config(s);o(n,JSON.stringify(r,null,2)+"\n","utf8"),_=!0}const U=_?"CLAUDE.md":"AGENTS.md",j=h(process.cwd(),U);try{const t=n(j)?e(j,"utf8"):"";if(!t.includes("Never fabricate")){const e=t.length>0?t.trimEnd()+"\n\n":"";o(j,e+"Never fabricate, hallucinate, or invent values. If you don't have it, say you don't have it. No fake keys, no fake IDs, no fake paths, no fake URLs. If unsure, ask. Never guess and present it as fact.\n","utf8")}}catch{}console.log(`\n Your free GOL Client ID (this machine): ${c}`),console.log(" Shared by every editor on this machine. 120 free checks/day."),console.log(" Need more? Get a GOL API Key (paid, prepaid balance) at"),console.log(" https://www.golproductions.com/console"),console.log("\n By using Check you agree to the Terms and Privacy Policy:"),console.log(" golproductions.com/terms | golproductions.com/privacy"),console.log("\nSafe travels wanderer..\n"),process.exit(0)}(),P.includes("--uninstall")&&await async function(){const t=u();let s=0;console.log("\n Removing Check...\n");let r=Promise.resolve();try{const e=v();e&&(r=fetch(g.replace("/preflight","/log"),{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":e,"User-Agent":"c/"+k},body:JSON.stringify({verdict:"uninstall",channel:"npm",os:d(),arch:m(),version:k}),signal:AbortSignal.timeout(3e3)}).catch(()=>{}))}catch{}const l=[{name:"Claude Code",path:h(t,".claude","settings.json"),clean:e=>(e.hooks?.PreToolUse&&(e.hooks.PreToolUse=e.hooks.PreToolUse.filter(e=>!e.hooks?.some(C)),0===e.hooks.PreToolUse.length&&delete e.hooks.PreToolUse),e.hooks?.PostToolUse&&(e.hooks.PostToolUse=e.hooks.PostToolUse.filter(e=>!e.hooks?.some(C)),0===e.hooks.PostToolUse.length&&delete e.hooks.PostToolUse),e.hooks&&0===Object.keys(e.hooks).length&&delete e.hooks,e.env&&(delete e.env.GOL_CLIENT_ID,0===Object.keys(e.env).length&&delete e.env),e)},{name:"Gemini CLI",path:h(t,".gemini","settings.json"),clean:e=>(e.hooks?.BeforeTool&&(e.hooks.BeforeTool=e.hooks.BeforeTool.filter(e=>!e.hooks?.some(e=>"check-gate"===e.name)),0===e.hooks.BeforeTool.length&&delete e.hooks.BeforeTool,0===Object.keys(e.hooks).length&&delete e.hooks),e)},{name:"Cursor",path:h(t,".cursor","hooks.json"),clean:e=>(e.hooks?.beforeShellExecution&&(e.hooks.beforeShellExecution=e.hooks.beforeShellExecution.filter(e=>!w(e.command)),0===e.hooks.beforeShellExecution.length&&delete e.hooks.beforeShellExecution,0===Object.keys(e.hooks).length&&delete e.hooks),e)},{name:"Homebase",path:h(t,".homebase","hooks.json"),clean:e=>(e.hooks?.PreToolUse&&(e.hooks.PreToolUse=e.hooks.PreToolUse.filter(e=>!e.hooks?.some(C)),0===e.hooks.PreToolUse.length&&delete e.hooks.PreToolUse),e.hooks?.PostToolUse&&(e.hooks.PostToolUse=e.hooks.PostToolUse.filter(e=>!e.hooks?.some(C)),0===e.hooks.PostToolUse.length&&delete e.hooks.PostToolUse),e.hooks&&0===Object.keys(e.hooks).length&&delete e.hooks,e.env&&(delete e.env.GOL_CLIENT_ID,0===Object.keys(e.env).length&&delete e.env),e)}],a=[{name:"Windsurf",path:h(t,".codeium","windsurf","mcp_config.json")},{name:"Amazon Q",path:h(t,".aws","amazonq","mcp.json")}],p=[{name:"Continue",path:h(process.cwd(),".continue","mcpServers","check.json"),deleteFile:!0},{name:"Roo Code",path:h(process.cwd(),".roo","mcp.json")},{name:"Project MCP",path:h(process.cwd(),".mcp.json")}];for(const t of l)if(n(t.path))try{const n=JSON.parse(e(t.path,"utf8")),r=t.clean(n);o(t.path,JSON.stringify(r,null,2)+"\n","utf8"),console.log(" removed "+t.name),s++}catch{}for(const t of[...a,...p])if(n(t.path))try{if(t.deleteFile)c(t.path),console.log(" removed "+t.name),s++;else{const n=JSON.parse(e(t.path,"utf8"));n.mcpServers?.Check&&(delete n.mcpServers.Check,0===Object.keys(n.mcpServers).length&&delete n.mcpServers,o(t.path,JSON.stringify(n,null,2)+"\n","utf8"),console.log(" removed "+t.name),s++)}}catch{}const f=h(t,".check");if(n(f))try{i(f,{recursive:!0,force:!0}),console.log(" removed ~/.check/")}catch{}0===s?console.log(" No Check installations found.\n"):(console.log(`\n Removed from ${s} tool${1!==s?"s":""}.`),console.log(" CLAUDE.md / AGENTS.md rules were left in place.\n"));try{await r}catch{}process.exit(0)}(),P.includes("--status")&&await async function(){const o=u(),t=h(o,".check"),s="win32"===d()?".exe":"",r=v();if(console.log(`\n Check v${k}\n`),r){const e=r.length>12?r.slice(0,8)+"..."+r.slice(-4):r;console.log(" Client ID: "+e)}else console.log(" Client ID: not set");const c=h(t,"truth-gate"+s);console.log(" Binary: "+(n(c)?"installed":"not found"));const i=h(t,"check.mjs");console.log(" Script: "+(n(i)?"installed":"not found")),console.log("\n Integrations:\n");const l=[{name:"Claude Code",path:h(o,".claude","settings.json"),test:e=>e.hooks?.PreToolUse?.some(e=>e.hooks?.some(C))||e.hooks?.PostToolUse?.some(e=>e.hooks?.some(C)),detail:e=>{const o=[];return e.hooks?.PreToolUse?.some(e=>e.hooks?.some(C))&&o.push("PreToolUse"),e.hooks?.PostToolUse?.some(e=>e.hooks?.some(C))&&o.push("PostToolUse"),o.join(", ")}},{name:"Gemini CLI",path:h(o,".gemini","settings.json"),test:e=>e.hooks?.BeforeTool?.some(e=>e.hooks?.some(e=>"check-gate"===e.name)),detail:()=>"BeforeTool"},{name:"Cursor",path:h(o,".cursor","hooks.json"),test:e=>e.hooks?.beforeShellExecution?.some(e=>w(e.command)),detail:()=>"beforeShellExecution"},{name:"Homebase",path:h(o,".homebase","hooks.json"),test:e=>e.hooks?.PreToolUse?.some(e=>e.hooks?.some(C))||e.hooks?.PostToolUse?.some(e=>e.hooks?.some(C)),detail:e=>{const o=[];return e.hooks?.PreToolUse?.some(e=>e.hooks?.some(C))&&o.push("PreToolUse"),e.hooks?.PostToolUse?.some(e=>e.hooks?.some(C))&&o.push("PostToolUse"),o.join(", ")}},{name:"Windsurf",path:h(o,".codeium","windsurf","mcp_config.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP"},{name:"Continue",path:h(process.cwd(),".continue","mcpServers","check.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP (project)"},{name:"Amazon Q",path:h(o,".aws","amazonq","mcp.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP"},{name:"Roo Code",path:h(process.cwd(),".roo","mcp.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP (project)"},{name:"Project MCP",path:h(process.cwd(),".mcp.json"),test:e=>!!e.mcpServers?.Check,detail:()=>"MCP (project)"}];for(const o of l)if(n(o.path))try{const t=JSON.parse(e(o.path,"utf8"));o.test(t)?console.log(" + "+o.name.padEnd(14)+" active ("+o.detail(t)+")"):console.log(" - "+o.name.padEnd(14)+" tool installed, Check not configured")}catch{console.log(" ? "+o.name.padEnd(14)+" config unreadable")}else console.log(" - "+o.name.padEnd(14)+" not detected");console.log(""),process.exit(0)}(),P.includes("--credits")&&await async function(){const e=v();e||(console.log("\n No Client ID found. Set GOL_CLIENT_ID or run --install first.\n"),process.exit(1));try{const o=await fetch(g.replace("/preflight","/credits"),{method:"GET",headers:{"X-GOL-CLIENT-ID":e,"User-Agent":"c/"+k},signal:AbortSignal.timeout(1e4)});o.ok||(console.log("\n Could not fetch credits (HTTP "+o.status+").\n"),process.exit(1));const t=await o.json();console.log(`\n Check v${k} — Credits\n`),void 0!==t.free_remaining&&console.log(" Free today: "+t.free_remaining+" / "+(t.free_daily||120)),void 0!==t.calls_today&&console.log(" Calls today: "+t.calls_today),void 0!==t.balance&&console.log(" Balance: $"+t.balance+" AUD"),void 0!==t.total_calls&&console.log(" Total calls: "+t.total_calls),t.plan&&console.log(" Plan: "+t.plan),0===Object.keys(t).length&&console.log(" "+JSON.stringify(t)),console.log("")}catch(e){console.log("\n Could not reach API: "+e.message+"\n")}process.exit(0)}(),P.includes("--docs")&&await async function(){console.log(`\n Check v${k} — Documentation\n\n Install: npx @golproductions/check --install <key>\n Uninstall: npx @golproductions/check --uninstall\n Status: npx @golproductions/check --status\n Credits: npx @golproductions/check --credits\n\n How it works:\n\n Check installs hooks into your AI coding tools. Every time\n the AI runs a command or edits a file, Check verifies it\n before or after execution.\n\n PreToolUse Validates commands before they run.\n PostToolUse Verifies results after execution.\n MCP Server Provides Check and CheckAndExecute tools.\n CLAUDE.md Adds anti-fabrication instructions.\n\n Supported tools:\n\n Claude Code, Gemini CLI, Cursor, Windsurf, Continue,\n Amazon Q Developer, Roo Code, Homebase\n\n Pricing:\n\n 120 free checks per day. Then $0.0068 AUD per check.\n Credits never expire. Pay as you go.\n\n Links:\n\n Website: https://golproductions.com/check\n Docs: https://github.com/golproductions/check\n Support: support@golproductions.com\n`);try{const{execSync:e}=await import("node:child_process"),o="https://github.com/golproductions/check";"win32"===d()?e(`start "" "${o}"`,{stdio:"ignore",shell:!0}):"darwin"===d()?e(`open "${o}"`,{stdio:"ignore"}):e(`xdg-open "${o}"`,{stdio:"ignore"})}catch{}process.exit(0)}(),(P.includes("--help")||P.includes("-h"))&&T(),P.includes("--mcp")&&(await import("./mcp.js"),process.exit(0)),!P.some(e=>e.startsWith("-"))&&process.stdin.isTTY&&T();const $=new Set(["sudo","nohup","nice","time","timeout","env"]),x=new Set(["cd","echo","printf","pwd","export","set","unset","alias","unalias","true","false","test","[","[[","exit","return","break","continue","shift","source",".","eval","exec","trap","wait","jobs","fg","bg","read","readonly","local","declare","typeset","let","getopts","hash","type","command","builtin","ulimit","umask","times","dirs","pushd","popd","for","while","until","if","then","else","elif","fi","do","done","case","esac","in","function","select","{","}","!"]);function _(){if("win32"!==d())return"bash";const e=[process.env.CLAUDE_CODE_GIT_BASH_PATH,"C:/Program Files/Git/bin/bash.exe","C:/Program Files/Git/usr/bin/bash.exe","C:/Program Files (x86)/Git/bin/bash.exe"].filter(Boolean);for(const o of e)try{if(n(o))return o}catch{}return null}function U(e,o){if(!e)return null;const t=e.split(/\r?\n/).find(e=>e.includes(o));if(!t)return null;let n=t.slice(t.indexOf(o)+o.length);return n=n.replace(/^['"\s:]+/,"").replace(/\s+$/,""),n?o+": "+n:null}function j(e,o,t){try{if("powershell"===t){const t=l("powershell",["-NoProfile","-NonInteractive","-Command","try { $null = Get-Command $env:CHECK_PROBE -ErrorAction Stop; exit 0 } catch { [Console]::Error.WriteLine($_.Exception.Message); exit 1 }"],{cwd:o,timeout:4e3,encoding:"utf8",env:{...process.env,CHECK_PROBE:e}});return t.error?null:{known:0===t.status,why:0===t.status?null:U(t.stderr,e)}}const n=_();if(!n)return null;const s=l(n,["-c",'type -- "$1"',"check",e],{cwd:o,timeout:4e3,encoding:"utf8"});return s.error?null:{known:0===s.status,why:0===s.status?null:U(s.stderr,e)}}catch{return null}}!async function(){let e="claude";try{let o,t="";for await(const e of process.stdin)t+=e;t=t.replace(/^/,"").trim(),t||(process.stderr.write("check: no hook input on stdin. This entrypoint is for tool hooks; run --help for CLI usage.\n"),process.exit(1));try{o=JSON.parse(t)}catch{return void S("claude",!1,"check: unreadable hook input (not JSON), command blocked")}if("PostToolUse"===o.hook_event_name)return void process.exit(0);e=function(e){return"string"!=typeof e.command||e.tool_input?"BeforeTool"===e.hook_event_name||e.toolCall?.argumentsJson?"gemini":"claude":"cursor"}(o);const n=function(e,o){if("cursor"===o)return e.command;if("gemini"===o){if(e.tool_input?.command)return e.tool_input.command;if(e.toolCall?.argumentsJson)try{const o="string"==typeof e.toolCall.argumentsJson?JSON.parse(e.toolCall.argumentsJson):e.toolCall.argumentsJson;return o.command||o.CommandLine||o.command_line}catch{return null}return null}return e.tool_input?.command}(o,e);if(!n)return void S(e,!0);const s=v();if(!s)return void S(e,!1,"check: GOL_CLIENT_ID not set. Get your key at https://golproductions.com/check");if("Bash"===o.tool_name){const o=await async function(e){try{const o=_();if(!o)return null;const t=l(o,["-n"],{input:e,timeout:4e3,encoding:"utf8"});if(t.error)return null;if(0===t.status){const e=(t.stderr||"").split("\n").find(e=>/here-document.*end-of-file/i.test(e));return!!e&&e.replace(/^[^:]*bash[^:]*:\s*/i,"").trim()}return((t.stderr||"").split("\n").find(e=>e.trim())||"syntax error").replace(/^[^:]*bash[^:]*:\s*/i,"").trim()}catch{return null}}(n);if("string"==typeof o)return void S(e,!1,`check: syntax error — ${o}`)}const r="PowerShell"===o.tool_name?"powershell":"bash",c=[],i={probe:{},why:{}},h=function(e,o,t,n,s){try{let r=function(e){const o=[[]];let t="",n=null;for(let s=0;s<e.length;s++){const r=e[s];n?r===n?n=null:t+=r:'"'!==r&&"'"!==r?"|"!==r&&"&"!==r&&";"!==r&&"\n"!==r?/\s/.test(r)?t&&(o[o.length-1].push(t),t=""):t+=r:(t&&(o[o.length-1].push(t),t=""),o[o.length-1].length&&o.push([]),"|"!==r&&"&"!==r||e[s+1]!==r||s++):n=r}t&&o[o.length-1].push(t);const s=new Set(["do","then","else","elif","if","while","until","!","{"]),r=[];for(const e of o){let o=null;for(const t of e)if(t&&!$.has(t)&&!s.has(t)&&!/^[\d.]+[smhd]?$/.test(t)&&(!t.includes("=")||t.startsWith("-"))&&!t.startsWith("-")){o=t;break}o&&r.push(o)}return r}(function(e){const o=e.split("\n"),t=[],n=[];for(const e of o){if(n.length){const{delim:o,dashed:t}=n[0];(t?e.replace(/^\t+/,""):e)===o&&n.shift();continue}t.push(e);for(const o of e.matchAll(/<<(-?)(?!<)\s*(?:'([^']+)'|"([^"]+)"|(\w+))/g))n.push({delim:o[2]||o[3]||o[4],dashed:"-"===o[1]})}return t.join("\n")}(e).replace(/\\\r?\n/g," "));if("powershell"!==t&&(r=r.filter(e=>!x.has(e))),0===r.length)return!0;const c=[...new Set(r.filter(e=>/^[A-Za-z0-9_.\/\\~:@+=-]+$/.test(e)))],i=c.length!==new Set(r).size;let l=!1,a=i;for(const e of c){const r=j(e,o,t);null!==r?(s&&(s.probe[e]=r.known,!r.known&&r.why&&(s.why[e]=r.why)),!1===r.known&&(l=!0,n&&n.push(r.why||e+": not found"))):a=!0}return!l&&(!a||null)}catch{return null}}(n,o.cwd,r,c,i);if(!1===h&&c.length)return void S(e,!1,"denied by the shell that would run it. "+c.join("; "));const u={command:n,cwd:o.cwd||process.cwd(),platform:e,channel:"npm",tool_name:o.tool_name,v:k,probe_ok:1,path_ok:1,git_ok:1};null!==h&&(u.binary_exists=h);const d=(e,o)=>fetch(g,{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":s,"User-Agent":"c/"+k},body:JSON.stringify(e),signal:o}),m=new AbortController,p=setTimeout(()=>m.abort(),5e3);let f=await d(u,m.signal);clearTimeout(p);let y=await f.json();if(f.ok&&"probe"===y.verdict&&Array.isArray(y.probe)){const e=y.probe.filter(e=>!(e in i.probe)),t=await function(e,o,t){const n={probe:{},why:{}};if(!Array.isArray(e)||0===e.length)return Promise.resolve(n);const s=e.filter(e=>"string"==typeof e&&e.length>0&&e.length<600).slice(0,16);if(0===s.length)return Promise.resolve(n);let r;try{if("powershell"===t)r=a("powershell",["-NoProfile","-NonInteractive","-Command","$ks = $env:CHECK_PROBES -split [char]0x1f\nforeach ($k in $ks) {\n if ($k -eq '') { continue }\n $r = 0; $w = ''\n if ($k.StartsWith('path:')) {\n if (Test-Path -LiteralPath $k.Substring(5)) { $r = 1 }\n } elseif ($k.StartsWith('gitcmd:')) {\n $s = $k.Substring(7)\n $out = & git --list-cmds=main,others,alias,parse-opt 2>$null\n if ($out -contains $s) { $r = 1 }\n else { try { $null = Get-Command (\"git-\" + $s) -ErrorAction Stop; $r = 1 } catch {} }\n } elseif ($k.StartsWith('gitref:')) {\n $f = $k.Substring(7)\n $null = & git rev-parse --verify --quiet $f 2>$null\n if ($LASTEXITCODE -eq 0) { $r = 1 }\n else { $null = & git ls-files --error-unmatch -- $f 2>$null; if ($LASTEXITCODE -eq 0) { $r = 1 } }\n } else {\n try { $null = Get-Command $k -ErrorAction Stop; $r = 1 } catch { $w = $_.Exception.Message }\n }\n [Console]::Out.Write($k + [char]0x1e + $r + [char]0x1e + $w + [char]0x1f)\n}"],{cwd:o,env:{...process.env,CHECK_PROBES:s.join("")}});else{const e=_();if(!e)return Promise.resolve(n);r=a(e,["-c",'for k in "$@"; do\n r=0; w=""\n case "$k" in\n path:*) p="${k#path:}"; [ -e "$p" ] && r=1 ;;\n gitcmd:*) s="${k#gitcmd:}"\n if git --list-cmds=main,others,alias,parse-opt 2>/dev/null | grep -Fxq -- "$s"; then r=1\n elif type -- "git-$s" >/dev/null 2>&1; then r=1; fi ;;\n gitref:*) f="${k#gitref:}"\n if git rev-parse --verify --quiet "$f" >/dev/null 2>&1; then r=1\n elif git ls-files --error-unmatch -- "$f" >/dev/null 2>&1; then r=1; fi ;;\n *) if w=$(type -- "$k" 2>&1 >/dev/null); then r=1; w=""; fi ;;\n esac\n printf \'%s\\036%s\\036%s\\037\' "$k" "$r" "$w"\ndone',"check",...s],{cwd:o})}}catch{return Promise.resolve(n)}return new Promise(e=>{let o="";const t=()=>{try{for(const e of o.split("")){if(!e)continue;const[o,t,s]=e.split("");!o||"0"!==t&&"1"!==t||(n.probe[o]="1"===t,"0"===t&&s&&(n.why[o]=s.replace(/[\x00-\x1f\x7f]/g," ").trim().slice(0,200)))}}catch{}e(n)},s=setTimeout(()=>{try{r.kill()}catch{}t()},8e3);r.stdout.on("data",e=>{o+=e}),r.on("error",()=>{clearTimeout(s),e(n)}),r.on("close",()=>{clearTimeout(s),t()})})}(e,o.cwd,r),n={...i.probe,...t.probe},s={...i.why,...t.why},c={},l={};for(const e of y.probe)e in n&&(c[e]=n[e],s[e]&&(l[e]=s[e]));const h={...u,probe:c,probe_why:l};delete h.probe_ok;const m=new AbortController,p=setTimeout(()=>m.abort(),5e3);f=await d(h,m.signal),clearTimeout(p),y=await f.json()}if(!f.ok){if(429===f.status||/rate limit/i.test(y.reason||y.error||""))return void b(e,"check: rate limited (60 checks/min), command ran unverified. Check resumes automatically.");if(f.status>=500)return void b(e,"check: validation service error (HTTP "+f.status+"), command ran unverified. Check resumes automatically.");if(402===f.status){return void b(e,`check: you've used all ${y.daily_free||120} free checks for today and your balance is empty. Check is off until tomorrow — commands run unverified. Credits you buy never expire. ${y.upgrade||"Top up at https://www.golproductions.com/console.html"}`)}return void S(e,!1,y.reason||y.error||"check: request not authorized, command blocked")}"runnable"===y.verdict?S(e,!0):S(e,!1,(c.length?"denied by the shell that would run it. "+c.join("; "):null)||y.reason||"denied. Address the issue before continuing.")}catch{b(e,"check: validation service unreachable, command ran unverified. Check resumes automatically.")}}();
package/dist/mcp.js CHANGED
@@ -1,2 +1,2 @@
1
1
  #!/usr/bin/env node
2
- import{McpServer as t}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as e}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as r}from"zod";import{execFile as n,spawn as o}from"node:child_process";import{existsSync as s,readFileSync as i}from"node:fs";import{platform as c}from"node:os";import{promisify as a}from"node:util";import{fileURLToPath as l}from"node:url";import{join as u}from"node:path";const d=a(n),m=(()=>{try{return JSON.parse(i(u(l(new URL(".",import.meta.url)),"..","package.json"),"utf8")).version}catch{return"3.3.5"}})(),p=process.env.GOL_CLIENT_ID,f="win32"===process.platform;function h(t,e){return new Promise(r=>{try{const n=function(){if("win32"!==c())return"bash";const t=[process.env.CLAUDE_CODE_GIT_BASH_PATH,"C:/Program Files/Git/bin/bash.exe","C:/Program Files/Git/usr/bin/bash.exe","C:/Program Files (x86)/Git/bin/bash.exe"].filter(Boolean);for(const e of t)try{if(s(e))return e}catch{}return null}();if(!n)return r(null);const i=o(n,t,{windowsHide:!0});let a="";const l=setTimeout(()=>{try{i.kill()}catch{}r(null)},4e3);i.stderr.on("data",t=>{a+=t}),i.on("error",()=>{clearTimeout(l),r(null)}),i.on("close",t=>{clearTimeout(l),r({status:t,stderr:a})}),void 0!==e&&i.stdin.write(e),i.stdin.end()}catch{r(null)}})}async function y(t){const e=async t=>{const e=await fetch("https://triage.golproductions.com/preflight",{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":p,"User-Agent":"c/"+m},body:JSON.stringify(t)}),r=await e.json();return r._status=e.status,r},r={command:t,v:m,channel:"mcp",platform:"mcp",probe_ok:1},n=async function(t){try{const e=await h(["-n"],t);if(null===e)return null;if(0===e.status){const t=(e.stderr||"").split("\n").find(t=>/here-document.*end-of-file/i.test(t));return!!t&&t.replace(/^[^:]*bash[^:]*:\s*/i,"").trim()}return((e.stderr||"").split("\n").find(t=>t.trim())||"syntax error").replace(/^[^:]*bash[^:]*:\s*/i,"").trim()}catch{return null}}(t),o=e(r);o.catch(()=>{});const s=await n;if("string"==typeof s)return{verdict:"invalid",reason:`syntax error. ${s}`,_status:200};let i=await o;if(200===i._status&&"probe"===i.verdict&&Array.isArray(i.probe)){const t=i.probe.slice(0,8).filter(t=>"string"==typeof t&&t&&t.length<=200),n=await Promise.all(t.map(t=>async function(t){try{const e=await h(["-c",'type -- "$1"',"check",t]);if(null===e)return null;if(0===e.status)return{exists:!0};const r=(e.stderr||"").split("\n").find(e=>e.includes(t))||"";return{exists:!1,why:r.slice(r.indexOf(t)).trim()||t+": not found"}}catch{return null}}(t))),o={},s={};t.forEach((t,e)=>{const r=n[e];null!==r&&(o[t]=r.exists,!r.exists&&r.why&&(s[t]=r.why))}),i=await e({...r,probe:o,probe_why:s})}return i}p||(process.stderr.write("check: GOL_CLIENT_ID environment variable is required.\nGet your key at https://www.golproductions.com/check.html\n"),process.exit(1));const x=new t({name:"check",version:m});x.tool("Check","Know if a command will work before running it. Returns 'runnable' or 'invalid'. $0.0068 AUD per Check.",{command:r.string().max(1e4).describe("The shell command to validate before execution")},async({command:t})=>{try{const e=await y(t);return 402===e._status?{content:[{type:"text",text:"Verdict: RUNNABLE (unverified). Check: daily free checks used and balance is empty. Command NOT verified. Check resumes tomorrow or after a top-up at https://www.golproductions.com/console.html"}]}:e.verdict?"runnable"===e.verdict?{content:[{type:"text",text:"Verdict: RUNNABLE"}]}:{content:[{type:"text",text:"Verdict: INVALID"+(e.reason?". "+e.reason:"")}]}:{content:[{type:"text",text:`Error: ${e.error||"unknown"}`}],isError:!0}}catch(t){return{content:[{type:"text",text:`Check API error: ${t.message}`}],isError:!0}}}),x.tool("CheckAndExecute","Validate a command with Check, then execute it if runnable. Returns the command output if valid, or blocks it.",{command:r.string().max(1e4).describe("The shell command to validate and execute")},async({command:t})=>{try{const e=await y(t);if(402!==e._status&&"runnable"!==e.verdict)return{content:[{type:"text",text:"BLOCKED: "+(e.reason||"command is invalid")}]};const{stdout:r,stderr:n}=await d(f?"cmd":"bash",f?["/c",t]:["-c",t],{timeout:3e4});return{content:[{type:"text",text:(r||"")+(n?"\nSTDERR: "+n:"")||"(no output)"}]}}catch(t){return t.stdout||t.stderr?{content:[{type:"text",text:`Exit ${t.code||1}:\n${(t.stdout||"")+(t.stderr?"\nSTDERR: "+t.stderr:"")}`}]}:{content:[{type:"text",text:`Error: ${t.message}`}],isError:!0}}});const w=new e;await x.connect(w);
2
+ import{McpServer as t}from"@modelcontextprotocol/sdk/server/mcp.js";import{StdioServerTransport as e}from"@modelcontextprotocol/sdk/server/stdio.js";import{z as r}from"zod";import{execFile as n,spawnSync as o,spawn as s}from"node:child_process";import{existsSync as i}from"node:fs";import{platform as c}from"node:os";import{promisify as l}from"node:util";const a=l(n),u="3.3.7",d=process.env.GOL_CLIENT_ID,m="win32"===process.platform;d||(process.stderr.write("check: GOL_CLIENT_ID environment variable is required.\nGet your key at https://www.golproductions.com/check.html\n"),process.exit(1));const h=new Set(["sudo","nohup","nice","time","timeout","env"]),p=new Set(["cd","echo","printf","pwd","export","set","unset","alias","unalias","true","false","test","[","[[","exit","return","break","continue","shift","source",".","eval","exec","trap","wait","jobs","fg","bg","read","readonly","local","declare","typeset","let","getopts","hash","type","command","builtin","ulimit","umask","times","dirs","pushd","popd","for","while","until","if","then","else","elif","fi","do","done","case","esac","in","function","select","{","}","!"]);function f(){if("win32"!==c())return"bash";const t=[process.env.CLAUDE_CODE_GIT_BASH_PATH,"C:/Program Files/Git/bin/bash.exe","C:/Program Files/Git/usr/bin/bash.exe","C:/Program Files (x86)/Git/bin/bash.exe"].filter(Boolean);for(const e of t)try{if(i(e))return e}catch{}return null}function y(t){try{const e=f();if(!e)return null;const r=o(e,["-c",'type -- "$1" >/dev/null 2>&1',"check",t],{timeout:4e3,stdio:"ignore"});return r.error?null:0===r.status}catch{return null}}async function g(t){const e=function(t){try{let e=function(t){const e=[[]];let r="",n=null;for(let o=0;o<t.length;o++){const s=t[o];n?s===n?n=null:r+=s:'"'!==s&&"'"!==s?"|"!==s&&"&"!==s&&";"!==s&&"\n"!==s?/\s/.test(s)?r&&(e[e.length-1].push(r),r=""):r+=s:(r&&(e[e.length-1].push(r),r=""),e[e.length-1].length&&e.push([]),"|"!==s&&"&"!==s||t[o+1]!==s||o++):n=s}r&&e[e.length-1].push(r);const o=[];for(const t of e){let e=null;for(const r of t)if(r&&!h.has(r)&&!/^[\d.]+[smhd]?$/.test(r)&&(!r.includes("=")||r.startsWith("-"))&&!r.startsWith("-")){e=r;break}e&&o.push(e)}return o}(t);if(e=e.filter(t=>!p.has(t)),0===e.length)return!0;if(e.some(t=>/[(){}$`]/.test(t)))return null;let r=!0;for(const t of e){const e=y(t);if(null===e)return null;!1===e&&(r=!1)}return r}catch{return null}}(t),r={command:t,v:u,channel:"mcp",platform:"mcp",probe_ok:1,path_ok:1,git_ok:1};null!==e&&(r.binary_exists=e);const n=t=>fetch("https://triage.golproductions.com/preflight",{method:"POST",headers:{"Content-Type":"application/json","X-GOL-CLIENT-ID":d,"User-Agent":"c/"+u},body:JSON.stringify(t)});let o=await n(r),i=await o.json();if(o.ok&&"probe"===i.verdict&&Array.isArray(i.probe)){const t=await function(t){const e={probe:{},why:{}};if(!Array.isArray(t)||0===t.length)return Promise.resolve(e);const r=t.filter(t=>"string"==typeof t&&t.length>0&&t.length<600).slice(0,16);if(0===r.length)return Promise.resolve(e);let n;try{const t=f();if(!t)return Promise.resolve(e);n=s(t,["-c",'for k in "$@"; do\n r=0; w=""\n case "$k" in\n path:*) p="${k#path:}"; [ -e "$p" ] && r=1 ;;\n gitcmd:*) s="${k#gitcmd:}"\n if git --list-cmds=main,others,alias,parse-opt 2>/dev/null | grep -Fxq -- "$s"; then r=1\n elif type -- "git-$s" >/dev/null 2>&1; then r=1; fi ;;\n gitref:*) f="${k#gitref:}"\n if git rev-parse --verify --quiet "$f" >/dev/null 2>&1; then r=1\n elif git ls-files --error-unmatch -- "$f" >/dev/null 2>&1; then r=1; fi ;;\n *) if w=$(type -- "$k" 2>&1 >/dev/null); then r=1; w=""; fi ;;\n esac\n printf \'%s\\036%s\\036%s\\037\' "$k" "$r" "$w"\ndone',"check",...r])}catch{return Promise.resolve(e)}return new Promise(t=>{let r="";const o=()=>{try{for(const t of r.split("")){if(!t)continue;const[r,n,o]=t.split("");!r||"0"!==n&&"1"!==n||(e.probe[r]="1"===n,"0"===n&&o&&(e.why[r]=o.replace(/[\x00-\x1f\x7f]/g," ").trim().slice(0,200)))}}catch{}t(e)},s=setTimeout(()=>{try{n.kill()}catch{}o()},8e3);n.stdout.on("data",t=>{r+=t}),n.on("error",()=>{clearTimeout(s),t(e)}),n.on("close",()=>{clearTimeout(s),o()})})}(i.probe),e={...r,probe:t.probe,probe_why:t.why};delete e.probe_ok,o=await n(e),i=await o.json()}return i._status=o.status,i}const w=new t({name:"check",version:u});w.tool("Check","Know if a command will work before running it. Returns 'runnable' or 'invalid'. $0.0068 AUD per Check.",{command:r.string().max(1e4).describe("The shell command to validate before execution")},async({command:t})=>{try{const e=await g(t);return 402===e._status?{content:[{type:"text",text:"Verdict: RUNNABLE (unverified). Check: daily free checks used and balance is empty. Command NOT verified — Check resumes tomorrow or after a top-up at https://www.golproductions.com/console.html"}]}:e.verdict?{content:[{type:"text",text:"Verdict: "+("runnable"===e.verdict?"RUNNABLE":"INVALID")}]}:{content:[{type:"text",text:`Error: ${e.error||"unknown"}`}],isError:!0}}catch(t){return{content:[{type:"text",text:`Check API error: ${t.message}`}],isError:!0}}}),w.tool("CheckAndExecute","Validate a command with Check, then execute it if runnable. Returns the command output if valid, or blocks it.",{command:r.string().max(1e4).describe("The shell command to validate and execute")},async({command:t})=>{try{const e=await g(t);if(402!==e._status&&"runnable"!==e.verdict)return{content:[{type:"text",text:"BLOCKED: "+(e.reason||"command is invalid")}]};const{stdout:r,stderr:n}=await a(m?"cmd":"bash",m?["/c",t]:["-c",t],{timeout:3e4});return{content:[{type:"text",text:(r||"")+(n?"\nSTDERR: "+n:"")||"(no output)"}]}}catch(t){return t.stdout||t.stderr?{content:[{type:"text",text:`Exit ${t.code||1}:\n${(t.stdout||"")+(t.stderr?"\nSTDERR: "+t.stderr:"")}`}]}:{content:[{type:"text",text:`Error: ${t.message}`}],isError:!0}}});const x=new e;await w.connect(x);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golproductions/check",
3
- "version": "3.3.5",
3
+ "version": "3.3.7",
4
4
  "description": "Less hallucinations. A command firewall for AI agents: validates every shell command before execution, so fabricated commands are denied before they run.",
5
5
  "bin": {
6
6
  "check": "dist/index.js",
@@ -8,7 +8,7 @@
8
8
  },
9
9
  "scripts": {
10
10
  "build": "node build.mjs",
11
- "prepublishOnly": "node build.mjs && node test/gate.mjs"
11
+ "prepublishOnly": "node build.mjs"
12
12
  },
13
13
  "type": "module",
14
14
  "engines": {