@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.
- package/package.json +1 -1
- package/src/index.js +350 -4
package/package.json
CHANGED
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
|
+
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
|
-
|
|
243
|
-
|
|
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";
|