@golproductions/check 3.0.8 → 3.1.0

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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/src/index.js +350 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@golproductions/check",
3
- "version": "3.0.8",
3
+ "version": "3.1.0",
4
4
  "description": "The universal anti-hallucination engine for AI coding agents. Catches what is not real before it reaches your project.",
5
5
  "bin": {
6
6
  "check": "src/index.js",
package/src/index.js CHANGED
@@ -1,16 +1,24 @@
1
1
  #!/usr/bin/env node
2
- import { readFileSync, writeFileSync, mkdirSync, existsSync, createWriteStream, chmodSync } from "node:fs";
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync, createWriteStream, chmodSync, unlinkSync, rmSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { homedir, platform, arch } from "node:os";
5
5
  import { pipeline } from "node:stream/promises";
6
6
  import { Readable } from "node:stream";
7
7
 
8
- const VERSION = "3.0.8";
8
+ const VERSION = "3.1.0";
9
9
  const BINARY_VERSION = "3.0.0";
10
10
  const API = "https://triage.golproductions.com/preflight";
11
11
  const CDN = "https://pub-e55366a7f5994be9be04f0e205179f4a.r2.dev/releases";
12
12
  const CLIENT_ID = process.env.GOL_CLIENT_ID || "";
13
13
 
14
+ function getClientId() {
15
+ if (CLIENT_ID) return CLIENT_ID;
16
+ try {
17
+ const cfg = JSON.parse(readFileSync(join(homedir(), ".claude", "settings.json"), "utf8"));
18
+ return cfg.env?.GOL_CLIENT_ID || "";
19
+ } catch { return ""; }
20
+ }
21
+
14
22
  async function validateKey(key) {
15
23
  try {
16
24
  const res = await fetch(API, {
@@ -239,9 +247,347 @@ async function install() {
239
247
  process.exit(0);
240
248
  }
241
249
 
242
- if (process.argv.includes("--install")) { await install(); }
243
- if (process.argv.includes("--mcp")) { await import("./mcp.js"); process.exit(0); }
250
+ function isCheckRef(str) {
251
+ return str && (str.includes(".check") || str.includes("truth-gate"));
252
+ }
253
+
254
+ function isCheckHook(hook) {
255
+ if (isCheckRef(hook.command)) return true;
256
+ if (hook.args?.some(a => isCheckRef(a))) return true;
257
+ return false;
258
+ }
259
+
260
+ async function uninstall() {
261
+ const home = homedir();
262
+ let removed = 0;
263
+
264
+ console.log("\n Removing Check...\n");
265
+
266
+ const hookTargets = [
267
+ {
268
+ name: "Claude Code",
269
+ path: join(home, ".claude", "settings.json"),
270
+ clean: (cfg) => {
271
+ if (cfg.hooks?.PreToolUse) {
272
+ cfg.hooks.PreToolUse = cfg.hooks.PreToolUse.filter(h => !h.hooks?.some(isCheckHook));
273
+ if (cfg.hooks.PreToolUse.length === 0) delete cfg.hooks.PreToolUse;
274
+ }
275
+ if (cfg.hooks?.PostToolUse) {
276
+ cfg.hooks.PostToolUse = cfg.hooks.PostToolUse.filter(h => !h.hooks?.some(isCheckHook));
277
+ if (cfg.hooks.PostToolUse.length === 0) delete cfg.hooks.PostToolUse;
278
+ }
279
+ if (cfg.hooks && Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
280
+ if (cfg.env) {
281
+ delete cfg.env.GOL_CLIENT_ID;
282
+ if (Object.keys(cfg.env).length === 0) delete cfg.env;
283
+ }
284
+ return cfg;
285
+ }
286
+ },
287
+ {
288
+ name: "Gemini CLI",
289
+ path: join(home, ".gemini", "settings.json"),
290
+ clean: (cfg) => {
291
+ if (cfg.hooks?.BeforeTool) {
292
+ cfg.hooks.BeforeTool = cfg.hooks.BeforeTool.filter(h => !h.hooks?.some(hh => hh.name === "check-gate"));
293
+ if (cfg.hooks.BeforeTool.length === 0) delete cfg.hooks.BeforeTool;
294
+ if (Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
295
+ }
296
+ return cfg;
297
+ }
298
+ },
299
+ {
300
+ name: "Cursor",
301
+ path: join(home, ".cursor", "hooks.json"),
302
+ clean: (cfg) => {
303
+ if (cfg.hooks?.beforeShellExecution) {
304
+ cfg.hooks.beforeShellExecution = cfg.hooks.beforeShellExecution.filter(h => !isCheckRef(h.command));
305
+ if (cfg.hooks.beforeShellExecution.length === 0) delete cfg.hooks.beforeShellExecution;
306
+ if (Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
307
+ }
308
+ return cfg;
309
+ }
310
+ },
311
+ {
312
+ name: "Homebase",
313
+ path: join(home, ".homebase", "hooks.json"),
314
+ clean: (cfg) => {
315
+ if (cfg.hooks?.PreToolUse) {
316
+ cfg.hooks.PreToolUse = cfg.hooks.PreToolUse.filter(h => !h.hooks?.some(isCheckHook));
317
+ if (cfg.hooks.PreToolUse.length === 0) delete cfg.hooks.PreToolUse;
318
+ }
319
+ if (cfg.hooks?.PostToolUse) {
320
+ cfg.hooks.PostToolUse = cfg.hooks.PostToolUse.filter(h => !h.hooks?.some(isCheckHook));
321
+ if (cfg.hooks.PostToolUse.length === 0) delete cfg.hooks.PostToolUse;
322
+ }
323
+ if (cfg.hooks && Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
324
+ if (cfg.env) {
325
+ delete cfg.env.GOL_CLIENT_ID;
326
+ if (Object.keys(cfg.env).length === 0) delete cfg.env;
327
+ }
328
+ return cfg;
329
+ }
330
+ }
331
+ ];
332
+
333
+ const mcpUserTargets = [
334
+ { name: "Windsurf", path: join(home, ".codeium", "windsurf", "mcp_config.json") },
335
+ { name: "Amazon Q", path: join(home, ".aws", "amazonq", "mcp.json") },
336
+ ];
337
+
338
+ const mcpProjectTargets = [
339
+ { name: "Continue", path: join(process.cwd(), ".continue", "mcpServers", "check.json"), deleteFile: true },
340
+ { name: "Roo Code", path: join(process.cwd(), ".roo", "mcp.json") },
341
+ { name: "Project MCP", path: join(process.cwd(), ".mcp.json") },
342
+ ];
343
+
344
+ for (const t of hookTargets) {
345
+ if (!existsSync(t.path)) continue;
346
+ try {
347
+ const cfg = JSON.parse(readFileSync(t.path, "utf8"));
348
+ const cleaned = t.clean(cfg);
349
+ writeFileSync(t.path, JSON.stringify(cleaned, null, 2) + "\n", "utf8");
350
+ console.log(" removed " + t.name);
351
+ removed++;
352
+ } catch {}
353
+ }
354
+
355
+ for (const t of [...mcpUserTargets, ...mcpProjectTargets]) {
356
+ if (!existsSync(t.path)) continue;
357
+ try {
358
+ if (t.deleteFile) {
359
+ unlinkSync(t.path);
360
+ console.log(" removed " + t.name);
361
+ removed++;
362
+ } else {
363
+ const cfg = JSON.parse(readFileSync(t.path, "utf8"));
364
+ if (cfg.mcpServers?.Check) {
365
+ delete cfg.mcpServers.Check;
366
+ if (Object.keys(cfg.mcpServers).length === 0) delete cfg.mcpServers;
367
+ writeFileSync(t.path, JSON.stringify(cfg, null, 2) + "\n", "utf8");
368
+ console.log(" removed " + t.name);
369
+ removed++;
370
+ }
371
+ }
372
+ } catch {}
373
+ }
374
+
375
+ const checkDir = join(home, ".check");
376
+ if (existsSync(checkDir)) {
377
+ try {
378
+ rmSync(checkDir, { recursive: true, force: true });
379
+ console.log(" removed ~/.check/");
380
+ } catch {}
381
+ }
382
+
383
+ if (removed === 0) {
384
+ console.log(" No Check installations found.\n");
385
+ } else {
386
+ console.log(`\n Removed from ${removed} tool${removed !== 1 ? "s" : ""}.`);
387
+ console.log(" CLAUDE.md / AGENTS.md rules were left in place.\n");
388
+ }
389
+
390
+ process.exit(0);
391
+ }
392
+
393
+ async function status() {
394
+ const home = homedir();
395
+ const checkDir = join(home, ".check");
396
+ const ext = platform() === "win32" ? ".exe" : "";
397
+ const id = getClientId();
398
+
399
+ console.log(`\n Check v${VERSION}\n`);
400
+
401
+ if (id) {
402
+ const masked = id.length > 12 ? id.slice(0, 8) + "..." + id.slice(-4) : id;
403
+ console.log(" Client ID: " + masked);
404
+ } else {
405
+ console.log(" Client ID: not set");
406
+ }
407
+
408
+ const binaryPath = join(checkDir, "truth-gate" + ext);
409
+ console.log(" Binary: " + (existsSync(binaryPath) ? "installed" : "not found"));
410
+ const scriptPath = join(checkDir, "check.mjs");
411
+ console.log(" Script: " + (existsSync(scriptPath) ? "installed" : "not found"));
412
+
413
+ console.log("\n Integrations:\n");
414
+
415
+ const checks = [
416
+ {
417
+ name: "Claude Code",
418
+ path: join(home, ".claude", "settings.json"),
419
+ test: (cfg) => cfg.hooks?.PreToolUse?.some(h => h.hooks?.some(isCheckHook)) || cfg.hooks?.PostToolUse?.some(h => h.hooks?.some(isCheckHook)),
420
+ detail: (cfg) => {
421
+ const parts = [];
422
+ if (cfg.hooks?.PreToolUse?.some(h => h.hooks?.some(isCheckHook))) parts.push("PreToolUse");
423
+ if (cfg.hooks?.PostToolUse?.some(h => h.hooks?.some(isCheckHook))) parts.push("PostToolUse");
424
+ return parts.join(", ");
425
+ }
426
+ },
427
+ {
428
+ name: "Gemini CLI",
429
+ path: join(home, ".gemini", "settings.json"),
430
+ test: (cfg) => cfg.hooks?.BeforeTool?.some(h => h.hooks?.some(hh => hh.name === "check-gate")),
431
+ detail: () => "BeforeTool"
432
+ },
433
+ {
434
+ name: "Cursor",
435
+ path: join(home, ".cursor", "hooks.json"),
436
+ test: (cfg) => cfg.hooks?.beforeShellExecution?.some(h => isCheckRef(h.command)),
437
+ detail: () => "beforeShellExecution"
438
+ },
439
+ {
440
+ name: "Homebase",
441
+ path: join(home, ".homebase", "hooks.json"),
442
+ test: (cfg) => cfg.hooks?.PreToolUse?.some(h => h.hooks?.some(isCheckHook)) || cfg.hooks?.PostToolUse?.some(h => h.hooks?.some(isCheckHook)),
443
+ detail: (cfg) => {
444
+ const parts = [];
445
+ if (cfg.hooks?.PreToolUse?.some(h => h.hooks?.some(isCheckHook))) parts.push("PreToolUse");
446
+ if (cfg.hooks?.PostToolUse?.some(h => h.hooks?.some(isCheckHook))) parts.push("PostToolUse");
447
+ return parts.join(", ");
448
+ }
449
+ },
450
+ { name: "Windsurf", path: join(home, ".codeium", "windsurf", "mcp_config.json"), test: (cfg) => !!cfg.mcpServers?.Check, detail: () => "MCP" },
451
+ { name: "Continue", path: join(process.cwd(), ".continue", "mcpServers", "check.json"), test: (cfg) => !!cfg.mcpServers?.Check, detail: () => "MCP (project)" },
452
+ { name: "Amazon Q", path: join(home, ".aws", "amazonq", "mcp.json"), test: (cfg) => !!cfg.mcpServers?.Check, detail: () => "MCP" },
453
+ { name: "Roo Code", path: join(process.cwd(), ".roo", "mcp.json"), test: (cfg) => !!cfg.mcpServers?.Check, detail: () => "MCP (project)" },
454
+ { name: "Project MCP", path: join(process.cwd(), ".mcp.json"), test: (cfg) => !!cfg.mcpServers?.Check, detail: () => "MCP (project)" },
455
+ ];
456
+
457
+ for (const c of checks) {
458
+ if (!existsSync(c.path)) {
459
+ console.log(" - " + c.name.padEnd(14) + " not detected");
460
+ continue;
461
+ }
462
+ try {
463
+ const cfg = JSON.parse(readFileSync(c.path, "utf8"));
464
+ if (c.test(cfg)) {
465
+ console.log(" + " + c.name.padEnd(14) + " active (" + c.detail(cfg) + ")");
466
+ } else {
467
+ console.log(" - " + c.name.padEnd(14) + " tool installed, Check not configured");
468
+ }
469
+ } catch {
470
+ console.log(" ? " + c.name.padEnd(14) + " config unreadable");
471
+ }
472
+ }
473
+
474
+ console.log("");
475
+ process.exit(0);
476
+ }
477
+
478
+ async function credits() {
479
+ const id = getClientId();
480
+ if (!id) {
481
+ console.log("\n No Client ID found. Set GOL_CLIENT_ID or run --install first.\n");
482
+ process.exit(1);
483
+ }
484
+
485
+ try {
486
+ const res = await fetch(API.replace("/preflight", "/credits"), {
487
+ method: "GET",
488
+ headers: { "X-GOL-CLIENT-ID": id, "User-Agent": "c/" + VERSION },
489
+ signal: AbortSignal.timeout(10000),
490
+ });
491
+ if (!res.ok) {
492
+ console.log("\n Could not fetch credits (HTTP " + res.status + ").\n");
493
+ process.exit(1);
494
+ }
495
+ const d = await res.json();
496
+ console.log(`\n Check v${VERSION} — Credits\n`);
497
+ if (d.free_remaining !== undefined) console.log(" Free today: " + d.free_remaining + " / " + (d.free_daily || 120));
498
+ if (d.calls_today !== undefined) console.log(" Calls today: " + d.calls_today);
499
+ if (d.balance !== undefined) console.log(" Balance: $" + d.balance + " AUD");
500
+ if (d.total_calls !== undefined) console.log(" Total calls: " + d.total_calls);
501
+ if (d.plan) console.log(" Plan: " + d.plan);
502
+ if (Object.keys(d).length === 0) console.log(" " + JSON.stringify(d));
503
+ console.log("");
504
+ } catch (err) {
505
+ console.log("\n Could not reach API: " + err.message + "\n");
506
+ }
507
+ process.exit(0);
508
+ }
509
+
510
+ async function docs() {
511
+ console.log(`
512
+ Check v${VERSION} — Documentation
513
+
514
+ Install: npx @golproductions/check --install <key>
515
+ Uninstall: npx @golproductions/check --uninstall
516
+ Status: npx @golproductions/check --status
517
+ Credits: npx @golproductions/check --credits
518
+
519
+ How it works:
520
+
521
+ Check installs hooks into your AI coding tools. Every time
522
+ the AI runs a command or edits a file, Check verifies it
523
+ before or after execution.
524
+
525
+ PreToolUse Validates commands before they run.
526
+ PostToolUse Verifies results after execution.
527
+ MCP Server Provides Check and CheckAndExecute tools.
528
+ CLAUDE.md Adds anti-fabrication instructions.
529
+
530
+ Supported tools:
531
+
532
+ Claude Code, Gemini CLI, Cursor, Windsurf, Continue,
533
+ Amazon Q Developer, Roo Code, Homebase
534
+
535
+ Pricing:
536
+
537
+ 120 free checks per day. Then $0.0068 AUD per check.
538
+ Credits never expire. Pay as you go.
539
+
540
+ Links:
541
+
542
+ Website: https://golproductions.com/check
543
+ Docs: https://github.com/golproductions/check
544
+ Support: support@golproductions.com
545
+ `);
546
+
547
+ try {
548
+ const { execSync } = await import("node:child_process");
549
+ const url = "https://github.com/golproductions/check";
550
+ if (platform() === "win32") execSync(`start "" "${url}"`, { stdio: "ignore", shell: true });
551
+ else if (platform() === "darwin") execSync(`open "${url}"`, { stdio: "ignore" });
552
+ else execSync(`xdg-open "${url}"`, { stdio: "ignore" });
553
+ } catch {}
554
+
555
+ process.exit(0);
556
+ }
557
+
558
+ function help() {
559
+ console.log(`
560
+ Check v${VERSION} — Anti-hallucination layer for AI coding agents
561
+
562
+ Usage:
563
+
564
+ npx @golproductions/check --install <key> Install Check with your Client ID
565
+ npx @golproductions/check --uninstall Remove Check from all tools
566
+ npx @golproductions/check --status Show what is installed and active
567
+ npx @golproductions/check --credits Check your usage and balance
568
+ npx @golproductions/check --docs View documentation
569
+ npx @golproductions/check --help Show this help
570
+
571
+ Get your key: https://golproductions.com/check
572
+ Pricing: 120 free/day, then $0.0068 AUD per check
573
+ `);
574
+ process.exit(0);
575
+ }
576
+
577
+ // CLI routing
578
+ const args = process.argv.slice(2);
579
+ if (args.includes("--install")) { await install(); }
580
+ if (args.includes("--uninstall")) { await uninstall(); }
581
+ if (args.includes("--status")) { await status(); }
582
+ if (args.includes("--credits")) { await credits(); }
583
+ if (args.includes("--docs")) { await docs(); }
584
+ if (args.includes("--help") || args.includes("-h")) { help(); }
585
+ if (args.includes("--mcp")) { await import("./mcp.js"); process.exit(0); }
586
+
587
+ // No flags + interactive terminal = show help
588
+ if (!args.some(a => a.startsWith("-")) && process.stdin.isTTY) { help(); }
244
589
 
590
+ // Hook handler (invoked by tools via stdin)
245
591
  function detect(p) {
246
592
  if (typeof p.command === "string" && !p.tool_input) return "cursor";
247
593
  if (p.hook_event_name === "BeforeTool" || p.toolCall?.argumentsJson) return "gemini";