@node9/proxy 1.28.0 → 1.29.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/dist/cli.js CHANGED
@@ -7190,6 +7190,12 @@ async function setupClaude() {
7190
7190
  }
7191
7191
  }
7192
7192
  async function setupGemini() {
7193
+ console.log(
7194
+ import_chalk.default.yellow(
7195
+ " \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18\n (replaced by Antigravity). If you use agy, run: node9 agents add antigravity"
7196
+ )
7197
+ );
7198
+ console.log("");
7193
7199
  seedMcpPinsIfMissing();
7194
7200
  const homeDir2 = import_os12.default.homedir();
7195
7201
  const settingsPath = import_path15.default.join(homeDir2, ".gemini", "settings.json");
@@ -7287,6 +7293,355 @@ async function setupGemini() {
7287
7293
  printDaemonTip();
7288
7294
  }
7289
7295
  }
7296
+ async function setupAntigravity() {
7297
+ seedMcpPinsIfMissing();
7298
+ const homeDir2 = import_os12.default.homedir();
7299
+ const hooksPath = import_path15.default.join(homeDir2, ".gemini", "config", "hooks.json");
7300
+ const mcpPath = import_path15.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
7301
+ const hooksFile = readJson(hooksPath) ?? {};
7302
+ const mcpConfig = readJson(mcpPath) ?? {};
7303
+ const servers = mcpConfig.mcpServers ?? {};
7304
+ let hooksChanged = false;
7305
+ let anythingChanged = false;
7306
+ if (!hooksFile.hooks) hooksFile.hooks = {};
7307
+ const hasPreHook = hooksFile.hooks.PreToolUse?.some(
7308
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7309
+ );
7310
+ if (!hasPreHook) {
7311
+ if (!Array.isArray(hooksFile.hooks.PreToolUse)) hooksFile.hooks.PreToolUse = [];
7312
+ hooksFile.hooks.PreToolUse.push({
7313
+ matcher: ".*",
7314
+ hooks: [
7315
+ {
7316
+ name: "node9-check",
7317
+ type: "command",
7318
+ command: fullPathCommand("check --agent antigravity"),
7319
+ timeout: 600
7320
+ }
7321
+ ]
7322
+ });
7323
+ console.log(import_chalk.default.green(" \u2705 PreToolUse hook added \u2192 node9 check"));
7324
+ hooksChanged = true;
7325
+ anythingChanged = true;
7326
+ } else if (hooksFile.hooks.PreToolUse) {
7327
+ for (const matcher of hooksFile.hooks.PreToolUse) {
7328
+ for (const h of matcher.hooks) {
7329
+ const cmd = h.command ?? "";
7330
+ if (isNode9Hook(cmd) && needsRewrite(cmd)) {
7331
+ h.command = fullPathCommand("check --agent antigravity");
7332
+ console.log(import_chalk.default.yellow(" \u{1F527} PreToolUse hook repaired (stale path \u2192 current binary)"));
7333
+ hooksChanged = true;
7334
+ anythingChanged = true;
7335
+ }
7336
+ }
7337
+ }
7338
+ }
7339
+ const hasPostHook = hooksFile.hooks.PostToolUse?.some(
7340
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7341
+ );
7342
+ if (!hasPostHook) {
7343
+ if (!Array.isArray(hooksFile.hooks.PostToolUse)) hooksFile.hooks.PostToolUse = [];
7344
+ hooksFile.hooks.PostToolUse.push({
7345
+ matcher: ".*",
7346
+ hooks: [
7347
+ {
7348
+ name: "node9-log",
7349
+ type: "command",
7350
+ command: fullPathCommand("log --agent antigravity"),
7351
+ timeout: 600
7352
+ }
7353
+ ]
7354
+ });
7355
+ console.log(import_chalk.default.green(" \u2705 PostToolUse hook added \u2192 node9 log"));
7356
+ hooksChanged = true;
7357
+ anythingChanged = true;
7358
+ } else if (hooksFile.hooks.PostToolUse) {
7359
+ for (const matcher of hooksFile.hooks.PostToolUse) {
7360
+ for (const h of matcher.hooks) {
7361
+ const cmd = h.command ?? "";
7362
+ if (isNode9Hook(cmd) && needsRewrite(cmd)) {
7363
+ h.command = fullPathCommand("log --agent antigravity");
7364
+ console.log(import_chalk.default.yellow(" \u{1F527} PostToolUse hook repaired (stale path \u2192 current binary)"));
7365
+ hooksChanged = true;
7366
+ anythingChanged = true;
7367
+ }
7368
+ }
7369
+ }
7370
+ }
7371
+ if (hooksChanged) {
7372
+ writeJson(hooksPath, hooksFile);
7373
+ console.log("");
7374
+ }
7375
+ if (!hasNode9McpServer(servers)) {
7376
+ servers["node9"] = NODE9_MCP_SERVER_ENTRY;
7377
+ mcpConfig.mcpServers = servers;
7378
+ writeJson(mcpPath, mcpConfig);
7379
+ console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
7380
+ anythingChanged = true;
7381
+ }
7382
+ const legacySettings = readJson(import_path15.default.join(homeDir2, ".gemini", "settings.json"));
7383
+ const legacyHasNode9 = ["BeforeTool", "AfterTool"].some(
7384
+ (ev) => legacySettings?.hooks?.[ev]?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)))
7385
+ );
7386
+ if (legacyHasNode9) {
7387
+ console.log(
7388
+ import_chalk.default.yellow(
7389
+ " \u26A0\uFE0F Found node9 hooks for the legacy Gemini CLI in ~/.gemini/settings.json.\n Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n Keep them only if you still use Gemini CLI (e.g. enterprise Code Assist)."
7390
+ )
7391
+ );
7392
+ const clean = await (0, import_prompts.confirm)({
7393
+ message: "Remove the legacy Gemini CLI hooks?",
7394
+ default: false
7395
+ });
7396
+ if (clean) {
7397
+ teardownGemini();
7398
+ anythingChanged = true;
7399
+ }
7400
+ console.log("");
7401
+ }
7402
+ const serversToWrap = [];
7403
+ for (const [name, server] of Object.entries(servers)) {
7404
+ if (!server.command || server.command === "node9") continue;
7405
+ serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
7406
+ }
7407
+ if (serversToWrap.length > 0) {
7408
+ console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
7409
+ console.log(import_chalk.default.white(` ${mcpPath}`));
7410
+ for (const { name, upstream } of serversToWrap) {
7411
+ console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
7412
+ }
7413
+ console.log("");
7414
+ const proceed = await (0, import_prompts.confirm)({ message: "Wrap these MCP servers?", default: true });
7415
+ if (proceed) {
7416
+ for (const { name, upstream } of serversToWrap) {
7417
+ servers[name] = {
7418
+ ...servers[name],
7419
+ command: "node9",
7420
+ args: ["mcp", "--upstream", upstream]
7421
+ };
7422
+ }
7423
+ mcpConfig.mcpServers = servers;
7424
+ writeJson(mcpPath, mcpConfig);
7425
+ console.log(import_chalk.default.green(`
7426
+ \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
7427
+ anythingChanged = true;
7428
+ } else {
7429
+ console.log(import_chalk.default.yellow(" Skipped MCP server wrapping."));
7430
+ }
7431
+ console.log("");
7432
+ }
7433
+ if (!anythingChanged && serversToWrap.length === 0) {
7434
+ console.log(import_chalk.default.blue("\u2139\uFE0F Node9 is already fully configured for Antigravity."));
7435
+ printDaemonTip();
7436
+ return;
7437
+ }
7438
+ if (anythingChanged) {
7439
+ console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Antigravity!"));
7440
+ console.log(
7441
+ import_chalk.default.gray(
7442
+ " Covers both the agy CLI and the Antigravity IDE (shared ~/.gemini/config).\n Restart agy / the IDE for changes to take effect."
7443
+ )
7444
+ );
7445
+ printDaemonTip();
7446
+ }
7447
+ }
7448
+ function teardownAntigravity() {
7449
+ const homeDir2 = import_os12.default.homedir();
7450
+ const hooksPath = import_path15.default.join(homeDir2, ".gemini", "config", "hooks.json");
7451
+ const mcpPath = import_path15.default.join(homeDir2, ".gemini", "config", "mcp_config.json");
7452
+ let changed = false;
7453
+ const hooksFile = readJson(hooksPath);
7454
+ if (hooksFile?.hooks) {
7455
+ for (const event of ["PreToolUse", "PostToolUse"]) {
7456
+ const before = hooksFile.hooks[event]?.length ?? 0;
7457
+ hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
7458
+ (m) => !m.hooks.some((h) => isNode9Hook(h.command))
7459
+ );
7460
+ if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
7461
+ if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
7462
+ }
7463
+ if (changed) {
7464
+ writeJson(hooksPath, hooksFile);
7465
+ console.log(
7466
+ import_chalk.default.green(" \u2705 Removed PreToolUse / PostToolUse hooks from ~/.gemini/config/hooks.json")
7467
+ );
7468
+ } else {
7469
+ console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9 hooks found in ~/.gemini/config/hooks.json"));
7470
+ }
7471
+ } else {
7472
+ console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.gemini/config/hooks.json not found \u2014 nothing to remove"));
7473
+ }
7474
+ const mcpConfig = readJson(mcpPath);
7475
+ if (mcpConfig?.mcpServers) {
7476
+ let mcpChanged = false;
7477
+ if (removeNode9McpServer(mcpConfig.mcpServers)) {
7478
+ mcpChanged = true;
7479
+ console.log(
7480
+ import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.gemini/config/mcp_config.json")
7481
+ );
7482
+ }
7483
+ for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
7484
+ const args = server.args;
7485
+ if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
7486
+ const [originalCmd, ...originalArgs] = args[2].split(" ");
7487
+ mcpConfig.mcpServers[name] = {
7488
+ ...server,
7489
+ command: originalCmd,
7490
+ args: originalArgs.length ? originalArgs : void 0
7491
+ };
7492
+ mcpChanged = true;
7493
+ }
7494
+ }
7495
+ if (mcpChanged) {
7496
+ writeJson(mcpPath, mcpConfig);
7497
+ console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in ~/.gemini/config/mcp_config.json"));
7498
+ }
7499
+ }
7500
+ }
7501
+ async function setupCopilot() {
7502
+ seedMcpPinsIfMissing();
7503
+ const homeDir2 = import_os12.default.homedir();
7504
+ const hooksPath = import_path15.default.join(homeDir2, ".copilot", "hooks", "node9.json");
7505
+ const mcpPath = import_path15.default.join(homeDir2, ".copilot", "mcp-config.json");
7506
+ const hooksFile = readJson(hooksPath) ?? { version: 1 };
7507
+ if (!hooksFile.version) hooksFile.version = 1;
7508
+ if (!hooksFile.hooks) hooksFile.hooks = {};
7509
+ const mcpConfig = readJson(mcpPath) ?? {};
7510
+ const servers = mcpConfig.mcpServers ?? {};
7511
+ let hooksChanged = false;
7512
+ let anythingChanged = false;
7513
+ const addHook = (event, subcommand) => {
7514
+ const arr = Array.isArray(hooksFile.hooks[event]) ? hooksFile.hooks[event] : [];
7515
+ const present = arr.some((h) => isNode9Hook(h.command));
7516
+ if (!present) {
7517
+ arr.push({ type: "command", command: fullPathCommand(subcommand), timeoutSec: 600 });
7518
+ hooksFile.hooks[event] = arr;
7519
+ console.log(import_chalk.default.green(` \u2705 ${event} hook added \u2192 node9 ${subcommand.split(" ")[0]}`));
7520
+ hooksChanged = true;
7521
+ anythingChanged = true;
7522
+ } else {
7523
+ for (const h of arr) {
7524
+ if (h.command && isNode9Hook(h.command) && needsRewrite(h.command)) {
7525
+ h.command = fullPathCommand(subcommand);
7526
+ console.log(import_chalk.default.yellow(` \u{1F527} ${event} hook repaired (stale path \u2192 current binary)`));
7527
+ hooksChanged = true;
7528
+ anythingChanged = true;
7529
+ }
7530
+ }
7531
+ }
7532
+ };
7533
+ addHook("PreToolUse", "check --agent copilot");
7534
+ addHook("PostToolUse", "log --agent copilot");
7535
+ addHook("UserPromptSubmit", "check --agent copilot");
7536
+ if (hooksChanged) {
7537
+ writeJson(hooksPath, hooksFile);
7538
+ console.log("");
7539
+ }
7540
+ if (!hasNode9McpServer(servers)) {
7541
+ servers["node9"] = NODE9_MCP_SERVER_ENTRY;
7542
+ mcpConfig.mcpServers = servers;
7543
+ writeJson(mcpPath, mcpConfig);
7544
+ console.log(import_chalk.default.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
7545
+ anythingChanged = true;
7546
+ }
7547
+ const serversToWrap = [];
7548
+ for (const [name, server] of Object.entries(servers)) {
7549
+ if (!server.command || server.command === "node9") continue;
7550
+ serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
7551
+ }
7552
+ if (serversToWrap.length > 0) {
7553
+ console.log(import_chalk.default.bold("The following existing entries will be modified:\n"));
7554
+ console.log(import_chalk.default.white(` ${mcpPath}`));
7555
+ for (const { name, upstream } of serversToWrap) {
7556
+ console.log(import_chalk.default.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
7557
+ }
7558
+ console.log("");
7559
+ const proceed = await (0, import_prompts.confirm)({ message: "Wrap these MCP servers?", default: true });
7560
+ if (proceed) {
7561
+ for (const { name, upstream } of serversToWrap) {
7562
+ servers[name] = {
7563
+ ...servers[name],
7564
+ command: "node9",
7565
+ args: ["mcp", "--upstream", upstream]
7566
+ };
7567
+ }
7568
+ mcpConfig.mcpServers = servers;
7569
+ writeJson(mcpPath, mcpConfig);
7570
+ console.log(import_chalk.default.green(`
7571
+ \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
7572
+ anythingChanged = true;
7573
+ } else {
7574
+ console.log(import_chalk.default.yellow(" Skipped MCP server wrapping."));
7575
+ }
7576
+ console.log("");
7577
+ }
7578
+ if (!anythingChanged) {
7579
+ console.log(import_chalk.default.blue("\u2139\uFE0F Node9 is already fully configured for GitHub Copilot CLI."));
7580
+ printDaemonTip();
7581
+ return;
7582
+ }
7583
+ console.log(import_chalk.default.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting GitHub Copilot CLI!"));
7584
+ console.log(import_chalk.default.gray(" Restart Copilot CLI for changes to take effect."));
7585
+ printDaemonTip();
7586
+ }
7587
+ function teardownCopilot() {
7588
+ const homeDir2 = import_os12.default.homedir();
7589
+ const hooksPath = import_path15.default.join(homeDir2, ".copilot", "hooks", "node9.json");
7590
+ const mcpPath = import_path15.default.join(homeDir2, ".copilot", "mcp-config.json");
7591
+ const hooksFile = readJson(hooksPath);
7592
+ let changed = false;
7593
+ if (hooksFile?.hooks) {
7594
+ for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
7595
+ const before = hooksFile.hooks[event]?.length ?? 0;
7596
+ hooksFile.hooks[event] = hooksFile.hooks[event]?.filter((h) => !isNode9Hook(h.command));
7597
+ if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
7598
+ if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
7599
+ }
7600
+ if (changed) {
7601
+ if (Object.keys(hooksFile.hooks).length === 0) {
7602
+ try {
7603
+ import_fs13.default.unlinkSync(hooksPath);
7604
+ console.log(import_chalk.default.green(" \u2705 Removed ~/.copilot/hooks/node9.json"));
7605
+ } catch {
7606
+ writeJson(hooksPath, hooksFile);
7607
+ }
7608
+ } else {
7609
+ writeJson(hooksPath, hooksFile);
7610
+ console.log(import_chalk.default.green(" \u2705 Removed Node9 hooks from ~/.copilot/hooks/node9.json"));
7611
+ }
7612
+ } else {
7613
+ console.log(import_chalk.default.blue(" \u2139\uFE0F No Node9 hooks found in ~/.copilot/hooks/node9.json"));
7614
+ }
7615
+ } else {
7616
+ console.log(import_chalk.default.blue(" \u2139\uFE0F ~/.copilot/hooks/node9.json not found \u2014 nothing to remove"));
7617
+ }
7618
+ const mcpConfig = readJson(mcpPath);
7619
+ if (mcpConfig?.mcpServers) {
7620
+ let mcpChanged = false;
7621
+ if (removeNode9McpServer(mcpConfig.mcpServers)) {
7622
+ mcpChanged = true;
7623
+ console.log(
7624
+ import_chalk.default.green(" \u2705 Removed node9 MCP server entry from ~/.copilot/mcp-config.json")
7625
+ );
7626
+ }
7627
+ for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
7628
+ const args = server.args;
7629
+ if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
7630
+ const [originalCmd, ...originalArgs] = args[2].split(" ");
7631
+ mcpConfig.mcpServers[name] = {
7632
+ ...server,
7633
+ command: originalCmd,
7634
+ args: originalArgs.length ? originalArgs : void 0
7635
+ };
7636
+ mcpChanged = true;
7637
+ }
7638
+ }
7639
+ if (mcpChanged) {
7640
+ writeJson(mcpPath, mcpConfig);
7641
+ console.log(import_chalk.default.green(" \u2705 Unwrapped MCP servers in ~/.copilot/mcp-config.json"));
7642
+ }
7643
+ }
7644
+ }
7290
7645
  function claudeDesktopConfigPath(homeDir2 = import_os12.default.homedir()) {
7291
7646
  if (process.platform === "darwin") {
7292
7647
  return import_path15.default.join(
@@ -7334,7 +7689,23 @@ function detectAgents(homeDir2 = import_os12.default.homedir()) {
7334
7689
  const desktopPath = claudeDesktopConfigPath(homeDir2);
7335
7690
  return {
7336
7691
  claude: exists(import_path15.default.join(homeDir2, ".claude")) || exists(import_path15.default.join(homeDir2, ".claude.json")),
7337
- gemini: exists(import_path15.default.join(homeDir2, ".gemini")),
7692
+ // Antigravity (agy) shares the ~/.gemini root, so a bare
7693
+ // `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
7694
+ // installed on every agy machine — and `node9 init` would then
7695
+ // write BeforeTool hooks into settings.json, a file agy ignores,
7696
+ // while reporting the machine protected (silent protection gap,
7697
+ // verified live on a machine with both installed). Gemini CLI
7698
+ // creates ~/.gemini/settings.json on first run; agy does not touch
7699
+ // it (it uses antigravity-cli/settings.json) — that file is the
7700
+ // legacy-CLI discriminator.
7701
+ gemini: exists(import_path15.default.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini"),
7702
+ // agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
7703
+ // creates antigravity-ide/. PATH fallback covers installed-but-
7704
+ // never-launched (same class as opencode #186).
7705
+ antigravity: exists(import_path15.default.join(homeDir2, ".gemini", "antigravity-cli")) || exists(import_path15.default.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy"),
7706
+ // GitHub Copilot CLI creates ~/.copilot on first launch; PATH
7707
+ // fallback covers installed-but-never-launched (same as opencode #186).
7708
+ copilot: exists(import_path15.default.join(homeDir2, ".copilot")) || binaryInPath("copilot"),
7338
7709
  cursor: exists(import_path15.default.join(homeDir2, ".cursor")),
7339
7710
  codex: exists(import_path15.default.join(homeDir2, ".codex")),
7340
7711
  windsurf: exists(import_path15.default.join(homeDir2, ".codeium", "windsurf")),
@@ -8335,6 +8706,18 @@ function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
8335
8706
  const settings = readJson(import_path15.default.join(homeDir2, ".gemini", "settings.json"));
8336
8707
  return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
8337
8708
  })();
8709
+ const antigravityWired = (() => {
8710
+ const hooksFile = readJson(
8711
+ import_path15.default.join(homeDir2, ".gemini", "config", "hooks.json")
8712
+ );
8713
+ return !!hooksFile?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
8714
+ })();
8715
+ const copilotWired = (() => {
8716
+ const hooksFile = readJson(
8717
+ import_path15.default.join(homeDir2, ".copilot", "hooks", "node9.json")
8718
+ );
8719
+ return !!hooksFile?.hooks?.PreToolUse?.some((h) => isNode9Hook(h.command));
8720
+ })();
8338
8721
  const cursorWired = (() => {
8339
8722
  const cfg = readJson(import_path15.default.join(homeDir2, ".cursor", "mcp.json"));
8340
8723
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
@@ -8368,6 +8751,20 @@ function getAgentsStatus(homeDir2 = import_os12.default.homedir()) {
8368
8751
  wired: geminiWired,
8369
8752
  mode: detected.gemini ? "hooks" : null
8370
8753
  },
8754
+ {
8755
+ name: "antigravity",
8756
+ label: "Antigravity",
8757
+ installed: detected.antigravity,
8758
+ wired: antigravityWired,
8759
+ mode: detected.antigravity ? "hooks" : null
8760
+ },
8761
+ {
8762
+ name: "copilot",
8763
+ label: "GitHub Copilot",
8764
+ installed: detected.copilot,
8765
+ wired: copilotWired,
8766
+ mode: detected.copilot ? "hooks" : null
8767
+ },
8371
8768
  {
8372
8769
  name: "cursor",
8373
8770
  label: "Cursor",
@@ -8485,7 +8882,84 @@ var init_setup = __esm({
8485
8882
  }
8486
8883
  });
8487
8884
 
8885
+ // src/utils/hook-payload.ts
8886
+ function extractToolName(payload, defaultValue = "") {
8887
+ return payload.tool_name ?? payload.name ?? payload.toolCall?.name ?? defaultValue;
8888
+ }
8889
+ function extractToolInput(payload) {
8890
+ return payload.tool_input ?? payload.args ?? payload.toolCall?.args ?? {};
8891
+ }
8892
+ function canonicalToolName(name) {
8893
+ switch (name) {
8894
+ // Hermes Agent
8895
+ case "terminal":
8896
+ return "Bash";
8897
+ case "write_file":
8898
+ return "Write";
8899
+ case "patch":
8900
+ return "Edit";
8901
+ case "read_file":
8902
+ return "Read";
8903
+ case "search_files":
8904
+ return "Grep";
8905
+ // Antigravity (agy) — shell tool renamed from Gemini's run_shell_command
8906
+ case "run_command":
8907
+ return "Bash";
8908
+ default:
8909
+ return name;
8910
+ }
8911
+ }
8912
+ function agentLabelFromFlag(flag) {
8913
+ if (typeof flag !== "string") return void 0;
8914
+ switch (flag.toLowerCase()) {
8915
+ case "antigravity":
8916
+ case "agy":
8917
+ return "Antigravity";
8918
+ case "copilot":
8919
+ return "GitHub Copilot";
8920
+ default:
8921
+ return void 0;
8922
+ }
8923
+ }
8924
+ function canonicalToolInput(rawToolName, input) {
8925
+ if (rawToolName !== "run_command") return input;
8926
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
8927
+ const args = input;
8928
+ if (typeof args.CommandLine !== "string") return input;
8929
+ const { CommandLine, Cwd, ...rest } = args;
8930
+ const canonical = { ...rest, command: CommandLine };
8931
+ if (typeof Cwd === "string" && Cwd.length > 0) canonical.cwd = Cwd;
8932
+ return canonical;
8933
+ }
8934
+ var init_hook_payload = __esm({
8935
+ "src/utils/hook-payload.ts"() {
8936
+ "use strict";
8937
+ }
8938
+ });
8939
+
8488
8940
  // src/scan-summary.ts
8941
+ function agentDisplayName(agent) {
8942
+ return AGENT_LONG[agent] ?? "Claude Code";
8943
+ }
8944
+ function agentBadgeText(agent, width = 10) {
8945
+ return `[${AGENT_SHORT[agent] ?? "Claude"}]`.padEnd(width);
8946
+ }
8947
+ function agentColorName(agent) {
8948
+ switch (agent) {
8949
+ case "gemini":
8950
+ return "blue";
8951
+ case "codex":
8952
+ return "magenta";
8953
+ case "antigravity":
8954
+ return "yellow";
8955
+ case "copilot":
8956
+ return "green";
8957
+ case "shell":
8958
+ return "yellow";
8959
+ default:
8960
+ return "cyan";
8961
+ }
8962
+ }
8489
8963
  function buildScanSummary(agents) {
8490
8964
  const stats = {
8491
8965
  sessions: 0,
@@ -8662,12 +9136,29 @@ function fullCommandOf(input) {
8662
9136
  const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
8663
9137
  return String(raw).replace(/\s+/g, " ").trim();
8664
9138
  }
9139
+ var AGENT_SHORT, AGENT_LONG;
8665
9140
  var init_scan_summary = __esm({
8666
9141
  "src/scan-summary.ts"() {
8667
9142
  "use strict";
8668
9143
  init_shields();
8669
9144
  init_dist();
8670
9145
  init_dist();
9146
+ AGENT_SHORT = {
9147
+ claude: "Claude",
9148
+ gemini: "Gemini",
9149
+ codex: "Codex",
9150
+ antigravity: "Agy",
9151
+ copilot: "Copilot",
9152
+ shell: "Shell"
9153
+ };
9154
+ AGENT_LONG = {
9155
+ claude: "Claude Code",
9156
+ gemini: "Gemini CLI",
9157
+ codex: "Codex",
9158
+ antigravity: "Antigravity",
9159
+ copilot: "GitHub Copilot",
9160
+ shell: "Shell"
9161
+ };
8671
9162
  }
8672
9163
  });
8673
9164
 
@@ -10308,6 +10799,34 @@ function countScanFiles() {
10308
10799
  } catch {
10309
10800
  }
10310
10801
  }
10802
+ for (const surface of ["antigravity-cli", "antigravity-ide"]) {
10803
+ const brainDir = import_path22.default.join(import_os19.default.homedir(), ".gemini", surface, "brain");
10804
+ if (!import_fs20.default.existsSync(brainDir)) continue;
10805
+ try {
10806
+ for (const conv of import_fs20.default.readdirSync(brainDir)) {
10807
+ const convPath = import_path22.default.join(brainDir, conv);
10808
+ try {
10809
+ if (!import_fs20.default.statSync(convPath).isDirectory()) continue;
10810
+ const logsDir = import_path22.default.join(convPath, ".system_generated", "logs");
10811
+ if (import_fs20.default.existsSync(import_path22.default.join(logsDir, "transcript_full.jsonl")) || import_fs20.default.existsSync(import_path22.default.join(logsDir, "transcript.jsonl"))) {
10812
+ total += 1;
10813
+ }
10814
+ } catch {
10815
+ continue;
10816
+ }
10817
+ }
10818
+ } catch {
10819
+ }
10820
+ }
10821
+ const copilotDir = import_path22.default.join(import_os19.default.homedir(), ".copilot", "session-state");
10822
+ if (import_fs20.default.existsSync(copilotDir)) {
10823
+ try {
10824
+ for (const sid of import_fs20.default.readdirSync(copilotDir)) {
10825
+ if (import_fs20.default.existsSync(import_path22.default.join(copilotDir, sid, "events.jsonl"))) total += 1;
10826
+ }
10827
+ } catch {
10828
+ }
10829
+ }
10311
10830
  const codexDir = import_path22.default.join(import_os19.default.homedir(), ".codex", "sessions");
10312
10831
  if (import_fs20.default.existsSync(codexDir)) {
10313
10832
  try {
@@ -10678,33 +11197,248 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10678
11197
  if (!import_fs20.default.existsSync(chatsDir)) continue;
10679
11198
  let chatFiles;
10680
11199
  try {
10681
- chatFiles = import_fs20.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
11200
+ chatFiles = import_fs20.default.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
11201
+ } catch {
11202
+ continue;
11203
+ }
11204
+ for (const chatFile of chatFiles) {
11205
+ result.filesScanned++;
11206
+ onProgress?.(result.filesScanned);
11207
+ const sessionId = chatFile.replace(/\.json$/, "");
11208
+ let raw;
11209
+ try {
11210
+ raw = import_fs20.default.readFileSync(import_path22.default.join(chatsDir, chatFile), "utf-8");
11211
+ } catch {
11212
+ continue;
11213
+ }
11214
+ const sessionCalls = [];
11215
+ let session;
11216
+ try {
11217
+ session = JSON.parse(raw);
11218
+ } catch {
11219
+ continue;
11220
+ }
11221
+ result.sessions++;
11222
+ for (const msg of session.messages ?? []) {
11223
+ onLine?.();
11224
+ if (msg.type === "user") {
11225
+ const content = msg.content;
11226
+ const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
11227
+ if (text) {
11228
+ const dlpMatch = scanArgs({ text });
11229
+ if (dlpMatch) {
11230
+ const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
11231
+ if (!dedup.dlpKeys.has(k)) {
11232
+ dedup.dlpKeys.add(k);
11233
+ result.dlpFindings.push({
11234
+ patternName: dlpMatch.patternName,
11235
+ redactedSample: dlpMatch.redactedSample,
11236
+ toolName: "user-prompt",
11237
+ timestamp: msg.timestamp ?? "",
11238
+ project: projLabel,
11239
+ sessionId,
11240
+ agent: "gemini"
11241
+ });
11242
+ }
11243
+ }
11244
+ }
11245
+ continue;
11246
+ }
11247
+ if (msg.type !== "gemini") continue;
11248
+ if (startDate && msg.timestamp && new Date(msg.timestamp) < startDate) continue;
11249
+ if (msg.timestamp) {
11250
+ if (!result.firstDate || msg.timestamp < result.firstDate)
11251
+ result.firstDate = msg.timestamp;
11252
+ if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
11253
+ }
11254
+ const tokens = msg.tokens;
11255
+ const model = msg.model;
11256
+ if (tokens && model) {
11257
+ const p = geminiModelPrice(model);
11258
+ if (p) {
11259
+ const nonCached = Math.max(0, tokens.input - tokens.cached);
11260
+ result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
11261
+ }
11262
+ }
11263
+ for (const tc of msg.toolCalls ?? []) {
11264
+ result.totalToolCalls++;
11265
+ const toolName = tc.name ?? "";
11266
+ const toolNameLower = toolName.toLowerCase();
11267
+ const input = tc.args ?? {};
11268
+ sessionCalls.push({ toolName, input, timestamp: msg.timestamp ?? "" });
11269
+ if (toolNameLower === "run_shell_command" || toolNameLower === "shell") {
11270
+ result.bashCalls++;
11271
+ }
11272
+ const rawCmd = String(input.command ?? "").trimStart();
11273
+ if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
11274
+ continue;
11275
+ const dlpMatch = scanArgs(input);
11276
+ if (dlpMatch) {
11277
+ const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
11278
+ if (!dedup.dlpKeys.has(k)) {
11279
+ dedup.dlpKeys.add(k);
11280
+ result.dlpFindings.push({
11281
+ patternName: dlpMatch.patternName,
11282
+ redactedSample: dlpMatch.redactedSample,
11283
+ toolName,
11284
+ timestamp: msg.timestamp ?? "",
11285
+ project: projLabel,
11286
+ sessionId,
11287
+ agent: "gemini"
11288
+ });
11289
+ }
11290
+ }
11291
+ let astFsMatched = false;
11292
+ const astRanForBash = toolNameLower === "run_shell_command" || toolNameLower === "shell";
11293
+ if (astRanForBash) {
11294
+ astFsMatched = pushFsOpAstFinding(
11295
+ String(input.command ?? ""),
11296
+ toolName,
11297
+ input,
11298
+ msg.timestamp ?? "",
11299
+ projLabel,
11300
+ sessionId,
11301
+ "gemini",
11302
+ result,
11303
+ dedup
11304
+ );
11305
+ }
11306
+ let ruleMatched = astFsMatched;
11307
+ for (const source of ruleSources) {
11308
+ const { rule } = source;
11309
+ if (rule.verdict === "allow") continue;
11310
+ if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
11311
+ if (astRanForBash && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
11312
+ if (!evaluateSmartConditions(input, rule)) continue;
11313
+ const inputPreview = preview(input, 120);
11314
+ const k = findingKey(rule.name, inputPreview, projLabel);
11315
+ if (!dedup.findingsKeys.has(k)) {
11316
+ dedup.findingsKeys.add(k);
11317
+ result.findings.push({
11318
+ source,
11319
+ toolName,
11320
+ input,
11321
+ timestamp: msg.timestamp ?? "",
11322
+ project: projLabel,
11323
+ sessionId,
11324
+ agent: "gemini"
11325
+ });
11326
+ }
11327
+ ruleMatched = true;
11328
+ break;
11329
+ }
11330
+ const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
11331
+ toolNameLower
11332
+ );
11333
+ if (!ruleMatched && isShellTool) {
11334
+ const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
11335
+ if (shellVerdict) {
11336
+ const astRule = {
11337
+ name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
11338
+ tool: "bash",
11339
+ conditions: [],
11340
+ verdict: shellVerdict,
11341
+ reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
11342
+ };
11343
+ const inputPreview = preview(input, 120);
11344
+ const k = findingKey(astRule.name, inputPreview, projLabel);
11345
+ if (!dedup.findingsKeys.has(k)) {
11346
+ dedup.findingsKeys.add(k);
11347
+ result.findings.push({
11348
+ source: {
11349
+ shieldName: "bash-safe",
11350
+ shieldLabel: "bash-safe (AST)",
11351
+ sourceType: "shield",
11352
+ rule: astRule
11353
+ },
11354
+ toolName,
11355
+ input,
11356
+ timestamp: msg.timestamp ?? "",
11357
+ project: projLabel,
11358
+ sessionId,
11359
+ agent: "gemini"
11360
+ });
11361
+ }
11362
+ }
11363
+ }
11364
+ }
11365
+ }
11366
+ result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "gemini"));
11367
+ }
11368
+ }
11369
+ return result;
11370
+ }
11371
+ function antigravityBrainDirs() {
11372
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => import_path22.default.join(import_os19.default.homedir(), ".gemini", surface, "brain")).filter((p) => import_fs20.default.existsSync(p));
11373
+ }
11374
+ function antigravityTranscriptPath(convPath) {
11375
+ const logsDir = import_path22.default.join(convPath, ".system_generated", "logs");
11376
+ for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
11377
+ const p = import_path22.default.join(logsDir, name);
11378
+ if (import_fs20.default.existsSync(p)) return p;
11379
+ }
11380
+ return null;
11381
+ }
11382
+ function scanAntigravityHistory(startDate, onProgress, onLine) {
11383
+ const result = {
11384
+ filesScanned: 0,
11385
+ sessions: 0,
11386
+ totalToolCalls: 0,
11387
+ bashCalls: 0,
11388
+ findings: [],
11389
+ dlpFindings: [],
11390
+ loopFindings: [],
11391
+ totalCostUSD: 0,
11392
+ // transcripts carry no token/model data
11393
+ firstDate: null,
11394
+ lastDate: null,
11395
+ sessionsWithEarlySecrets: 0
11396
+ };
11397
+ const dedup = emptyScanDedup();
11398
+ const brainDirs = antigravityBrainDirs();
11399
+ if (brainDirs.length === 0) return result;
11400
+ const ruleSources = buildRuleSources();
11401
+ for (const brainDir of brainDirs) {
11402
+ let convDirs;
11403
+ try {
11404
+ convDirs = import_fs20.default.readdirSync(brainDir);
10682
11405
  } catch {
10683
11406
  continue;
10684
11407
  }
10685
- for (const chatFile of chatFiles) {
10686
- result.filesScanned++;
10687
- onProgress?.(result.filesScanned);
10688
- const sessionId = chatFile.replace(/\.json$/, "");
10689
- let raw;
11408
+ for (const conv of convDirs) {
11409
+ const convPath = import_path22.default.join(brainDir, conv);
10690
11410
  try {
10691
- raw = import_fs20.default.readFileSync(import_path22.default.join(chatsDir, chatFile), "utf-8");
11411
+ if (!import_fs20.default.statSync(convPath).isDirectory()) continue;
10692
11412
  } catch {
10693
11413
  continue;
10694
11414
  }
10695
- const sessionCalls = [];
10696
- let session;
11415
+ const transcriptFile = antigravityTranscriptPath(convPath);
11416
+ if (!transcriptFile) continue;
11417
+ result.filesScanned++;
11418
+ onProgress?.(result.filesScanned);
11419
+ let raw;
10697
11420
  try {
10698
- session = JSON.parse(raw);
11421
+ raw = import_fs20.default.readFileSync(transcriptFile, "utf-8");
10699
11422
  } catch {
10700
11423
  continue;
10701
11424
  }
11425
+ const sessionId = conv;
11426
+ let projLabel = conv.slice(0, 8);
11427
+ const sessionCalls = [];
10702
11428
  result.sessions++;
10703
- for (const msg of session.messages ?? []) {
11429
+ for (const line of raw.split("\n")) {
11430
+ if (!line.trim()) continue;
10704
11431
  onLine?.();
10705
- if (msg.type === "user") {
10706
- const content = msg.content;
10707
- const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
11432
+ let step;
11433
+ try {
11434
+ step = JSON.parse(line);
11435
+ } catch {
11436
+ continue;
11437
+ }
11438
+ const timestamp = step.created_at ?? "";
11439
+ if (startDate && timestamp && new Date(timestamp) < startDate) continue;
11440
+ if (step.type === "USER_INPUT") {
11441
+ const text = typeof step.content === "string" ? step.content : "";
10708
11442
  if (text) {
10709
11443
  const dlpMatch = scanArgs({ text });
10710
11444
  if (dlpMatch) {
@@ -10715,40 +11449,34 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10715
11449
  patternName: dlpMatch.patternName,
10716
11450
  redactedSample: dlpMatch.redactedSample,
10717
11451
  toolName: "user-prompt",
10718
- timestamp: msg.timestamp ?? "",
11452
+ timestamp,
10719
11453
  project: projLabel,
10720
11454
  sessionId,
10721
- agent: "gemini"
11455
+ agent: "antigravity"
10722
11456
  });
10723
11457
  }
10724
11458
  }
10725
11459
  }
10726
11460
  continue;
10727
11461
  }
10728
- if (msg.type !== "gemini") continue;
10729
- if (startDate && msg.timestamp && new Date(msg.timestamp) < startDate) continue;
10730
- if (msg.timestamp) {
10731
- if (!result.firstDate || msg.timestamp < result.firstDate)
10732
- result.firstDate = msg.timestamp;
10733
- if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
10734
- }
10735
- const tokens = msg.tokens;
10736
- const model = msg.model;
10737
- if (tokens && model) {
10738
- const p = geminiModelPrice(model);
10739
- if (p) {
10740
- const nonCached = Math.max(0, tokens.input - tokens.cached);
10741
- result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
10742
- }
11462
+ if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
11463
+ if (timestamp) {
11464
+ if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
11465
+ if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
10743
11466
  }
10744
- for (const tc of msg.toolCalls ?? []) {
11467
+ for (const tc of step.tool_calls) {
10745
11468
  result.totalToolCalls++;
10746
11469
  const toolName = tc.name ?? "";
10747
11470
  const toolNameLower = toolName.toLowerCase();
10748
- const input = tc.args ?? {};
10749
- sessionCalls.push({ toolName, input, timestamp: msg.timestamp ?? "" });
10750
- if (toolNameLower === "run_shell_command" || toolNameLower === "shell") {
11471
+ const input = canonicalToolInput(toolName, tc.args ?? {});
11472
+ sessionCalls.push({ toolName, input, timestamp });
11473
+ const isShellTool = toolNameLower === "run_command";
11474
+ if (isShellTool) {
10751
11475
  result.bashCalls++;
11476
+ const cwd = String(input.cwd ?? "");
11477
+ if (cwd && projLabel === conv.slice(0, 8)) {
11478
+ projLabel = stripTerminalEscapes(cwd).replace(import_os19.default.homedir(), "~").slice(0, 40);
11479
+ }
10752
11480
  }
10753
11481
  const rawCmd = String(input.command ?? "").trimStart();
10754
11482
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
@@ -10762,24 +11490,23 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10762
11490
  patternName: dlpMatch.patternName,
10763
11491
  redactedSample: dlpMatch.redactedSample,
10764
11492
  toolName,
10765
- timestamp: msg.timestamp ?? "",
11493
+ timestamp,
10766
11494
  project: projLabel,
10767
11495
  sessionId,
10768
- agent: "gemini"
11496
+ agent: "antigravity"
10769
11497
  });
10770
11498
  }
10771
11499
  }
10772
11500
  let astFsMatched = false;
10773
- const astRanForBash = toolNameLower === "run_shell_command" || toolNameLower === "shell";
10774
- if (astRanForBash) {
11501
+ if (isShellTool) {
10775
11502
  astFsMatched = pushFsOpAstFinding(
10776
11503
  String(input.command ?? ""),
10777
11504
  toolName,
10778
11505
  input,
10779
- msg.timestamp ?? "",
11506
+ timestamp,
10780
11507
  projLabel,
10781
11508
  sessionId,
10782
- "gemini",
11509
+ "antigravity",
10783
11510
  result,
10784
11511
  dedup
10785
11512
  );
@@ -10788,8 +11515,9 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10788
11515
  for (const source of ruleSources) {
10789
11516
  const { rule } = source;
10790
11517
  if (rule.verdict === "allow") continue;
10791
- if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
10792
- if (astRanForBash && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
11518
+ const ruleToolName = isShellTool ? "bash" : toolNameLower;
11519
+ if (rule.tool && !matchesPattern(ruleToolName, rule.tool)) continue;
11520
+ if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
10793
11521
  if (!evaluateSmartConditions(input, rule)) continue;
10794
11522
  const inputPreview = preview(input, 120);
10795
11523
  const k = findingKey(rule.name, inputPreview, projLabel);
@@ -10799,18 +11527,15 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10799
11527
  source,
10800
11528
  toolName,
10801
11529
  input,
10802
- timestamp: msg.timestamp ?? "",
11530
+ timestamp,
10803
11531
  project: projLabel,
10804
11532
  sessionId,
10805
- agent: "gemini"
11533
+ agent: "antigravity"
10806
11534
  });
10807
11535
  }
10808
11536
  ruleMatched = true;
10809
11537
  break;
10810
11538
  }
10811
- const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
10812
- toolNameLower
10813
- );
10814
11539
  if (!ruleMatched && isShellTool) {
10815
11540
  const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
10816
11541
  if (shellVerdict) {
@@ -10834,18 +11559,201 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10834
11559
  },
10835
11560
  toolName,
10836
11561
  input,
10837
- timestamp: msg.timestamp ?? "",
11562
+ timestamp,
10838
11563
  project: projLabel,
10839
11564
  sessionId,
10840
- agent: "gemini"
11565
+ agent: "antigravity"
10841
11566
  });
10842
11567
  }
10843
11568
  }
10844
11569
  }
10845
11570
  }
10846
11571
  }
10847
- result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "gemini"));
11572
+ result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "antigravity"));
11573
+ }
11574
+ }
11575
+ return result;
11576
+ }
11577
+ function scanCopilotHistory(startDate, onProgress, onLine) {
11578
+ const sessionDir = import_path22.default.join(import_os19.default.homedir(), ".copilot", "session-state");
11579
+ const result = {
11580
+ filesScanned: 0,
11581
+ sessions: 0,
11582
+ totalToolCalls: 0,
11583
+ bashCalls: 0,
11584
+ findings: [],
11585
+ dlpFindings: [],
11586
+ loopFindings: [],
11587
+ totalCostUSD: 0,
11588
+ // event logs carry no token/cost rollup
11589
+ firstDate: null,
11590
+ lastDate: null,
11591
+ sessionsWithEarlySecrets: 0
11592
+ };
11593
+ const dedup = emptyScanDedup();
11594
+ if (!import_fs20.default.existsSync(sessionDir)) return result;
11595
+ let sessionIds;
11596
+ try {
11597
+ sessionIds = import_fs20.default.readdirSync(sessionDir);
11598
+ } catch {
11599
+ return result;
11600
+ }
11601
+ const ruleSources = buildRuleSources();
11602
+ for (const sessionId of sessionIds) {
11603
+ const eventsPath = import_path22.default.join(sessionDir, sessionId, "events.jsonl");
11604
+ if (!import_fs20.default.existsSync(eventsPath)) continue;
11605
+ result.filesScanned++;
11606
+ onProgress?.(result.filesScanned);
11607
+ let raw;
11608
+ try {
11609
+ raw = import_fs20.default.readFileSync(eventsPath, "utf-8");
11610
+ } catch {
11611
+ continue;
11612
+ }
11613
+ let projLabel = sessionId.slice(0, 8);
11614
+ const sessionCalls = [];
11615
+ result.sessions++;
11616
+ for (const line of raw.split("\n")) {
11617
+ if (!line.trim()) continue;
11618
+ onLine?.();
11619
+ let ev;
11620
+ try {
11621
+ ev = JSON.parse(line);
11622
+ } catch {
11623
+ continue;
11624
+ }
11625
+ const timestamp = ev.timestamp ?? "";
11626
+ if (ev.type === "session.start") {
11627
+ const cwd = ev.data?.context?.cwd;
11628
+ if (typeof cwd === "string" && cwd) {
11629
+ projLabel = stripTerminalEscapes(cwd).replace(import_os19.default.homedir(), "~").slice(0, 40);
11630
+ }
11631
+ continue;
11632
+ }
11633
+ if (startDate && timestamp && new Date(timestamp) < startDate) continue;
11634
+ if (ev.type === "user.message") {
11635
+ const text = ev.data?.content ?? ev.data?.text ?? "";
11636
+ if (typeof text === "string" && text) {
11637
+ const dlpMatch2 = scanArgs({ text });
11638
+ if (dlpMatch2) {
11639
+ const k = dlpKey(dlpMatch2.patternName, dlpMatch2.redactedSample, projLabel);
11640
+ if (!dedup.dlpKeys.has(k)) {
11641
+ dedup.dlpKeys.add(k);
11642
+ result.dlpFindings.push({
11643
+ patternName: dlpMatch2.patternName,
11644
+ redactedSample: dlpMatch2.redactedSample,
11645
+ toolName: "user-prompt",
11646
+ timestamp,
11647
+ project: projLabel,
11648
+ sessionId,
11649
+ agent: "copilot"
11650
+ });
11651
+ }
11652
+ }
11653
+ }
11654
+ continue;
11655
+ }
11656
+ if (ev.type !== "tool.execution_start") continue;
11657
+ const toolName = ev.data?.toolName ?? "";
11658
+ const toolNameLower = toolName.toLowerCase();
11659
+ const input = ev.data?.arguments ?? {};
11660
+ result.totalToolCalls++;
11661
+ sessionCalls.push({ toolName, input, timestamp });
11662
+ const isShellTool = toolNameLower === "bash" || toolNameLower === "shell";
11663
+ if (isShellTool) result.bashCalls++;
11664
+ if (timestamp) {
11665
+ if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
11666
+ if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
11667
+ }
11668
+ const rawCmd = String(input.command ?? "").trimStart();
11669
+ if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
11670
+ const dlpMatch = scanArgs(input);
11671
+ if (dlpMatch) {
11672
+ const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
11673
+ if (!dedup.dlpKeys.has(k)) {
11674
+ dedup.dlpKeys.add(k);
11675
+ result.dlpFindings.push({
11676
+ patternName: dlpMatch.patternName,
11677
+ redactedSample: dlpMatch.redactedSample,
11678
+ toolName,
11679
+ timestamp,
11680
+ project: projLabel,
11681
+ sessionId,
11682
+ agent: "copilot"
11683
+ });
11684
+ }
11685
+ }
11686
+ let astFsMatched = false;
11687
+ if (isShellTool) {
11688
+ astFsMatched = pushFsOpAstFinding(
11689
+ String(input.command ?? ""),
11690
+ toolName,
11691
+ input,
11692
+ timestamp,
11693
+ projLabel,
11694
+ sessionId,
11695
+ "copilot",
11696
+ result,
11697
+ dedup
11698
+ );
11699
+ }
11700
+ let ruleMatched = astFsMatched;
11701
+ for (const source of ruleSources) {
11702
+ const { rule } = source;
11703
+ if (rule.verdict === "allow") continue;
11704
+ if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
11705
+ if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
11706
+ if (!evaluateSmartConditions(input, rule)) continue;
11707
+ const inputPreview = preview(input, 120);
11708
+ const k = findingKey(rule.name, inputPreview, projLabel);
11709
+ if (!dedup.findingsKeys.has(k)) {
11710
+ dedup.findingsKeys.add(k);
11711
+ result.findings.push({
11712
+ source,
11713
+ toolName,
11714
+ input,
11715
+ timestamp,
11716
+ project: projLabel,
11717
+ sessionId,
11718
+ agent: "copilot"
11719
+ });
11720
+ }
11721
+ ruleMatched = true;
11722
+ break;
11723
+ }
11724
+ if (!ruleMatched && isShellTool) {
11725
+ const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
11726
+ if (shellVerdict) {
11727
+ const astRule = {
11728
+ name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
11729
+ tool: "bash",
11730
+ conditions: [],
11731
+ verdict: shellVerdict,
11732
+ reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
11733
+ };
11734
+ const inputPreview = preview(input, 120);
11735
+ const k = findingKey(astRule.name, inputPreview, projLabel);
11736
+ if (!dedup.findingsKeys.has(k)) {
11737
+ dedup.findingsKeys.add(k);
11738
+ result.findings.push({
11739
+ source: {
11740
+ shieldName: "bash-safe",
11741
+ shieldLabel: "bash-safe (AST)",
11742
+ sourceType: "shield",
11743
+ rule: astRule
11744
+ },
11745
+ toolName,
11746
+ input,
11747
+ timestamp,
11748
+ project: projLabel,
11749
+ sessionId,
11750
+ agent: "copilot"
11751
+ });
11752
+ }
11753
+ }
11754
+ }
10848
11755
  }
11756
+ result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "copilot"));
10849
11757
  }
10850
11758
  return result;
10851
11759
  }
@@ -11144,8 +12052,8 @@ function printFindingRow(f, drillDown, showSessionId, previewWidth) {
11144
12052
  const stale = isStaleFinding(f.timestamp);
11145
12053
  const ts = f.timestamp ? import_chalk5.default.dim(fmtTs(f.timestamp) + " ") : "";
11146
12054
  const proj = import_chalk5.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
11147
- const agentLabel2 = f.agent === "gemini" ? "[Gemini] " : f.agent === "codex" ? "[Codex] " : "[Claude] ";
11148
- const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) : f.agent === "gemini" ? import_chalk5.default.blue(agentLabel2) : f.agent === "codex" ? import_chalk5.default.magenta(agentLabel2) : import_chalk5.default.cyan(agentLabel2);
12055
+ const agentLabel2 = agentBadgeText(f.agent);
12056
+ const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) : import_chalk5.default[agentColorName(f.agent)](agentLabel2);
11149
12057
  let cmdText;
11150
12058
  if (drillDown) {
11151
12059
  cmdText = f.fullCommand;
@@ -11708,16 +12616,35 @@ function registerScanCommand(program2) {
11708
12616
  (done) => onProgress(claudeScan.filesScanned + done),
11709
12617
  onLine
11710
12618
  );
11711
- const codexScan = scanCodexHistory(
12619
+ const antigravityScan = scanAntigravityHistory(
11712
12620
  startDate,
11713
12621
  (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
11714
12622
  onLine
11715
12623
  );
11716
- const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
12624
+ const copilotScan = scanCopilotHistory(
12625
+ startDate,
12626
+ (done) => onProgress(
12627
+ claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + done
12628
+ ),
12629
+ onLine
12630
+ );
12631
+ const codexScan = scanCodexHistory(
12632
+ startDate,
12633
+ (done) => onProgress(
12634
+ claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + copilotScan.filesScanned + done
12635
+ ),
12636
+ onLine
12637
+ );
12638
+ const scan = mergeScans(
12639
+ mergeScans(mergeScans(mergeScans(claudeScan, geminiScan), antigravityScan), copilotScan),
12640
+ codexScan
12641
+ );
11717
12642
  scan.dlpFindings.push(...scanShellConfig());
11718
12643
  const summary = buildScanSummary([
11719
12644
  { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
11720
12645
  { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
12646
+ { id: "antigravity", label: "Antigravity", icon: "\u{1F680}", scan: antigravityScan },
12647
+ { id: "copilot", label: "Copilot", icon: "\u{1F419}", scan: copilotScan },
11721
12648
  { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
11722
12649
  ]);
11723
12650
  if (useTTY) process.stdout.write("\r" + " ".repeat(60) + "\r");
@@ -11725,7 +12652,7 @@ function registerScanCommand(program2) {
11725
12652
  console.log(import_chalk5.default.yellow(" No session history found."));
11726
12653
  console.log(
11727
12654
  import_chalk5.default.gray(
11728
- " Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/)\n"
12655
+ " Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/) \xB7 Antigravity (~/.gemini/antigravity-*/brain/) \xB7 Copilot CLI (~/.copilot/session-state/)\n"
11729
12656
  )
11730
12657
  );
11731
12658
  return;
@@ -11737,6 +12664,12 @@ function registerScanCommand(program2) {
11737
12664
  breakdownParts.push(import_chalk5.default.cyan(String(claudeScan.sessions)) + import_chalk5.default.dim(" Claude"));
11738
12665
  if (geminiScan.sessions > 0)
11739
12666
  breakdownParts.push(import_chalk5.default.blue(String(geminiScan.sessions)) + import_chalk5.default.dim(" Gemini"));
12667
+ if (antigravityScan.sessions > 0)
12668
+ breakdownParts.push(
12669
+ import_chalk5.default.yellow(String(antigravityScan.sessions)) + import_chalk5.default.dim(" Antigravity")
12670
+ );
12671
+ if (copilotScan.sessions > 0)
12672
+ breakdownParts.push(import_chalk5.default.green(String(copilotScan.sessions)) + import_chalk5.default.dim(" Copilot"));
11740
12673
  if (codexScan.sessions > 0)
11741
12674
  breakdownParts.push(import_chalk5.default.magenta(String(codexScan.sessions)) + import_chalk5.default.dim(" Codex"));
11742
12675
  const sessionBreakdown = breakdownParts.length > 1 ? import_chalk5.default.dim("(") + breakdownParts.join(import_chalk5.default.dim(" \xB7 ")) + import_chalk5.default.dim(")") : "";
@@ -11925,7 +12858,7 @@ function registerScanCommand(program2) {
11925
12858
  const stale = isStaleFinding(f.timestamp);
11926
12859
  const ts = f.timestamp ? import_chalk5.default.dim(fmtTs(f.timestamp) + " ") : "";
11927
12860
  const proj = import_chalk5.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
11928
- const agentBadge = f.agent === "gemini" ? import_chalk5.default.blue("[Gemini] ") : f.agent === "codex" ? import_chalk5.default.magenta("[Codex] ") : f.agent === "shell" ? import_chalk5.default.yellow("[Shell] ") : import_chalk5.default.cyan("[Claude] ");
12861
+ const agentBadge = import_chalk5.default[agentColorName(f.agent)](agentBadgeText(f.agent));
11929
12862
  const sessionSuffix = f.sessionId ? import_chalk5.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
11930
12863
  const recurringBadge = recurringPatterns.has(f.patternName) ? import_chalk5.default.red.bold(" \u26A0\uFE0F recurring ") : "";
11931
12864
  const patternDisplay = stale ? import_chalk5.default.dim(f.patternName) : import_chalk5.default.yellow(f.patternName);
@@ -11973,8 +12906,8 @@ function registerScanCommand(program2) {
11973
12906
  const stale = isStaleFinding(f.timestamp);
11974
12907
  const ts = f.timestamp ? import_chalk5.default.dim(fmtTs(f.timestamp) + " ") : "";
11975
12908
  const proj = import_chalk5.default.dim(f.project.slice(0, 22).padEnd(22) + " ");
11976
- const agentLabel2 = f.agent === "gemini" ? "[Gemini] " : f.agent === "codex" ? "[Codex] " : "[Claude] ";
11977
- const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) : f.agent === "gemini" ? import_chalk5.default.blue(agentLabel2) : f.agent === "codex" ? import_chalk5.default.magenta(agentLabel2) : import_chalk5.default.cyan(agentLabel2);
12909
+ const agentLabel2 = agentBadgeText(f.agent);
12910
+ const agentBadge = stale ? import_chalk5.default.dim(agentLabel2) : import_chalk5.default[agentColorName(f.agent)](agentLabel2);
11978
12911
  const toolDisplay = stale ? import_chalk5.default.dim(f.toolName) : import_chalk5.default.yellow(f.toolName);
11979
12912
  const cmdDisplay = stale ? import_chalk5.default.dim(f.commandPreview) : import_chalk5.default.gray(f.commandPreview);
11980
12913
  const sessionSuffix = f.sessionId ? import_chalk5.default.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
@@ -12112,6 +13045,7 @@ var init_scan = __esm({
12112
13045
  init_policy();
12113
13046
  init_dist();
12114
13047
  init_dlp();
13048
+ init_hook_payload();
12115
13049
  init_dist();
12116
13050
  init_scan_summary();
12117
13051
  init_setup();
@@ -12191,6 +13125,8 @@ var init_scan = __esm({
12191
13125
  "exec_command",
12192
13126
  "shell",
12193
13127
  "run_shell_command",
13128
+ "run_command",
13129
+ // Antigravity (agy)
12194
13130
  "write",
12195
13131
  "edit",
12196
13132
  "multiedit"
@@ -16779,33 +17715,7 @@ function resolveUserSkillRoot(entry, cwd) {
16779
17715
  // src/cli/commands/check.ts
16780
17716
  init_dlp();
16781
17717
  init_audit();
16782
-
16783
- // src/utils/hook-payload.ts
16784
- function extractToolName(payload, defaultValue = "") {
16785
- return payload.tool_name ?? payload.name ?? defaultValue;
16786
- }
16787
- function extractToolInput(payload) {
16788
- return payload.tool_input ?? payload.args ?? {};
16789
- }
16790
- function canonicalToolName(name) {
16791
- switch (name) {
16792
- // Hermes Agent
16793
- case "terminal":
16794
- return "Bash";
16795
- case "write_file":
16796
- return "Write";
16797
- case "patch":
16798
- return "Edit";
16799
- case "read_file":
16800
- return "Read";
16801
- case "search_files":
16802
- return "Grep";
16803
- default:
16804
- return name;
16805
- }
16806
- }
16807
-
16808
- // src/cli/commands/check.ts
17718
+ init_hook_payload();
16809
17719
  function sanitize2(value) {
16810
17720
  return value.replace(/[\x00-\x1F\x7F]/g, "");
16811
17721
  }
@@ -16818,6 +17728,9 @@ function detectAiAgent(payload) {
16818
17728
  if (payload.turn_id !== void 0) {
16819
17729
  return "Codex";
16820
17730
  }
17731
+ if (payload.toolCall !== void 0 || payload.conversationId !== void 0) {
17732
+ return "Antigravity";
17733
+ }
16821
17734
  if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
16822
17735
  return "Claude Code";
16823
17736
  }
@@ -16833,6 +17746,9 @@ function detectAiAgent(payload) {
16833
17746
  if (process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE) {
16834
17747
  return "Hermes";
16835
17748
  }
17749
+ if (process.env.ANTIGRAVITY_CONVERSATION_ID) {
17750
+ return "Antigravity";
17751
+ }
16836
17752
  if (process.env.GEMINI_CLI_VERSION || process.env.GEMINI_API_KEY) {
16837
17753
  return "Gemini CLI";
16838
17754
  }
@@ -16848,7 +17764,11 @@ function detectAiAgent(payload) {
16848
17764
  return "Terminal";
16849
17765
  }
16850
17766
  function registerCheckCommand(program2) {
16851
- program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").action(async (data) => {
17767
+ program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").option(
17768
+ "--agent <name>",
17769
+ "Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
17770
+ ).action(async (data, opts) => {
17771
+ const agentOverride = agentLabelFromFlag(opts?.agent);
16852
17772
  const processPayload = async (raw) => {
16853
17773
  try {
16854
17774
  if (!raw || raw.trim() === "") process.exit(0);
@@ -16888,7 +17808,7 @@ RAW: ${raw}
16888
17808
  if (!prompt) process.exit(0);
16889
17809
  const dlpMatch = scanArgs({ prompt });
16890
17810
  if (!dlpMatch) process.exit(0);
16891
- const agent2 = detectAiAgent(payload);
17811
+ const agent2 = agentOverride ?? detectAiAgent(payload);
16892
17812
  const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
16893
17813
  appendLocalAudit(
16894
17814
  "UserPromptSubmit",
@@ -16929,7 +17849,8 @@ RAW: ${raw}
16929
17849
  );
16930
17850
  process.exit(2);
16931
17851
  }
16932
- const safeCwdForConfig = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
17852
+ const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
17853
+ const safeCwdForConfig = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
16933
17854
  const config = getConfig(safeCwdForConfig);
16934
17855
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
16935
17856
  try {
@@ -16979,9 +17900,10 @@ RAW: ${raw}
16979
17900
  import_fs32.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
16980
17901
  `);
16981
17902
  }
16982
- const toolName = canonicalToolName(sanitize2(extractToolName(payload)));
16983
- const toolInput = extractToolInput(payload);
16984
- const agent = detectAiAgent(payload);
17903
+ const rawToolName = sanitize2(extractToolName(payload));
17904
+ const toolName = canonicalToolName(rawToolName);
17905
+ const toolInput = canonicalToolInput(rawToolName, extractToolInput(payload));
17906
+ const agent = agentOverride ?? detectAiAgent(payload);
16985
17907
  const mcpMatch = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
16986
17908
  const mcpServer = mcpMatch?.[1];
16987
17909
  const sendBlock = (msg, result2) => {
@@ -17019,6 +17941,21 @@ RAW: ${raw}
17019
17941
  msg,
17020
17942
  result2?.recoveryCommand
17021
17943
  );
17944
+ if (agent === "Antigravity") {
17945
+ process.stdout.write(
17946
+ JSON.stringify({ decision: "deny", reason: aiFeedbackMessage }) + "\n"
17947
+ );
17948
+ process.exit(0);
17949
+ }
17950
+ if (agent === "GitHub Copilot") {
17951
+ process.stdout.write(
17952
+ JSON.stringify({
17953
+ permissionDecision: "deny",
17954
+ permissionDecisionReason: aiFeedbackMessage
17955
+ }) + "\n"
17956
+ );
17957
+ process.exit(0);
17958
+ }
17022
17959
  process.stdout.write(
17023
17960
  JSON.stringify({
17024
17961
  decision: "block",
@@ -17037,11 +17974,11 @@ RAW: ${raw}
17037
17974
  sendBlock("Node9: unrecognised hook payload \u2014 tool name missing.");
17038
17975
  return;
17039
17976
  }
17040
- const sessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
17041
- const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : void 0;
17977
+ const sessionId = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : void 0;
17978
+ const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : typeof payload.transcriptPath === "string" ? payload.transcriptPath : void 0;
17042
17979
  const meta = { agent, mcpServer, sessionId, transcriptPath };
17043
17980
  const skillPinCfg = config.policy.skillPinning;
17044
- const rawSessionId = typeof payload.session_id === "string" ? payload.session_id : "";
17981
+ const rawSessionId = sessionId ?? "";
17045
17982
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
17046
17983
  if (skillPinCfg.enabled && safeSessionId) {
17047
17984
  try {
@@ -17098,7 +18035,7 @@ RAW: ${raw}
17098
18035
  return;
17099
18036
  }
17100
18037
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
17101
- const absoluteCwd = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
18038
+ const absoluteCwd = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17102
18039
  const extraRoots = skillPinCfg.roots;
17103
18040
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
17104
18041
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -17164,7 +18101,7 @@ RAW: ${raw}
17164
18101
  if (shouldSnapshot(toolName, toolInput, config)) {
17165
18102
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
17166
18103
  }
17167
- const safeCwdForAuth = typeof payload.cwd === "string" && import_path33.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
18104
+ const safeCwdForAuth = typeof payloadCwd === "string" && import_path33.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17168
18105
  const result = await authorizeHeadless(toolName, toolInput, meta, {
17169
18106
  cwd: safeCwdForAuth
17170
18107
  });
@@ -17289,6 +18226,7 @@ function containsShellMetachar(token) {
17289
18226
  }
17290
18227
 
17291
18228
  // src/cli/commands/log.ts
18229
+ init_hook_payload();
17292
18230
  var TEST_COMMAND_RE2 = /(?:^|\s)(npm\s+(?:run\s+)?test|npx\s+(?:vitest|jest|mocha)|yarn\s+(?:run\s+)?test|pnpm\s+(?:run\s+)?test|vitest|jest|mocha|pytest|py\.test|cargo\s+test|go\s+test|bundle\s+exec\s+rspec|rspec|phpunit|dotnet\s+test)\b/i;
17293
18231
  function detectTestResult(command, output) {
17294
18232
  if (!TEST_COMMAND_RE2.test(command)) return null;
@@ -17307,14 +18245,19 @@ function sanitize3(value) {
17307
18245
  return value.replace(/[\x00-\x1F\x7F]/g, "");
17308
18246
  }
17309
18247
  function registerLogCommand(program2) {
17310
- program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").action(async (data) => {
18248
+ program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
18249
+ "--agent <name>",
18250
+ "Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
18251
+ ).action(async (data, opts) => {
18252
+ const agentOverride = agentLabelFromFlag(opts?.agent);
17311
18253
  const logPayload = async (raw) => {
17312
18254
  try {
17313
18255
  if (!raw || raw.trim() === "") process.exit(0);
17314
18256
  const payload = JSON.parse(raw);
18257
+ if (payload.toolCall === null) process.exit(0);
17315
18258
  const rawToolName = sanitize3(extractToolName(payload, "unknown"));
17316
18259
  const tool = canonicalToolName(rawToolName);
17317
- const rawInput = extractToolInput(payload);
18260
+ const rawInput = canonicalToolInput(rawToolName, extractToolInput(payload));
17318
18261
  const metaTag = (() => {
17319
18262
  const m = payload.meta;
17320
18263
  if (m && typeof m === "object") {
@@ -17323,7 +18266,7 @@ function registerLogCommand(program2) {
17323
18266
  }
17324
18267
  return void 0;
17325
18268
  })();
17326
- const agent = metaTag !== void 0 ? metaTag : payload.turn_id !== void 0 ? "Codex" : payload.hook_event_name === "pre_tool_call" || payload.hook_event_name === "post_tool_call" ? "Hermes" : payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE ? "Hermes" : void 0;
18269
+ const agent = agentOverride !== void 0 ? agentOverride : metaTag !== void 0 ? metaTag : payload.turn_id !== void 0 ? "Codex" : payload.toolCall !== void 0 || payload.conversationId !== void 0 ? "Antigravity" : payload.hook_event_name === "pre_tool_call" || payload.hook_event_name === "post_tool_call" ? "Hermes" : payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0 ? "Claude Code" : payload.hook_event_name === "BeforeTool" || payload.hook_event_name === "AfterTool" || payload.timestamp !== void 0 ? "Gemini CLI" : process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE ? "Hermes" : process.env.ANTIGRAVITY_CONVERSATION_ID ? "Antigravity" : void 0;
17327
18270
  const entry = {
17328
18271
  ts: (/* @__PURE__ */ new Date()).toISOString(),
17329
18272
  tool,
@@ -17333,7 +18276,8 @@ function registerLogCommand(program2) {
17333
18276
  };
17334
18277
  if (agent) entry.agent = agent;
17335
18278
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
17336
- if (payload.session_id) entry.sessionId = payload.session_id;
18279
+ const payloadSessionId = payload.session_id ?? payload.conversationId;
18280
+ if (payloadSessionId) entry.sessionId = payloadSessionId;
17337
18281
  const logPath = import_path34.default.join(import_os29.default.homedir(), ".node9", "audit.log");
17338
18282
  if (!import_fs33.default.existsSync(import_path34.default.dirname(logPath)))
17339
18283
  import_fs33.default.mkdirSync(import_path34.default.dirname(logPath), { recursive: true });
@@ -17370,7 +18314,8 @@ function registerLogCommand(program2) {
17370
18314
  }
17371
18315
  }
17372
18316
  }
17373
- const safeCwd = typeof payload.cwd === "string" && import_path34.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
18317
+ const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
18318
+ const safeCwd = typeof payloadCwd === "string" && import_path34.default.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17374
18319
  const config = getConfig(safeCwd);
17375
18320
  if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
17376
18321
  const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
@@ -19458,12 +20403,12 @@ function registerInitCommand(program2) {
19458
20403
  if (found.length === 0) {
19459
20404
  console.log(
19460
20405
  import_chalk16.default.gray(
19461
- "No AI agents detected. Install one of the supported agents (Claude Code, Codex, Gemini CLI, Cursor, Windsurf, VSCode, Claude Desktop, Opencode, Pi, or Hermes Agent)."
20406
+ "No AI agents detected. Install one of the supported agents (Claude Code, Codex, Antigravity, Gemini CLI, GitHub Copilot CLI, Cursor, Windsurf, VSCode, Claude Desktop, Opencode, Pi, or Hermes Agent)."
19462
20407
  )
19463
20408
  );
19464
20409
  console.log(
19465
20410
  import_chalk16.default.gray(
19466
- "then run: node9 agents add <claude|codex|gemini|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
20411
+ "then run: node9 agents add <claude|codex|antigravity|gemini|copilot|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
19467
20412
  )
19468
20413
  );
19469
20414
  return;
@@ -19477,6 +20422,8 @@ function registerInitCommand(program2) {
19477
20422
  console.log(import_chalk16.default.bold(`Wiring ${agent}...`));
19478
20423
  if (agent === "claude") await setupClaude();
19479
20424
  else if (agent === "gemini") await setupGemini();
20425
+ else if (agent === "antigravity") await setupAntigravity();
20426
+ else if (agent === "copilot") await setupCopilot();
19480
20427
  else if (agent === "cursor") await setupCursor();
19481
20428
  else if (agent === "codex") await setupCodex();
19482
20429
  else if (agent === "windsurf") await setupWindsurf();
@@ -21240,6 +22187,8 @@ init_setup();
21240
22187
  var SETUP_FN = {
21241
22188
  claude: setupClaude,
21242
22189
  gemini: setupGemini,
22190
+ antigravity: setupAntigravity,
22191
+ copilot: setupCopilot,
21243
22192
  cursor: setupCursor,
21244
22193
  codex: setupCodex,
21245
22194
  windsurf: setupWindsurf,
@@ -21252,6 +22201,8 @@ var SETUP_FN = {
21252
22201
  var TEARDOWN_FN = {
21253
22202
  claude: teardownClaude,
21254
22203
  gemini: teardownGemini,
22204
+ antigravity: teardownAntigravity,
22205
+ copilot: teardownCopilot,
21255
22206
  cursor: teardownCursor,
21256
22207
  codex: teardownCodex,
21257
22208
  windsurf: teardownWindsurf,
@@ -21262,6 +22213,10 @@ var TEARDOWN_FN = {
21262
22213
  hermes: teardownHermes
21263
22214
  };
21264
22215
  var AGENT_NAMES = Object.keys(SETUP_FN);
22216
+ function resolveAgentName(agent) {
22217
+ const lower = agent.toLowerCase();
22218
+ return lower === "agy" ? "antigravity" : lower;
22219
+ }
21265
22220
  function registerAgentsCommand(program2) {
21266
22221
  const agents = program2.command("agents").description("List and manage AI agent integrations");
21267
22222
  agents.command("list").description("Show all supported agents and their Node9 status").action(() => {
@@ -21290,9 +22245,16 @@ function registerAgentsCommand(program2) {
21290
22245
  import_chalk23.default.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + import_chalk23.default.white(`node9 agents add ${unwired[0].name}`) + "\n"
21291
22246
  );
21292
22247
  }
22248
+ if (statuses.some((s) => s.name === "gemini" && s.installed)) {
22249
+ console.log(
22250
+ import_chalk23.default.yellow(
22251
+ " \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
22252
+ ) + import_chalk23.default.gray(" Migrate to Antigravity: ") + import_chalk23.default.white("node9 agents add antigravity") + "\n"
22253
+ );
22254
+ }
21293
22255
  });
21294
22256
  agents.command("add").description("Wire Node9 into an agent").argument("<agent>", `Agent to wire: ${AGENT_NAMES.join(" | ")}`).action(async (agent) => {
21295
- const name = agent.toLowerCase();
22257
+ const name = resolveAgentName(agent);
21296
22258
  const fn = SETUP_FN[name];
21297
22259
  if (!fn) {
21298
22260
  console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
@@ -21301,7 +22263,7 @@ function registerAgentsCommand(program2) {
21301
22263
  await fn();
21302
22264
  });
21303
22265
  agents.command("remove").description("Remove Node9 from an agent").argument("<agent>", `Agent to unwire: ${AGENT_NAMES.join(" | ")}`).action((agent) => {
21304
- const name = agent.toLowerCase();
22266
+ const name = resolveAgentName(agent);
21305
22267
  const fn = TEARDOWN_FN[name];
21306
22268
  if (!fn) {
21307
22269
  console.error(import_chalk23.default.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
@@ -21323,6 +22285,7 @@ var import_chalk24 = __toESM(require("chalk"));
21323
22285
  var import_fs41 = __toESM(require("fs"));
21324
22286
  var import_path42 = __toESM(require("path"));
21325
22287
  var import_os36 = __toESM(require("os"));
22288
+ init_scan_summary();
21326
22289
  var CLAUDE_PRICING3 = {
21327
22290
  "claude-opus-4-6": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
21328
22291
  "claude-opus-4-5": { i: 5e-6, o: 25e-6, cw: 625e-8, cr: 5e-7 },
@@ -21929,7 +22892,9 @@ function renderList(summaries, totalCost) {
21929
22892
  const cost = s.costUSD > 0 ? import_chalk24.default.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
21930
22893
  const blocked = s.blockedCalls.length > 0 ? import_chalk24.default.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
21931
22894
  const snap = s.hasSnapshot ? import_chalk24.default.green(" \u{1F4F8}") : "";
21932
- const agentBadge = s.agent === "gemini" ? import_chalk24.default.blue(" [Gemini]") : s.agent === "codex" ? import_chalk24.default.magenta(" [Codex]") : import_chalk24.default.cyan(" [Claude]");
22895
+ const agentBadge = import_chalk24.default[agentColorName(s.agent ?? "claude")](
22896
+ " " + agentBadgeText(s.agent ?? "claude", 0)
22897
+ );
21933
22898
  const sid = import_chalk24.default.dim(" " + s.sessionId.slice(0, 8));
21934
22899
  console.log(
21935
22900
  ` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
@@ -21949,7 +22914,7 @@ function renderDetail(s) {
21949
22914
  );
21950
22915
  console.log(import_chalk24.default.bold(" Project ") + import_chalk24.default.white(s.projectLabel));
21951
22916
  if (s.agent) {
21952
- const agentLabel2 = s.agent === "gemini" ? import_chalk24.default.blue("Gemini CLI") : s.agent === "codex" ? import_chalk24.default.magenta("Codex") : import_chalk24.default.cyan("Claude Code");
22917
+ const agentLabel2 = import_chalk24.default[agentColorName(s.agent)](agentDisplayName(s.agent));
21953
22918
  console.log(import_chalk24.default.bold(" Agent ") + agentLabel2);
21954
22919
  }
21955
22920
  console.log(import_chalk24.default.bold(" When ") + import_chalk24.default.white(fmtDateTime(s.startTime)));
@@ -22586,12 +23551,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
22586
23551
  });
22587
23552
  program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
22588
23553
  "after",
22589
- "\n Supported targets: claude gemini cursor codex windsurf vscode hud"
23554
+ "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
22590
23555
  ).argument(
22591
23556
  "<target>",
22592
- "The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
23557
+ "The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
22593
23558
  ).action(async (target) => {
22594
23559
  if (target === "gemini") return await setupGemini();
23560
+ if (target === "antigravity" || target === "agy") return await setupAntigravity();
23561
+ if (target === "copilot") return await setupCopilot();
22595
23562
  if (target === "claude") return await setupClaude();
22596
23563
  if (target === "cursor") return await setupCursor();
22597
23564
  if (target === "codex") return await setupCodex();
@@ -22601,17 +23568,17 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
22601
23568
  if (target === "hud") return setupHud();
22602
23569
  console.error(
22603
23570
  import_chalk30.default.red(
22604
- `Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
23571
+ `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
22605
23572
  )
22606
23573
  );
22607
23574
  process.exit(1);
22608
23575
  });
22609
23576
  program.command("setup", { hidden: true }).description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
22610
23577
  "after",
22611
- "\n Supported targets: claude gemini cursor codex windsurf vscode hud"
23578
+ "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
22612
23579
  ).argument(
22613
23580
  "[target]",
22614
- "The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
23581
+ "The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
22615
23582
  ).action(async (target) => {
22616
23583
  if (!target) {
22617
23584
  console.log(import_chalk30.default.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
@@ -22619,6 +23586,8 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
22619
23586
  console.log(" Targets:");
22620
23587
  console.log(" " + import_chalk30.default.green("claude") + " \u2014 Claude Code (hook mode)");
22621
23588
  console.log(" " + import_chalk30.default.green("gemini") + " \u2014 Gemini CLI (hook mode)");
23589
+ console.log(" " + import_chalk30.default.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
23590
+ console.log(" " + import_chalk30.default.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
22622
23591
  console.log(" " + import_chalk30.default.green("cursor") + " \u2014 Cursor (MCP proxy)");
22623
23592
  console.log(" " + import_chalk30.default.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
22624
23593
  console.log(" " + import_chalk30.default.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
@@ -22632,6 +23601,8 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
22632
23601
  }
22633
23602
  const t = target.toLowerCase();
22634
23603
  if (t === "gemini") return await setupGemini();
23604
+ if (t === "antigravity" || t === "agy") return await setupAntigravity();
23605
+ if (t === "copilot") return await setupCopilot();
22635
23606
  if (t === "claude") return await setupClaude();
22636
23607
  if (t === "cursor") return await setupCursor();
22637
23608
  if (t === "codex") return await setupCodex();
@@ -22641,21 +23612,23 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
22641
23612
  if (t === "hud") return setupHud();
22642
23613
  console.error(
22643
23614
  import_chalk30.default.red(
22644
- `Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
23615
+ `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
22645
23616
  )
22646
23617
  );
22647
23618
  process.exit(1);
22648
23619
  });
22649
23620
  program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
22650
23621
  "after",
22651
- "\n Supported targets: claude gemini cursor codex windsurf vscode hud"
23622
+ "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
22652
23623
  ).argument(
22653
23624
  "<target>",
22654
- "The agent to remove from: claude | gemini | cursor | codex | windsurf | vscode | hud"
23625
+ "The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
22655
23626
  ).action((target) => {
22656
23627
  let fn;
22657
23628
  if (target === "claude") fn = teardownClaude;
22658
23629
  else if (target === "gemini") fn = teardownGemini;
23630
+ else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
23631
+ else if (target === "copilot") fn = teardownCopilot;
22659
23632
  else if (target === "cursor") fn = teardownCursor;
22660
23633
  else if (target === "codex") fn = teardownCodex;
22661
23634
  else if (target === "windsurf") fn = teardownWindsurf;
@@ -22665,7 +23638,7 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
22665
23638
  else {
22666
23639
  console.error(
22667
23640
  import_chalk30.default.red(
22668
- `Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
23641
+ `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
22669
23642
  )
22670
23643
  );
22671
23644
  process.exit(1);
@@ -22922,6 +23895,9 @@ program.command("resume").description("Re-enable Node9 protection immediately").
22922
23895
  var HOOK_BASED_AGENTS = {
22923
23896
  claude: "claude",
22924
23897
  gemini: "gemini",
23898
+ antigravity: "antigravity",
23899
+ agy: "antigravity",
23900
+ copilot: "copilot",
22925
23901
  cursor: "cursor"
22926
23902
  };
22927
23903
  program.argument("[command...]", "The agent command to run (e.g., gemini)").action(async (commandArgs) => {