@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.mjs CHANGED
@@ -7172,6 +7172,12 @@ async function setupClaude() {
7172
7172
  }
7173
7173
  }
7174
7174
  async function setupGemini() {
7175
+ console.log(
7176
+ chalk.yellow(
7177
+ " \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"
7178
+ )
7179
+ );
7180
+ console.log("");
7175
7181
  seedMcpPinsIfMissing();
7176
7182
  const homeDir2 = os12.homedir();
7177
7183
  const settingsPath = path15.join(homeDir2, ".gemini", "settings.json");
@@ -7269,6 +7275,355 @@ async function setupGemini() {
7269
7275
  printDaemonTip();
7270
7276
  }
7271
7277
  }
7278
+ async function setupAntigravity() {
7279
+ seedMcpPinsIfMissing();
7280
+ const homeDir2 = os12.homedir();
7281
+ const hooksPath = path15.join(homeDir2, ".gemini", "config", "hooks.json");
7282
+ const mcpPath = path15.join(homeDir2, ".gemini", "config", "mcp_config.json");
7283
+ const hooksFile = readJson(hooksPath) ?? {};
7284
+ const mcpConfig = readJson(mcpPath) ?? {};
7285
+ const servers = mcpConfig.mcpServers ?? {};
7286
+ let hooksChanged = false;
7287
+ let anythingChanged = false;
7288
+ if (!hooksFile.hooks) hooksFile.hooks = {};
7289
+ const hasPreHook = hooksFile.hooks.PreToolUse?.some(
7290
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7291
+ );
7292
+ if (!hasPreHook) {
7293
+ if (!Array.isArray(hooksFile.hooks.PreToolUse)) hooksFile.hooks.PreToolUse = [];
7294
+ hooksFile.hooks.PreToolUse.push({
7295
+ matcher: ".*",
7296
+ hooks: [
7297
+ {
7298
+ name: "node9-check",
7299
+ type: "command",
7300
+ command: fullPathCommand("check --agent antigravity"),
7301
+ timeout: 600
7302
+ }
7303
+ ]
7304
+ });
7305
+ console.log(chalk.green(" \u2705 PreToolUse hook added \u2192 node9 check"));
7306
+ hooksChanged = true;
7307
+ anythingChanged = true;
7308
+ } else if (hooksFile.hooks.PreToolUse) {
7309
+ for (const matcher of hooksFile.hooks.PreToolUse) {
7310
+ for (const h of matcher.hooks) {
7311
+ const cmd = h.command ?? "";
7312
+ if (isNode9Hook(cmd) && needsRewrite(cmd)) {
7313
+ h.command = fullPathCommand("check --agent antigravity");
7314
+ console.log(chalk.yellow(" \u{1F527} PreToolUse hook repaired (stale path \u2192 current binary)"));
7315
+ hooksChanged = true;
7316
+ anythingChanged = true;
7317
+ }
7318
+ }
7319
+ }
7320
+ }
7321
+ const hasPostHook = hooksFile.hooks.PostToolUse?.some(
7322
+ (m) => m.hooks.some((h) => isNode9Hook(h.command))
7323
+ );
7324
+ if (!hasPostHook) {
7325
+ if (!Array.isArray(hooksFile.hooks.PostToolUse)) hooksFile.hooks.PostToolUse = [];
7326
+ hooksFile.hooks.PostToolUse.push({
7327
+ matcher: ".*",
7328
+ hooks: [
7329
+ {
7330
+ name: "node9-log",
7331
+ type: "command",
7332
+ command: fullPathCommand("log --agent antigravity"),
7333
+ timeout: 600
7334
+ }
7335
+ ]
7336
+ });
7337
+ console.log(chalk.green(" \u2705 PostToolUse hook added \u2192 node9 log"));
7338
+ hooksChanged = true;
7339
+ anythingChanged = true;
7340
+ } else if (hooksFile.hooks.PostToolUse) {
7341
+ for (const matcher of hooksFile.hooks.PostToolUse) {
7342
+ for (const h of matcher.hooks) {
7343
+ const cmd = h.command ?? "";
7344
+ if (isNode9Hook(cmd) && needsRewrite(cmd)) {
7345
+ h.command = fullPathCommand("log --agent antigravity");
7346
+ console.log(chalk.yellow(" \u{1F527} PostToolUse hook repaired (stale path \u2192 current binary)"));
7347
+ hooksChanged = true;
7348
+ anythingChanged = true;
7349
+ }
7350
+ }
7351
+ }
7352
+ }
7353
+ if (hooksChanged) {
7354
+ writeJson(hooksPath, hooksFile);
7355
+ console.log("");
7356
+ }
7357
+ if (!hasNode9McpServer(servers)) {
7358
+ servers["node9"] = NODE9_MCP_SERVER_ENTRY;
7359
+ mcpConfig.mcpServers = servers;
7360
+ writeJson(mcpPath, mcpConfig);
7361
+ console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
7362
+ anythingChanged = true;
7363
+ }
7364
+ const legacySettings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
7365
+ const legacyHasNode9 = ["BeforeTool", "AfterTool"].some(
7366
+ (ev) => legacySettings?.hooks?.[ev]?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)))
7367
+ );
7368
+ if (legacyHasNode9) {
7369
+ console.log(
7370
+ chalk.yellow(
7371
+ " \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)."
7372
+ )
7373
+ );
7374
+ const clean = await confirm({
7375
+ message: "Remove the legacy Gemini CLI hooks?",
7376
+ default: false
7377
+ });
7378
+ if (clean) {
7379
+ teardownGemini();
7380
+ anythingChanged = true;
7381
+ }
7382
+ console.log("");
7383
+ }
7384
+ const serversToWrap = [];
7385
+ for (const [name, server] of Object.entries(servers)) {
7386
+ if (!server.command || server.command === "node9") continue;
7387
+ serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
7388
+ }
7389
+ if (serversToWrap.length > 0) {
7390
+ console.log(chalk.bold("The following existing entries will be modified:\n"));
7391
+ console.log(chalk.white(` ${mcpPath}`));
7392
+ for (const { name, upstream } of serversToWrap) {
7393
+ console.log(chalk.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
7394
+ }
7395
+ console.log("");
7396
+ const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
7397
+ if (proceed) {
7398
+ for (const { name, upstream } of serversToWrap) {
7399
+ servers[name] = {
7400
+ ...servers[name],
7401
+ command: "node9",
7402
+ args: ["mcp", "--upstream", upstream]
7403
+ };
7404
+ }
7405
+ mcpConfig.mcpServers = servers;
7406
+ writeJson(mcpPath, mcpConfig);
7407
+ console.log(chalk.green(`
7408
+ \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
7409
+ anythingChanged = true;
7410
+ } else {
7411
+ console.log(chalk.yellow(" Skipped MCP server wrapping."));
7412
+ }
7413
+ console.log("");
7414
+ }
7415
+ if (!anythingChanged && serversToWrap.length === 0) {
7416
+ console.log(chalk.blue("\u2139\uFE0F Node9 is already fully configured for Antigravity."));
7417
+ printDaemonTip();
7418
+ return;
7419
+ }
7420
+ if (anythingChanged) {
7421
+ console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting Antigravity!"));
7422
+ console.log(
7423
+ chalk.gray(
7424
+ " Covers both the agy CLI and the Antigravity IDE (shared ~/.gemini/config).\n Restart agy / the IDE for changes to take effect."
7425
+ )
7426
+ );
7427
+ printDaemonTip();
7428
+ }
7429
+ }
7430
+ function teardownAntigravity() {
7431
+ const homeDir2 = os12.homedir();
7432
+ const hooksPath = path15.join(homeDir2, ".gemini", "config", "hooks.json");
7433
+ const mcpPath = path15.join(homeDir2, ".gemini", "config", "mcp_config.json");
7434
+ let changed = false;
7435
+ const hooksFile = readJson(hooksPath);
7436
+ if (hooksFile?.hooks) {
7437
+ for (const event of ["PreToolUse", "PostToolUse"]) {
7438
+ const before = hooksFile.hooks[event]?.length ?? 0;
7439
+ hooksFile.hooks[event] = hooksFile.hooks[event]?.filter(
7440
+ (m) => !m.hooks.some((h) => isNode9Hook(h.command))
7441
+ );
7442
+ if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
7443
+ if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
7444
+ }
7445
+ if (changed) {
7446
+ writeJson(hooksPath, hooksFile);
7447
+ console.log(
7448
+ chalk.green(" \u2705 Removed PreToolUse / PostToolUse hooks from ~/.gemini/config/hooks.json")
7449
+ );
7450
+ } else {
7451
+ console.log(chalk.blue(" \u2139\uFE0F No Node9 hooks found in ~/.gemini/config/hooks.json"));
7452
+ }
7453
+ } else {
7454
+ console.log(chalk.blue(" \u2139\uFE0F ~/.gemini/config/hooks.json not found \u2014 nothing to remove"));
7455
+ }
7456
+ const mcpConfig = readJson(mcpPath);
7457
+ if (mcpConfig?.mcpServers) {
7458
+ let mcpChanged = false;
7459
+ if (removeNode9McpServer(mcpConfig.mcpServers)) {
7460
+ mcpChanged = true;
7461
+ console.log(
7462
+ chalk.green(" \u2705 Removed node9 MCP server entry from ~/.gemini/config/mcp_config.json")
7463
+ );
7464
+ }
7465
+ for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
7466
+ const args = server.args;
7467
+ if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
7468
+ const [originalCmd, ...originalArgs] = args[2].split(" ");
7469
+ mcpConfig.mcpServers[name] = {
7470
+ ...server,
7471
+ command: originalCmd,
7472
+ args: originalArgs.length ? originalArgs : void 0
7473
+ };
7474
+ mcpChanged = true;
7475
+ }
7476
+ }
7477
+ if (mcpChanged) {
7478
+ writeJson(mcpPath, mcpConfig);
7479
+ console.log(chalk.green(" \u2705 Unwrapped MCP servers in ~/.gemini/config/mcp_config.json"));
7480
+ }
7481
+ }
7482
+ }
7483
+ async function setupCopilot() {
7484
+ seedMcpPinsIfMissing();
7485
+ const homeDir2 = os12.homedir();
7486
+ const hooksPath = path15.join(homeDir2, ".copilot", "hooks", "node9.json");
7487
+ const mcpPath = path15.join(homeDir2, ".copilot", "mcp-config.json");
7488
+ const hooksFile = readJson(hooksPath) ?? { version: 1 };
7489
+ if (!hooksFile.version) hooksFile.version = 1;
7490
+ if (!hooksFile.hooks) hooksFile.hooks = {};
7491
+ const mcpConfig = readJson(mcpPath) ?? {};
7492
+ const servers = mcpConfig.mcpServers ?? {};
7493
+ let hooksChanged = false;
7494
+ let anythingChanged = false;
7495
+ const addHook = (event, subcommand) => {
7496
+ const arr = Array.isArray(hooksFile.hooks[event]) ? hooksFile.hooks[event] : [];
7497
+ const present = arr.some((h) => isNode9Hook(h.command));
7498
+ if (!present) {
7499
+ arr.push({ type: "command", command: fullPathCommand(subcommand), timeoutSec: 600 });
7500
+ hooksFile.hooks[event] = arr;
7501
+ console.log(chalk.green(` \u2705 ${event} hook added \u2192 node9 ${subcommand.split(" ")[0]}`));
7502
+ hooksChanged = true;
7503
+ anythingChanged = true;
7504
+ } else {
7505
+ for (const h of arr) {
7506
+ if (h.command && isNode9Hook(h.command) && needsRewrite(h.command)) {
7507
+ h.command = fullPathCommand(subcommand);
7508
+ console.log(chalk.yellow(` \u{1F527} ${event} hook repaired (stale path \u2192 current binary)`));
7509
+ hooksChanged = true;
7510
+ anythingChanged = true;
7511
+ }
7512
+ }
7513
+ }
7514
+ };
7515
+ addHook("PreToolUse", "check --agent copilot");
7516
+ addHook("PostToolUse", "log --agent copilot");
7517
+ addHook("UserPromptSubmit", "check --agent copilot");
7518
+ if (hooksChanged) {
7519
+ writeJson(hooksPath, hooksFile);
7520
+ console.log("");
7521
+ }
7522
+ if (!hasNode9McpServer(servers)) {
7523
+ servers["node9"] = NODE9_MCP_SERVER_ENTRY;
7524
+ mcpConfig.mcpServers = servers;
7525
+ writeJson(mcpPath, mcpConfig);
7526
+ console.log(chalk.green(" \u2705 node9 MCP server added \u2192 node9 mcp-server"));
7527
+ anythingChanged = true;
7528
+ }
7529
+ const serversToWrap = [];
7530
+ for (const [name, server] of Object.entries(servers)) {
7531
+ if (!server.command || server.command === "node9") continue;
7532
+ serversToWrap.push({ name, upstream: [server.command, ...server.args ?? []].join(" ") });
7533
+ }
7534
+ if (serversToWrap.length > 0) {
7535
+ console.log(chalk.bold("The following existing entries will be modified:\n"));
7536
+ console.log(chalk.white(` ${mcpPath}`));
7537
+ for (const { name, upstream } of serversToWrap) {
7538
+ console.log(chalk.gray(` \u2022 ${name}: "${upstream}" \u2192 node9 mcp --upstream "${upstream}"`));
7539
+ }
7540
+ console.log("");
7541
+ const proceed = await confirm({ message: "Wrap these MCP servers?", default: true });
7542
+ if (proceed) {
7543
+ for (const { name, upstream } of serversToWrap) {
7544
+ servers[name] = {
7545
+ ...servers[name],
7546
+ command: "node9",
7547
+ args: ["mcp", "--upstream", upstream]
7548
+ };
7549
+ }
7550
+ mcpConfig.mcpServers = servers;
7551
+ writeJson(mcpPath, mcpConfig);
7552
+ console.log(chalk.green(`
7553
+ \u2705 ${serversToWrap.length} MCP server(s) wrapped`));
7554
+ anythingChanged = true;
7555
+ } else {
7556
+ console.log(chalk.yellow(" Skipped MCP server wrapping."));
7557
+ }
7558
+ console.log("");
7559
+ }
7560
+ if (!anythingChanged) {
7561
+ console.log(chalk.blue("\u2139\uFE0F Node9 is already fully configured for GitHub Copilot CLI."));
7562
+ printDaemonTip();
7563
+ return;
7564
+ }
7565
+ console.log(chalk.green.bold("\u{1F6E1}\uFE0F Node9 is now protecting GitHub Copilot CLI!"));
7566
+ console.log(chalk.gray(" Restart Copilot CLI for changes to take effect."));
7567
+ printDaemonTip();
7568
+ }
7569
+ function teardownCopilot() {
7570
+ const homeDir2 = os12.homedir();
7571
+ const hooksPath = path15.join(homeDir2, ".copilot", "hooks", "node9.json");
7572
+ const mcpPath = path15.join(homeDir2, ".copilot", "mcp-config.json");
7573
+ const hooksFile = readJson(hooksPath);
7574
+ let changed = false;
7575
+ if (hooksFile?.hooks) {
7576
+ for (const event of ["PreToolUse", "PostToolUse", "UserPromptSubmit"]) {
7577
+ const before = hooksFile.hooks[event]?.length ?? 0;
7578
+ hooksFile.hooks[event] = hooksFile.hooks[event]?.filter((h) => !isNode9Hook(h.command));
7579
+ if ((hooksFile.hooks[event]?.length ?? 0) < before) changed = true;
7580
+ if (hooksFile.hooks[event]?.length === 0) delete hooksFile.hooks[event];
7581
+ }
7582
+ if (changed) {
7583
+ if (Object.keys(hooksFile.hooks).length === 0) {
7584
+ try {
7585
+ fs13.unlinkSync(hooksPath);
7586
+ console.log(chalk.green(" \u2705 Removed ~/.copilot/hooks/node9.json"));
7587
+ } catch {
7588
+ writeJson(hooksPath, hooksFile);
7589
+ }
7590
+ } else {
7591
+ writeJson(hooksPath, hooksFile);
7592
+ console.log(chalk.green(" \u2705 Removed Node9 hooks from ~/.copilot/hooks/node9.json"));
7593
+ }
7594
+ } else {
7595
+ console.log(chalk.blue(" \u2139\uFE0F No Node9 hooks found in ~/.copilot/hooks/node9.json"));
7596
+ }
7597
+ } else {
7598
+ console.log(chalk.blue(" \u2139\uFE0F ~/.copilot/hooks/node9.json not found \u2014 nothing to remove"));
7599
+ }
7600
+ const mcpConfig = readJson(mcpPath);
7601
+ if (mcpConfig?.mcpServers) {
7602
+ let mcpChanged = false;
7603
+ if (removeNode9McpServer(mcpConfig.mcpServers)) {
7604
+ mcpChanged = true;
7605
+ console.log(
7606
+ chalk.green(" \u2705 Removed node9 MCP server entry from ~/.copilot/mcp-config.json")
7607
+ );
7608
+ }
7609
+ for (const [name, server] of Object.entries(mcpConfig.mcpServers)) {
7610
+ const args = server.args;
7611
+ if (server.command === "node9" && Array.isArray(args) && args[0] === "mcp" && args[1] === "--upstream" && typeof args[2] === "string") {
7612
+ const [originalCmd, ...originalArgs] = args[2].split(" ");
7613
+ mcpConfig.mcpServers[name] = {
7614
+ ...server,
7615
+ command: originalCmd,
7616
+ args: originalArgs.length ? originalArgs : void 0
7617
+ };
7618
+ mcpChanged = true;
7619
+ }
7620
+ }
7621
+ if (mcpChanged) {
7622
+ writeJson(mcpPath, mcpConfig);
7623
+ console.log(chalk.green(" \u2705 Unwrapped MCP servers in ~/.copilot/mcp-config.json"));
7624
+ }
7625
+ }
7626
+ }
7272
7627
  function claudeDesktopConfigPath(homeDir2 = os12.homedir()) {
7273
7628
  if (process.platform === "darwin") {
7274
7629
  return path15.join(
@@ -7316,7 +7671,23 @@ function detectAgents(homeDir2 = os12.homedir()) {
7316
7671
  const desktopPath = claudeDesktopConfigPath(homeDir2);
7317
7672
  return {
7318
7673
  claude: exists(path15.join(homeDir2, ".claude")) || exists(path15.join(homeDir2, ".claude.json")),
7319
- gemini: exists(path15.join(homeDir2, ".gemini")),
7674
+ // Antigravity (agy) shares the ~/.gemini root, so a bare
7675
+ // `exists(~/.gemini)` would report the (EOL'd) Gemini CLI as
7676
+ // installed on every agy machine — and `node9 init` would then
7677
+ // write BeforeTool hooks into settings.json, a file agy ignores,
7678
+ // while reporting the machine protected (silent protection gap,
7679
+ // verified live on a machine with both installed). Gemini CLI
7680
+ // creates ~/.gemini/settings.json on first run; agy does not touch
7681
+ // it (it uses antigravity-cli/settings.json) — that file is the
7682
+ // legacy-CLI discriminator.
7683
+ gemini: exists(path15.join(homeDir2, ".gemini", "settings.json")) || binaryInPath("gemini"),
7684
+ // agy creates ~/.gemini/antigravity-cli/ on first launch; the IDE
7685
+ // creates antigravity-ide/. PATH fallback covers installed-but-
7686
+ // never-launched (same class as opencode #186).
7687
+ antigravity: exists(path15.join(homeDir2, ".gemini", "antigravity-cli")) || exists(path15.join(homeDir2, ".gemini", "antigravity-ide")) || binaryInPath("agy"),
7688
+ // GitHub Copilot CLI creates ~/.copilot on first launch; PATH
7689
+ // fallback covers installed-but-never-launched (same as opencode #186).
7690
+ copilot: exists(path15.join(homeDir2, ".copilot")) || binaryInPath("copilot"),
7320
7691
  cursor: exists(path15.join(homeDir2, ".cursor")),
7321
7692
  codex: exists(path15.join(homeDir2, ".codex")),
7322
7693
  windsurf: exists(path15.join(homeDir2, ".codeium", "windsurf")),
@@ -8317,6 +8688,18 @@ function getAgentsStatus(homeDir2 = os12.homedir()) {
8317
8688
  const settings = readJson(path15.join(homeDir2, ".gemini", "settings.json"));
8318
8689
  return !!settings?.hooks?.BeforeTool?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
8319
8690
  })();
8691
+ const antigravityWired = (() => {
8692
+ const hooksFile = readJson(
8693
+ path15.join(homeDir2, ".gemini", "config", "hooks.json")
8694
+ );
8695
+ return !!hooksFile?.hooks?.PreToolUse?.some((m) => m.hooks.some((h) => isNode9Hook(h.command)));
8696
+ })();
8697
+ const copilotWired = (() => {
8698
+ const hooksFile = readJson(
8699
+ path15.join(homeDir2, ".copilot", "hooks", "node9.json")
8700
+ );
8701
+ return !!hooksFile?.hooks?.PreToolUse?.some((h) => isNode9Hook(h.command));
8702
+ })();
8320
8703
  const cursorWired = (() => {
8321
8704
  const cfg = readJson(path15.join(homeDir2, ".cursor", "mcp.json"));
8322
8705
  return !!(cfg?.mcpServers && hasNode9McpServer(cfg.mcpServers));
@@ -8350,6 +8733,20 @@ function getAgentsStatus(homeDir2 = os12.homedir()) {
8350
8733
  wired: geminiWired,
8351
8734
  mode: detected.gemini ? "hooks" : null
8352
8735
  },
8736
+ {
8737
+ name: "antigravity",
8738
+ label: "Antigravity",
8739
+ installed: detected.antigravity,
8740
+ wired: antigravityWired,
8741
+ mode: detected.antigravity ? "hooks" : null
8742
+ },
8743
+ {
8744
+ name: "copilot",
8745
+ label: "GitHub Copilot",
8746
+ installed: detected.copilot,
8747
+ wired: copilotWired,
8748
+ mode: detected.copilot ? "hooks" : null
8749
+ },
8353
8750
  {
8354
8751
  name: "cursor",
8355
8752
  label: "Cursor",
@@ -8460,7 +8857,84 @@ var init_setup = __esm({
8460
8857
  }
8461
8858
  });
8462
8859
 
8860
+ // src/utils/hook-payload.ts
8861
+ function extractToolName(payload, defaultValue = "") {
8862
+ return payload.tool_name ?? payload.name ?? payload.toolCall?.name ?? defaultValue;
8863
+ }
8864
+ function extractToolInput(payload) {
8865
+ return payload.tool_input ?? payload.args ?? payload.toolCall?.args ?? {};
8866
+ }
8867
+ function canonicalToolName(name) {
8868
+ switch (name) {
8869
+ // Hermes Agent
8870
+ case "terminal":
8871
+ return "Bash";
8872
+ case "write_file":
8873
+ return "Write";
8874
+ case "patch":
8875
+ return "Edit";
8876
+ case "read_file":
8877
+ return "Read";
8878
+ case "search_files":
8879
+ return "Grep";
8880
+ // Antigravity (agy) — shell tool renamed from Gemini's run_shell_command
8881
+ case "run_command":
8882
+ return "Bash";
8883
+ default:
8884
+ return name;
8885
+ }
8886
+ }
8887
+ function agentLabelFromFlag(flag) {
8888
+ if (typeof flag !== "string") return void 0;
8889
+ switch (flag.toLowerCase()) {
8890
+ case "antigravity":
8891
+ case "agy":
8892
+ return "Antigravity";
8893
+ case "copilot":
8894
+ return "GitHub Copilot";
8895
+ default:
8896
+ return void 0;
8897
+ }
8898
+ }
8899
+ function canonicalToolInput(rawToolName, input) {
8900
+ if (rawToolName !== "run_command") return input;
8901
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return input;
8902
+ const args = input;
8903
+ if (typeof args.CommandLine !== "string") return input;
8904
+ const { CommandLine, Cwd, ...rest } = args;
8905
+ const canonical = { ...rest, command: CommandLine };
8906
+ if (typeof Cwd === "string" && Cwd.length > 0) canonical.cwd = Cwd;
8907
+ return canonical;
8908
+ }
8909
+ var init_hook_payload = __esm({
8910
+ "src/utils/hook-payload.ts"() {
8911
+ "use strict";
8912
+ }
8913
+ });
8914
+
8463
8915
  // src/scan-summary.ts
8916
+ function agentDisplayName(agent) {
8917
+ return AGENT_LONG[agent] ?? "Claude Code";
8918
+ }
8919
+ function agentBadgeText(agent, width = 10) {
8920
+ return `[${AGENT_SHORT[agent] ?? "Claude"}]`.padEnd(width);
8921
+ }
8922
+ function agentColorName(agent) {
8923
+ switch (agent) {
8924
+ case "gemini":
8925
+ return "blue";
8926
+ case "codex":
8927
+ return "magenta";
8928
+ case "antigravity":
8929
+ return "yellow";
8930
+ case "copilot":
8931
+ return "green";
8932
+ case "shell":
8933
+ return "yellow";
8934
+ default:
8935
+ return "cyan";
8936
+ }
8937
+ }
8464
8938
  function buildScanSummary(agents) {
8465
8939
  const stats = {
8466
8940
  sessions: 0,
@@ -8637,12 +9111,29 @@ function fullCommandOf(input) {
8637
9111
  const raw = input.command ?? input.query ?? input.file_path ?? JSON.stringify(input);
8638
9112
  return String(raw).replace(/\s+/g, " ").trim();
8639
9113
  }
9114
+ var AGENT_SHORT, AGENT_LONG;
8640
9115
  var init_scan_summary = __esm({
8641
9116
  "src/scan-summary.ts"() {
8642
9117
  "use strict";
8643
9118
  init_shields();
8644
9119
  init_dist();
8645
9120
  init_dist();
9121
+ AGENT_SHORT = {
9122
+ claude: "Claude",
9123
+ gemini: "Gemini",
9124
+ codex: "Codex",
9125
+ antigravity: "Agy",
9126
+ copilot: "Copilot",
9127
+ shell: "Shell"
9128
+ };
9129
+ AGENT_LONG = {
9130
+ claude: "Claude Code",
9131
+ gemini: "Gemini CLI",
9132
+ codex: "Codex",
9133
+ antigravity: "Antigravity",
9134
+ copilot: "GitHub Copilot",
9135
+ shell: "Shell"
9136
+ };
8646
9137
  }
8647
9138
  });
8648
9139
 
@@ -10287,6 +10778,34 @@ function countScanFiles() {
10287
10778
  } catch {
10288
10779
  }
10289
10780
  }
10781
+ for (const surface of ["antigravity-cli", "antigravity-ide"]) {
10782
+ const brainDir = path22.join(os19.homedir(), ".gemini", surface, "brain");
10783
+ if (!fs20.existsSync(brainDir)) continue;
10784
+ try {
10785
+ for (const conv of fs20.readdirSync(brainDir)) {
10786
+ const convPath = path22.join(brainDir, conv);
10787
+ try {
10788
+ if (!fs20.statSync(convPath).isDirectory()) continue;
10789
+ const logsDir = path22.join(convPath, ".system_generated", "logs");
10790
+ if (fs20.existsSync(path22.join(logsDir, "transcript_full.jsonl")) || fs20.existsSync(path22.join(logsDir, "transcript.jsonl"))) {
10791
+ total += 1;
10792
+ }
10793
+ } catch {
10794
+ continue;
10795
+ }
10796
+ }
10797
+ } catch {
10798
+ }
10799
+ }
10800
+ const copilotDir = path22.join(os19.homedir(), ".copilot", "session-state");
10801
+ if (fs20.existsSync(copilotDir)) {
10802
+ try {
10803
+ for (const sid of fs20.readdirSync(copilotDir)) {
10804
+ if (fs20.existsSync(path22.join(copilotDir, sid, "events.jsonl"))) total += 1;
10805
+ }
10806
+ } catch {
10807
+ }
10808
+ }
10290
10809
  const codexDir = path22.join(os19.homedir(), ".codex", "sessions");
10291
10810
  if (fs20.existsSync(codexDir)) {
10292
10811
  try {
@@ -10657,33 +11176,248 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10657
11176
  if (!fs20.existsSync(chatsDir)) continue;
10658
11177
  let chatFiles;
10659
11178
  try {
10660
- chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
11179
+ chatFiles = fs20.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
11180
+ } catch {
11181
+ continue;
11182
+ }
11183
+ for (const chatFile of chatFiles) {
11184
+ result.filesScanned++;
11185
+ onProgress?.(result.filesScanned);
11186
+ const sessionId = chatFile.replace(/\.json$/, "");
11187
+ let raw;
11188
+ try {
11189
+ raw = fs20.readFileSync(path22.join(chatsDir, chatFile), "utf-8");
11190
+ } catch {
11191
+ continue;
11192
+ }
11193
+ const sessionCalls = [];
11194
+ let session;
11195
+ try {
11196
+ session = JSON.parse(raw);
11197
+ } catch {
11198
+ continue;
11199
+ }
11200
+ result.sessions++;
11201
+ for (const msg of session.messages ?? []) {
11202
+ onLine?.();
11203
+ if (msg.type === "user") {
11204
+ const content = msg.content;
11205
+ const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
11206
+ if (text) {
11207
+ const dlpMatch = scanArgs({ text });
11208
+ if (dlpMatch) {
11209
+ const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
11210
+ if (!dedup.dlpKeys.has(k)) {
11211
+ dedup.dlpKeys.add(k);
11212
+ result.dlpFindings.push({
11213
+ patternName: dlpMatch.patternName,
11214
+ redactedSample: dlpMatch.redactedSample,
11215
+ toolName: "user-prompt",
11216
+ timestamp: msg.timestamp ?? "",
11217
+ project: projLabel,
11218
+ sessionId,
11219
+ agent: "gemini"
11220
+ });
11221
+ }
11222
+ }
11223
+ }
11224
+ continue;
11225
+ }
11226
+ if (msg.type !== "gemini") continue;
11227
+ if (startDate && msg.timestamp && new Date(msg.timestamp) < startDate) continue;
11228
+ if (msg.timestamp) {
11229
+ if (!result.firstDate || msg.timestamp < result.firstDate)
11230
+ result.firstDate = msg.timestamp;
11231
+ if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
11232
+ }
11233
+ const tokens = msg.tokens;
11234
+ const model = msg.model;
11235
+ if (tokens && model) {
11236
+ const p = geminiModelPrice(model);
11237
+ if (p) {
11238
+ const nonCached = Math.max(0, tokens.input - tokens.cached);
11239
+ result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
11240
+ }
11241
+ }
11242
+ for (const tc of msg.toolCalls ?? []) {
11243
+ result.totalToolCalls++;
11244
+ const toolName = tc.name ?? "";
11245
+ const toolNameLower = toolName.toLowerCase();
11246
+ const input = tc.args ?? {};
11247
+ sessionCalls.push({ toolName, input, timestamp: msg.timestamp ?? "" });
11248
+ if (toolNameLower === "run_shell_command" || toolNameLower === "shell") {
11249
+ result.bashCalls++;
11250
+ }
11251
+ const rawCmd = String(input.command ?? "").trimStart();
11252
+ if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
11253
+ continue;
11254
+ const dlpMatch = scanArgs(input);
11255
+ if (dlpMatch) {
11256
+ const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
11257
+ if (!dedup.dlpKeys.has(k)) {
11258
+ dedup.dlpKeys.add(k);
11259
+ result.dlpFindings.push({
11260
+ patternName: dlpMatch.patternName,
11261
+ redactedSample: dlpMatch.redactedSample,
11262
+ toolName,
11263
+ timestamp: msg.timestamp ?? "",
11264
+ project: projLabel,
11265
+ sessionId,
11266
+ agent: "gemini"
11267
+ });
11268
+ }
11269
+ }
11270
+ let astFsMatched = false;
11271
+ const astRanForBash = toolNameLower === "run_shell_command" || toolNameLower === "shell";
11272
+ if (astRanForBash) {
11273
+ astFsMatched = pushFsOpAstFinding(
11274
+ String(input.command ?? ""),
11275
+ toolName,
11276
+ input,
11277
+ msg.timestamp ?? "",
11278
+ projLabel,
11279
+ sessionId,
11280
+ "gemini",
11281
+ result,
11282
+ dedup
11283
+ );
11284
+ }
11285
+ let ruleMatched = astFsMatched;
11286
+ for (const source of ruleSources) {
11287
+ const { rule } = source;
11288
+ if (rule.verdict === "allow") continue;
11289
+ if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
11290
+ if (astRanForBash && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
11291
+ if (!evaluateSmartConditions(input, rule)) continue;
11292
+ const inputPreview = preview(input, 120);
11293
+ const k = findingKey(rule.name, inputPreview, projLabel);
11294
+ if (!dedup.findingsKeys.has(k)) {
11295
+ dedup.findingsKeys.add(k);
11296
+ result.findings.push({
11297
+ source,
11298
+ toolName,
11299
+ input,
11300
+ timestamp: msg.timestamp ?? "",
11301
+ project: projLabel,
11302
+ sessionId,
11303
+ agent: "gemini"
11304
+ });
11305
+ }
11306
+ ruleMatched = true;
11307
+ break;
11308
+ }
11309
+ const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
11310
+ toolNameLower
11311
+ );
11312
+ if (!ruleMatched && isShellTool) {
11313
+ const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
11314
+ if (shellVerdict) {
11315
+ const astRule = {
11316
+ name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
11317
+ tool: "bash",
11318
+ conditions: [],
11319
+ verdict: shellVerdict,
11320
+ reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
11321
+ };
11322
+ const inputPreview = preview(input, 120);
11323
+ const k = findingKey(astRule.name, inputPreview, projLabel);
11324
+ if (!dedup.findingsKeys.has(k)) {
11325
+ dedup.findingsKeys.add(k);
11326
+ result.findings.push({
11327
+ source: {
11328
+ shieldName: "bash-safe",
11329
+ shieldLabel: "bash-safe (AST)",
11330
+ sourceType: "shield",
11331
+ rule: astRule
11332
+ },
11333
+ toolName,
11334
+ input,
11335
+ timestamp: msg.timestamp ?? "",
11336
+ project: projLabel,
11337
+ sessionId,
11338
+ agent: "gemini"
11339
+ });
11340
+ }
11341
+ }
11342
+ }
11343
+ }
11344
+ }
11345
+ result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "gemini"));
11346
+ }
11347
+ }
11348
+ return result;
11349
+ }
11350
+ function antigravityBrainDirs() {
11351
+ return ["antigravity-cli", "antigravity-ide"].map((surface) => path22.join(os19.homedir(), ".gemini", surface, "brain")).filter((p) => fs20.existsSync(p));
11352
+ }
11353
+ function antigravityTranscriptPath(convPath) {
11354
+ const logsDir = path22.join(convPath, ".system_generated", "logs");
11355
+ for (const name of ["transcript_full.jsonl", "transcript.jsonl"]) {
11356
+ const p = path22.join(logsDir, name);
11357
+ if (fs20.existsSync(p)) return p;
11358
+ }
11359
+ return null;
11360
+ }
11361
+ function scanAntigravityHistory(startDate, onProgress, onLine) {
11362
+ const result = {
11363
+ filesScanned: 0,
11364
+ sessions: 0,
11365
+ totalToolCalls: 0,
11366
+ bashCalls: 0,
11367
+ findings: [],
11368
+ dlpFindings: [],
11369
+ loopFindings: [],
11370
+ totalCostUSD: 0,
11371
+ // transcripts carry no token/model data
11372
+ firstDate: null,
11373
+ lastDate: null,
11374
+ sessionsWithEarlySecrets: 0
11375
+ };
11376
+ const dedup = emptyScanDedup();
11377
+ const brainDirs = antigravityBrainDirs();
11378
+ if (brainDirs.length === 0) return result;
11379
+ const ruleSources = buildRuleSources();
11380
+ for (const brainDir of brainDirs) {
11381
+ let convDirs;
11382
+ try {
11383
+ convDirs = fs20.readdirSync(brainDir);
10661
11384
  } catch {
10662
11385
  continue;
10663
11386
  }
10664
- for (const chatFile of chatFiles) {
10665
- result.filesScanned++;
10666
- onProgress?.(result.filesScanned);
10667
- const sessionId = chatFile.replace(/\.json$/, "");
10668
- let raw;
11387
+ for (const conv of convDirs) {
11388
+ const convPath = path22.join(brainDir, conv);
10669
11389
  try {
10670
- raw = fs20.readFileSync(path22.join(chatsDir, chatFile), "utf-8");
11390
+ if (!fs20.statSync(convPath).isDirectory()) continue;
10671
11391
  } catch {
10672
11392
  continue;
10673
11393
  }
10674
- const sessionCalls = [];
10675
- let session;
11394
+ const transcriptFile = antigravityTranscriptPath(convPath);
11395
+ if (!transcriptFile) continue;
11396
+ result.filesScanned++;
11397
+ onProgress?.(result.filesScanned);
11398
+ let raw;
10676
11399
  try {
10677
- session = JSON.parse(raw);
11400
+ raw = fs20.readFileSync(transcriptFile, "utf-8");
10678
11401
  } catch {
10679
11402
  continue;
10680
11403
  }
11404
+ const sessionId = conv;
11405
+ let projLabel = conv.slice(0, 8);
11406
+ const sessionCalls = [];
10681
11407
  result.sessions++;
10682
- for (const msg of session.messages ?? []) {
11408
+ for (const line of raw.split("\n")) {
11409
+ if (!line.trim()) continue;
10683
11410
  onLine?.();
10684
- if (msg.type === "user") {
10685
- const content = msg.content;
10686
- const text = Array.isArray(content) ? content.map((c) => c.text ?? "").join("\n") : typeof content === "string" ? content : "";
11411
+ let step;
11412
+ try {
11413
+ step = JSON.parse(line);
11414
+ } catch {
11415
+ continue;
11416
+ }
11417
+ const timestamp = step.created_at ?? "";
11418
+ if (startDate && timestamp && new Date(timestamp) < startDate) continue;
11419
+ if (step.type === "USER_INPUT") {
11420
+ const text = typeof step.content === "string" ? step.content : "";
10687
11421
  if (text) {
10688
11422
  const dlpMatch = scanArgs({ text });
10689
11423
  if (dlpMatch) {
@@ -10694,40 +11428,34 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10694
11428
  patternName: dlpMatch.patternName,
10695
11429
  redactedSample: dlpMatch.redactedSample,
10696
11430
  toolName: "user-prompt",
10697
- timestamp: msg.timestamp ?? "",
11431
+ timestamp,
10698
11432
  project: projLabel,
10699
11433
  sessionId,
10700
- agent: "gemini"
11434
+ agent: "antigravity"
10701
11435
  });
10702
11436
  }
10703
11437
  }
10704
11438
  }
10705
11439
  continue;
10706
11440
  }
10707
- if (msg.type !== "gemini") continue;
10708
- if (startDate && msg.timestamp && new Date(msg.timestamp) < startDate) continue;
10709
- if (msg.timestamp) {
10710
- if (!result.firstDate || msg.timestamp < result.firstDate)
10711
- result.firstDate = msg.timestamp;
10712
- if (!result.lastDate || msg.timestamp > result.lastDate) result.lastDate = msg.timestamp;
10713
- }
10714
- const tokens = msg.tokens;
10715
- const model = msg.model;
10716
- if (tokens && model) {
10717
- const p = geminiModelPrice(model);
10718
- if (p) {
10719
- const nonCached = Math.max(0, tokens.input - tokens.cached);
10720
- result.totalCostUSD += nonCached * p.i + tokens.cached * p.cr + tokens.output * p.o;
10721
- }
11441
+ if (!Array.isArray(step.tool_calls) || step.tool_calls.length === 0) continue;
11442
+ if (timestamp) {
11443
+ if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
11444
+ if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
10722
11445
  }
10723
- for (const tc of msg.toolCalls ?? []) {
11446
+ for (const tc of step.tool_calls) {
10724
11447
  result.totalToolCalls++;
10725
11448
  const toolName = tc.name ?? "";
10726
11449
  const toolNameLower = toolName.toLowerCase();
10727
- const input = tc.args ?? {};
10728
- sessionCalls.push({ toolName, input, timestamp: msg.timestamp ?? "" });
10729
- if (toolNameLower === "run_shell_command" || toolNameLower === "shell") {
11450
+ const input = canonicalToolInput(toolName, tc.args ?? {});
11451
+ sessionCalls.push({ toolName, input, timestamp });
11452
+ const isShellTool = toolNameLower === "run_command";
11453
+ if (isShellTool) {
10730
11454
  result.bashCalls++;
11455
+ const cwd = String(input.cwd ?? "");
11456
+ if (cwd && projLabel === conv.slice(0, 8)) {
11457
+ projLabel = stripTerminalEscapes(cwd).replace(os19.homedir(), "~").slice(0, 40);
11458
+ }
10731
11459
  }
10732
11460
  const rawCmd = String(input.command ?? "").trimStart();
10733
11461
  if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd))
@@ -10741,24 +11469,23 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10741
11469
  patternName: dlpMatch.patternName,
10742
11470
  redactedSample: dlpMatch.redactedSample,
10743
11471
  toolName,
10744
- timestamp: msg.timestamp ?? "",
11472
+ timestamp,
10745
11473
  project: projLabel,
10746
11474
  sessionId,
10747
- agent: "gemini"
11475
+ agent: "antigravity"
10748
11476
  });
10749
11477
  }
10750
11478
  }
10751
11479
  let astFsMatched = false;
10752
- const astRanForBash = toolNameLower === "run_shell_command" || toolNameLower === "shell";
10753
- if (astRanForBash) {
11480
+ if (isShellTool) {
10754
11481
  astFsMatched = pushFsOpAstFinding(
10755
11482
  String(input.command ?? ""),
10756
11483
  toolName,
10757
11484
  input,
10758
- msg.timestamp ?? "",
11485
+ timestamp,
10759
11486
  projLabel,
10760
11487
  sessionId,
10761
- "gemini",
11488
+ "antigravity",
10762
11489
  result,
10763
11490
  dedup
10764
11491
  );
@@ -10767,8 +11494,9 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10767
11494
  for (const source of ruleSources) {
10768
11495
  const { rule } = source;
10769
11496
  if (rule.verdict === "allow") continue;
10770
- if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
10771
- if (astRanForBash && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
11497
+ const ruleToolName = isShellTool ? "bash" : toolNameLower;
11498
+ if (rule.tool && !matchesPattern(ruleToolName, rule.tool)) continue;
11499
+ if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
10772
11500
  if (!evaluateSmartConditions(input, rule)) continue;
10773
11501
  const inputPreview = preview(input, 120);
10774
11502
  const k = findingKey(rule.name, inputPreview, projLabel);
@@ -10778,18 +11506,15 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10778
11506
  source,
10779
11507
  toolName,
10780
11508
  input,
10781
- timestamp: msg.timestamp ?? "",
11509
+ timestamp,
10782
11510
  project: projLabel,
10783
11511
  sessionId,
10784
- agent: "gemini"
11512
+ agent: "antigravity"
10785
11513
  });
10786
11514
  }
10787
11515
  ruleMatched = true;
10788
11516
  break;
10789
11517
  }
10790
- const isShellTool = ["bash", "execute_bash", "run_shell_command", "shell"].includes(
10791
- toolNameLower
10792
- );
10793
11518
  if (!ruleMatched && isShellTool) {
10794
11519
  const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
10795
11520
  if (shellVerdict) {
@@ -10813,18 +11538,201 @@ function scanGeminiHistory(startDate, onProgress, onLine) {
10813
11538
  },
10814
11539
  toolName,
10815
11540
  input,
10816
- timestamp: msg.timestamp ?? "",
11541
+ timestamp,
10817
11542
  project: projLabel,
10818
11543
  sessionId,
10819
- agent: "gemini"
11544
+ agent: "antigravity"
10820
11545
  });
10821
11546
  }
10822
11547
  }
10823
11548
  }
10824
11549
  }
10825
11550
  }
10826
- result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "gemini"));
11551
+ result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "antigravity"));
11552
+ }
11553
+ }
11554
+ return result;
11555
+ }
11556
+ function scanCopilotHistory(startDate, onProgress, onLine) {
11557
+ const sessionDir = path22.join(os19.homedir(), ".copilot", "session-state");
11558
+ const result = {
11559
+ filesScanned: 0,
11560
+ sessions: 0,
11561
+ totalToolCalls: 0,
11562
+ bashCalls: 0,
11563
+ findings: [],
11564
+ dlpFindings: [],
11565
+ loopFindings: [],
11566
+ totalCostUSD: 0,
11567
+ // event logs carry no token/cost rollup
11568
+ firstDate: null,
11569
+ lastDate: null,
11570
+ sessionsWithEarlySecrets: 0
11571
+ };
11572
+ const dedup = emptyScanDedup();
11573
+ if (!fs20.existsSync(sessionDir)) return result;
11574
+ let sessionIds;
11575
+ try {
11576
+ sessionIds = fs20.readdirSync(sessionDir);
11577
+ } catch {
11578
+ return result;
11579
+ }
11580
+ const ruleSources = buildRuleSources();
11581
+ for (const sessionId of sessionIds) {
11582
+ const eventsPath = path22.join(sessionDir, sessionId, "events.jsonl");
11583
+ if (!fs20.existsSync(eventsPath)) continue;
11584
+ result.filesScanned++;
11585
+ onProgress?.(result.filesScanned);
11586
+ let raw;
11587
+ try {
11588
+ raw = fs20.readFileSync(eventsPath, "utf-8");
11589
+ } catch {
11590
+ continue;
11591
+ }
11592
+ let projLabel = sessionId.slice(0, 8);
11593
+ const sessionCalls = [];
11594
+ result.sessions++;
11595
+ for (const line of raw.split("\n")) {
11596
+ if (!line.trim()) continue;
11597
+ onLine?.();
11598
+ let ev;
11599
+ try {
11600
+ ev = JSON.parse(line);
11601
+ } catch {
11602
+ continue;
11603
+ }
11604
+ const timestamp = ev.timestamp ?? "";
11605
+ if (ev.type === "session.start") {
11606
+ const cwd = ev.data?.context?.cwd;
11607
+ if (typeof cwd === "string" && cwd) {
11608
+ projLabel = stripTerminalEscapes(cwd).replace(os19.homedir(), "~").slice(0, 40);
11609
+ }
11610
+ continue;
11611
+ }
11612
+ if (startDate && timestamp && new Date(timestamp) < startDate) continue;
11613
+ if (ev.type === "user.message") {
11614
+ const text = ev.data?.content ?? ev.data?.text ?? "";
11615
+ if (typeof text === "string" && text) {
11616
+ const dlpMatch2 = scanArgs({ text });
11617
+ if (dlpMatch2) {
11618
+ const k = dlpKey(dlpMatch2.patternName, dlpMatch2.redactedSample, projLabel);
11619
+ if (!dedup.dlpKeys.has(k)) {
11620
+ dedup.dlpKeys.add(k);
11621
+ result.dlpFindings.push({
11622
+ patternName: dlpMatch2.patternName,
11623
+ redactedSample: dlpMatch2.redactedSample,
11624
+ toolName: "user-prompt",
11625
+ timestamp,
11626
+ project: projLabel,
11627
+ sessionId,
11628
+ agent: "copilot"
11629
+ });
11630
+ }
11631
+ }
11632
+ }
11633
+ continue;
11634
+ }
11635
+ if (ev.type !== "tool.execution_start") continue;
11636
+ const toolName = ev.data?.toolName ?? "";
11637
+ const toolNameLower = toolName.toLowerCase();
11638
+ const input = ev.data?.arguments ?? {};
11639
+ result.totalToolCalls++;
11640
+ sessionCalls.push({ toolName, input, timestamp });
11641
+ const isShellTool = toolNameLower === "bash" || toolNameLower === "shell";
11642
+ if (isShellTool) result.bashCalls++;
11643
+ if (timestamp) {
11644
+ if (!result.firstDate || timestamp < result.firstDate) result.firstDate = timestamp;
11645
+ if (!result.lastDate || timestamp > result.lastDate) result.lastDate = timestamp;
11646
+ }
11647
+ const rawCmd = String(input.command ?? "").trimStart();
11648
+ if (/^node9\s+(scan|explain|report|tail|dlp|status|sessions|audit)\b/.test(rawCmd)) continue;
11649
+ const dlpMatch = scanArgs(input);
11650
+ if (dlpMatch) {
11651
+ const k = dlpKey(dlpMatch.patternName, dlpMatch.redactedSample, projLabel);
11652
+ if (!dedup.dlpKeys.has(k)) {
11653
+ dedup.dlpKeys.add(k);
11654
+ result.dlpFindings.push({
11655
+ patternName: dlpMatch.patternName,
11656
+ redactedSample: dlpMatch.redactedSample,
11657
+ toolName,
11658
+ timestamp,
11659
+ project: projLabel,
11660
+ sessionId,
11661
+ agent: "copilot"
11662
+ });
11663
+ }
11664
+ }
11665
+ let astFsMatched = false;
11666
+ if (isShellTool) {
11667
+ astFsMatched = pushFsOpAstFinding(
11668
+ String(input.command ?? ""),
11669
+ toolName,
11670
+ input,
11671
+ timestamp,
11672
+ projLabel,
11673
+ sessionId,
11674
+ "copilot",
11675
+ result,
11676
+ dedup
11677
+ );
11678
+ }
11679
+ let ruleMatched = astFsMatched;
11680
+ for (const source of ruleSources) {
11681
+ const { rule } = source;
11682
+ if (rule.verdict === "allow") continue;
11683
+ if (rule.tool && !matchesPattern(toolNameLower, rule.tool)) continue;
11684
+ if (isShellTool && rule.name && AST_FS_REGEX_RULES.has(rule.name)) continue;
11685
+ if (!evaluateSmartConditions(input, rule)) continue;
11686
+ const inputPreview = preview(input, 120);
11687
+ const k = findingKey(rule.name, inputPreview, projLabel);
11688
+ if (!dedup.findingsKeys.has(k)) {
11689
+ dedup.findingsKeys.add(k);
11690
+ result.findings.push({
11691
+ source,
11692
+ toolName,
11693
+ input,
11694
+ timestamp,
11695
+ project: projLabel,
11696
+ sessionId,
11697
+ agent: "copilot"
11698
+ });
11699
+ }
11700
+ ruleMatched = true;
11701
+ break;
11702
+ }
11703
+ if (!ruleMatched && isShellTool) {
11704
+ const shellVerdict = detectDangerousShellExec(String(input.command ?? ""));
11705
+ if (shellVerdict) {
11706
+ const astRule = {
11707
+ name: `ast:bash-safe:${shellVerdict}-shell-exec-remote`,
11708
+ tool: "bash",
11709
+ conditions: [],
11710
+ verdict: shellVerdict,
11711
+ reason: `Shell execution of remote download detected by AST analysis (bash-safe)`
11712
+ };
11713
+ const inputPreview = preview(input, 120);
11714
+ const k = findingKey(astRule.name, inputPreview, projLabel);
11715
+ if (!dedup.findingsKeys.has(k)) {
11716
+ dedup.findingsKeys.add(k);
11717
+ result.findings.push({
11718
+ source: {
11719
+ shieldName: "bash-safe",
11720
+ shieldLabel: "bash-safe (AST)",
11721
+ sourceType: "shield",
11722
+ rule: astRule
11723
+ },
11724
+ toolName,
11725
+ input,
11726
+ timestamp,
11727
+ project: projLabel,
11728
+ sessionId,
11729
+ agent: "copilot"
11730
+ });
11731
+ }
11732
+ }
11733
+ }
10827
11734
  }
11735
+ result.loopFindings.push(...detectLoops(sessionCalls, projLabel, sessionId, "copilot"));
10828
11736
  }
10829
11737
  return result;
10830
11738
  }
@@ -11123,8 +12031,8 @@ function printFindingRow(f, drillDown, showSessionId, previewWidth) {
11123
12031
  const stale = isStaleFinding(f.timestamp);
11124
12032
  const ts = f.timestamp ? chalk5.dim(fmtTs(f.timestamp) + " ") : "";
11125
12033
  const proj = chalk5.dim(f.project.slice(0, 22).padEnd(22) + " ");
11126
- const agentLabel2 = f.agent === "gemini" ? "[Gemini] " : f.agent === "codex" ? "[Codex] " : "[Claude] ";
11127
- const agentBadge = stale ? chalk5.dim(agentLabel2) : f.agent === "gemini" ? chalk5.blue(agentLabel2) : f.agent === "codex" ? chalk5.magenta(agentLabel2) : chalk5.cyan(agentLabel2);
12034
+ const agentLabel2 = agentBadgeText(f.agent);
12035
+ const agentBadge = stale ? chalk5.dim(agentLabel2) : chalk5[agentColorName(f.agent)](agentLabel2);
11128
12036
  let cmdText;
11129
12037
  if (drillDown) {
11130
12038
  cmdText = f.fullCommand;
@@ -11687,16 +12595,35 @@ function registerScanCommand(program2) {
11687
12595
  (done) => onProgress(claudeScan.filesScanned + done),
11688
12596
  onLine
11689
12597
  );
11690
- const codexScan = scanCodexHistory(
12598
+ const antigravityScan = scanAntigravityHistory(
11691
12599
  startDate,
11692
12600
  (done) => onProgress(claudeScan.filesScanned + geminiScan.filesScanned + done),
11693
12601
  onLine
11694
12602
  );
11695
- const scan = mergeScans(mergeScans(claudeScan, geminiScan), codexScan);
12603
+ const copilotScan = scanCopilotHistory(
12604
+ startDate,
12605
+ (done) => onProgress(
12606
+ claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + done
12607
+ ),
12608
+ onLine
12609
+ );
12610
+ const codexScan = scanCodexHistory(
12611
+ startDate,
12612
+ (done) => onProgress(
12613
+ claudeScan.filesScanned + geminiScan.filesScanned + antigravityScan.filesScanned + copilotScan.filesScanned + done
12614
+ ),
12615
+ onLine
12616
+ );
12617
+ const scan = mergeScans(
12618
+ mergeScans(mergeScans(mergeScans(claudeScan, geminiScan), antigravityScan), copilotScan),
12619
+ codexScan
12620
+ );
11696
12621
  scan.dlpFindings.push(...scanShellConfig());
11697
12622
  const summary = buildScanSummary([
11698
12623
  { id: "claude", label: "Claude", icon: "\u{1F916}", scan: claudeScan },
11699
12624
  { id: "gemini", label: "Gemini", icon: "\u264A", scan: geminiScan },
12625
+ { id: "antigravity", label: "Antigravity", icon: "\u{1F680}", scan: antigravityScan },
12626
+ { id: "copilot", label: "Copilot", icon: "\u{1F419}", scan: copilotScan },
11700
12627
  { id: "codex", label: "Codex", icon: "\u{1F52E}", scan: codexScan }
11701
12628
  ]);
11702
12629
  if (useTTY) process.stdout.write("\r" + " ".repeat(60) + "\r");
@@ -11704,7 +12631,7 @@ function registerScanCommand(program2) {
11704
12631
  console.log(chalk5.yellow(" No session history found."));
11705
12632
  console.log(
11706
12633
  chalk5.gray(
11707
- " Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/)\n"
12634
+ " Supported: Claude Code (~/.claude/projects/) \xB7 Gemini CLI (~/.gemini/tmp/) \xB7 Antigravity (~/.gemini/antigravity-*/brain/) \xB7 Copilot CLI (~/.copilot/session-state/)\n"
11708
12635
  )
11709
12636
  );
11710
12637
  return;
@@ -11716,6 +12643,12 @@ function registerScanCommand(program2) {
11716
12643
  breakdownParts.push(chalk5.cyan(String(claudeScan.sessions)) + chalk5.dim(" Claude"));
11717
12644
  if (geminiScan.sessions > 0)
11718
12645
  breakdownParts.push(chalk5.blue(String(geminiScan.sessions)) + chalk5.dim(" Gemini"));
12646
+ if (antigravityScan.sessions > 0)
12647
+ breakdownParts.push(
12648
+ chalk5.yellow(String(antigravityScan.sessions)) + chalk5.dim(" Antigravity")
12649
+ );
12650
+ if (copilotScan.sessions > 0)
12651
+ breakdownParts.push(chalk5.green(String(copilotScan.sessions)) + chalk5.dim(" Copilot"));
11719
12652
  if (codexScan.sessions > 0)
11720
12653
  breakdownParts.push(chalk5.magenta(String(codexScan.sessions)) + chalk5.dim(" Codex"));
11721
12654
  const sessionBreakdown = breakdownParts.length > 1 ? chalk5.dim("(") + breakdownParts.join(chalk5.dim(" \xB7 ")) + chalk5.dim(")") : "";
@@ -11904,7 +12837,7 @@ function registerScanCommand(program2) {
11904
12837
  const stale = isStaleFinding(f.timestamp);
11905
12838
  const ts = f.timestamp ? chalk5.dim(fmtTs(f.timestamp) + " ") : "";
11906
12839
  const proj = chalk5.dim(f.project.slice(0, 22).padEnd(22) + " ");
11907
- const agentBadge = f.agent === "gemini" ? chalk5.blue("[Gemini] ") : f.agent === "codex" ? chalk5.magenta("[Codex] ") : f.agent === "shell" ? chalk5.yellow("[Shell] ") : chalk5.cyan("[Claude] ");
12840
+ const agentBadge = chalk5[agentColorName(f.agent)](agentBadgeText(f.agent));
11908
12841
  const sessionSuffix = f.sessionId ? chalk5.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
11909
12842
  const recurringBadge = recurringPatterns.has(f.patternName) ? chalk5.red.bold(" \u26A0\uFE0F recurring ") : "";
11910
12843
  const patternDisplay = stale ? chalk5.dim(f.patternName) : chalk5.yellow(f.patternName);
@@ -11952,8 +12885,8 @@ function registerScanCommand(program2) {
11952
12885
  const stale = isStaleFinding(f.timestamp);
11953
12886
  const ts = f.timestamp ? chalk5.dim(fmtTs(f.timestamp) + " ") : "";
11954
12887
  const proj = chalk5.dim(f.project.slice(0, 22).padEnd(22) + " ");
11955
- const agentLabel2 = f.agent === "gemini" ? "[Gemini] " : f.agent === "codex" ? "[Codex] " : "[Claude] ";
11956
- const agentBadge = stale ? chalk5.dim(agentLabel2) : f.agent === "gemini" ? chalk5.blue(agentLabel2) : f.agent === "codex" ? chalk5.magenta(agentLabel2) : chalk5.cyan(agentLabel2);
12888
+ const agentLabel2 = agentBadgeText(f.agent);
12889
+ const agentBadge = stale ? chalk5.dim(agentLabel2) : chalk5[agentColorName(f.agent)](agentLabel2);
11957
12890
  const toolDisplay = stale ? chalk5.dim(f.toolName) : chalk5.yellow(f.toolName);
11958
12891
  const cmdDisplay = stale ? chalk5.dim(f.commandPreview) : chalk5.gray(f.commandPreview);
11959
12892
  const sessionSuffix = f.sessionId ? chalk5.dim(` \u2192 ${f.sessionId.slice(0, 8)}`) : "";
@@ -12087,6 +13020,7 @@ var init_scan = __esm({
12087
13020
  init_policy();
12088
13021
  init_dist();
12089
13022
  init_dlp();
13023
+ init_hook_payload();
12090
13024
  init_dist();
12091
13025
  init_scan_summary();
12092
13026
  init_setup();
@@ -12165,6 +13099,8 @@ var init_scan = __esm({
12165
13099
  "exec_command",
12166
13100
  "shell",
12167
13101
  "run_shell_command",
13102
+ "run_command",
13103
+ // Antigravity (agy)
12168
13104
  "write",
12169
13105
  "edit",
12170
13106
  "multiedit"
@@ -16751,33 +17687,7 @@ function resolveUserSkillRoot(entry, cwd) {
16751
17687
  // src/cli/commands/check.ts
16752
17688
  init_dlp();
16753
17689
  init_audit();
16754
-
16755
- // src/utils/hook-payload.ts
16756
- function extractToolName(payload, defaultValue = "") {
16757
- return payload.tool_name ?? payload.name ?? defaultValue;
16758
- }
16759
- function extractToolInput(payload) {
16760
- return payload.tool_input ?? payload.args ?? {};
16761
- }
16762
- function canonicalToolName(name) {
16763
- switch (name) {
16764
- // Hermes Agent
16765
- case "terminal":
16766
- return "Bash";
16767
- case "write_file":
16768
- return "Write";
16769
- case "patch":
16770
- return "Edit";
16771
- case "read_file":
16772
- return "Read";
16773
- case "search_files":
16774
- return "Grep";
16775
- default:
16776
- return name;
16777
- }
16778
- }
16779
-
16780
- // src/cli/commands/check.ts
17690
+ init_hook_payload();
16781
17691
  function sanitize2(value) {
16782
17692
  return value.replace(/[\x00-\x1F\x7F]/g, "");
16783
17693
  }
@@ -16790,6 +17700,9 @@ function detectAiAgent(payload) {
16790
17700
  if (payload.turn_id !== void 0) {
16791
17701
  return "Codex";
16792
17702
  }
17703
+ if (payload.toolCall !== void 0 || payload.conversationId !== void 0) {
17704
+ return "Antigravity";
17705
+ }
16793
17706
  if (payload.hook_event_name === "PreToolUse" || payload.hook_event_name === "PostToolUse" || payload.tool_use_id !== void 0 || payload.permission_mode !== void 0) {
16794
17707
  return "Claude Code";
16795
17708
  }
@@ -16805,6 +17718,9 @@ function detectAiAgent(payload) {
16805
17718
  if (process.env.HERMES_SESSION_ID || process.env.HERMES_HOME || process.env.HERMES_INTERACTIVE) {
16806
17719
  return "Hermes";
16807
17720
  }
17721
+ if (process.env.ANTIGRAVITY_CONVERSATION_ID) {
17722
+ return "Antigravity";
17723
+ }
16808
17724
  if (process.env.GEMINI_CLI_VERSION || process.env.GEMINI_API_KEY) {
16809
17725
  return "Gemini CLI";
16810
17726
  }
@@ -16820,7 +17736,11 @@ function detectAiAgent(payload) {
16820
17736
  return "Terminal";
16821
17737
  }
16822
17738
  function registerCheckCommand(program2) {
16823
- 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) => {
17739
+ program2.command("check", { hidden: true }).description("Hook handler \u2014 evaluates a tool call before execution").argument("[data]", "JSON string of the tool call").option(
17740
+ "--agent <name>",
17741
+ "Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
17742
+ ).action(async (data, opts) => {
17743
+ const agentOverride = agentLabelFromFlag(opts?.agent);
16824
17744
  const processPayload = async (raw) => {
16825
17745
  try {
16826
17746
  if (!raw || raw.trim() === "") process.exit(0);
@@ -16860,7 +17780,7 @@ RAW: ${raw}
16860
17780
  if (!prompt) process.exit(0);
16861
17781
  const dlpMatch = scanArgs({ prompt });
16862
17782
  if (!dlpMatch) process.exit(0);
16863
- const agent2 = detectAiAgent(payload);
17783
+ const agent2 = agentOverride ?? detectAiAgent(payload);
16864
17784
  const sessionId2 = typeof payload.session_id === "string" ? payload.session_id : void 0;
16865
17785
  appendLocalAudit(
16866
17786
  "UserPromptSubmit",
@@ -16901,7 +17821,8 @@ RAW: ${raw}
16901
17821
  );
16902
17822
  process.exit(2);
16903
17823
  }
16904
- const safeCwdForConfig = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
17824
+ const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
17825
+ const safeCwdForConfig = typeof payloadCwd === "string" && path33.isAbsolute(payloadCwd) ? payloadCwd : void 0;
16905
17826
  const config = getConfig(safeCwdForConfig);
16906
17827
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
16907
17828
  try {
@@ -16951,9 +17872,10 @@ RAW: ${raw}
16951
17872
  fs32.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
16952
17873
  `);
16953
17874
  }
16954
- const toolName = canonicalToolName(sanitize2(extractToolName(payload)));
16955
- const toolInput = extractToolInput(payload);
16956
- const agent = detectAiAgent(payload);
17875
+ const rawToolName = sanitize2(extractToolName(payload));
17876
+ const toolName = canonicalToolName(rawToolName);
17877
+ const toolInput = canonicalToolInput(rawToolName, extractToolInput(payload));
17878
+ const agent = agentOverride ?? detectAiAgent(payload);
16957
17879
  const mcpMatch = toolName.match(/^mcp__([^_](?:[^_]|_(?!_))*?)__/i);
16958
17880
  const mcpServer = mcpMatch?.[1];
16959
17881
  const sendBlock = (msg, result2) => {
@@ -16991,6 +17913,21 @@ RAW: ${raw}
16991
17913
  msg,
16992
17914
  result2?.recoveryCommand
16993
17915
  );
17916
+ if (agent === "Antigravity") {
17917
+ process.stdout.write(
17918
+ JSON.stringify({ decision: "deny", reason: aiFeedbackMessage }) + "\n"
17919
+ );
17920
+ process.exit(0);
17921
+ }
17922
+ if (agent === "GitHub Copilot") {
17923
+ process.stdout.write(
17924
+ JSON.stringify({
17925
+ permissionDecision: "deny",
17926
+ permissionDecisionReason: aiFeedbackMessage
17927
+ }) + "\n"
17928
+ );
17929
+ process.exit(0);
17930
+ }
16994
17931
  process.stdout.write(
16995
17932
  JSON.stringify({
16996
17933
  decision: "block",
@@ -17009,11 +17946,11 @@ RAW: ${raw}
17009
17946
  sendBlock("Node9: unrecognised hook payload \u2014 tool name missing.");
17010
17947
  return;
17011
17948
  }
17012
- const sessionId = typeof payload.session_id === "string" ? payload.session_id : void 0;
17013
- const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : void 0;
17949
+ const sessionId = typeof payload.session_id === "string" ? payload.session_id : typeof payload.conversationId === "string" ? payload.conversationId : void 0;
17950
+ const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : typeof payload.transcriptPath === "string" ? payload.transcriptPath : void 0;
17014
17951
  const meta = { agent, mcpServer, sessionId, transcriptPath };
17015
17952
  const skillPinCfg = config.policy.skillPinning;
17016
- const rawSessionId = typeof payload.session_id === "string" ? payload.session_id : "";
17953
+ const rawSessionId = sessionId ?? "";
17017
17954
  const safeSessionId = /^[A-Za-z0-9_\-]{1,128}$/.test(rawSessionId) ? rawSessionId : "";
17018
17955
  if (skillPinCfg.enabled && safeSessionId) {
17019
17956
  try {
@@ -17070,7 +18007,7 @@ RAW: ${raw}
17070
18007
  return;
17071
18008
  }
17072
18009
  if (!flag || flag.state !== "verified" && flag.state !== "warned") {
17073
- const absoluteCwd = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
18010
+ const absoluteCwd = typeof payloadCwd === "string" && path33.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17074
18011
  const extraRoots = skillPinCfg.roots;
17075
18012
  const resolvedExtra = extraRoots.map((r) => resolveUserSkillRoot(r, absoluteCwd)).filter((r) => typeof r === "string");
17076
18013
  const roots = [...defaultSkillRoots(absoluteCwd), ...resolvedExtra];
@@ -17136,7 +18073,7 @@ RAW: ${raw}
17136
18073
  if (shouldSnapshot(toolName, toolInput, config)) {
17137
18074
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
17138
18075
  }
17139
- const safeCwdForAuth = typeof payload.cwd === "string" && path33.isAbsolute(payload.cwd) ? payload.cwd : void 0;
18076
+ const safeCwdForAuth = typeof payloadCwd === "string" && path33.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17140
18077
  const result = await authorizeHeadless(toolName, toolInput, meta, {
17141
18078
  cwd: safeCwdForAuth
17142
18079
  });
@@ -17261,6 +18198,7 @@ function containsShellMetachar(token) {
17261
18198
  }
17262
18199
 
17263
18200
  // src/cli/commands/log.ts
18201
+ init_hook_payload();
17264
18202
  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;
17265
18203
  function detectTestResult(command, output) {
17266
18204
  if (!TEST_COMMAND_RE2.test(command)) return null;
@@ -17279,14 +18217,19 @@ function sanitize3(value) {
17279
18217
  return value.replace(/[\x00-\x1F\x7F]/g, "");
17280
18218
  }
17281
18219
  function registerLogCommand(program2) {
17282
- program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").action(async (data) => {
18220
+ program2.command("log", { hidden: true }).description("PostToolUse hook \u2014 records executed tool calls").argument("[data]", "JSON string of the tool call").option(
18221
+ "--agent <name>",
18222
+ "Agent identity override, set by node9-authored hook registrations (e.g. antigravity)"
18223
+ ).action(async (data, opts) => {
18224
+ const agentOverride = agentLabelFromFlag(opts?.agent);
17283
18225
  const logPayload = async (raw) => {
17284
18226
  try {
17285
18227
  if (!raw || raw.trim() === "") process.exit(0);
17286
18228
  const payload = JSON.parse(raw);
18229
+ if (payload.toolCall === null) process.exit(0);
17287
18230
  const rawToolName = sanitize3(extractToolName(payload, "unknown"));
17288
18231
  const tool = canonicalToolName(rawToolName);
17289
- const rawInput = extractToolInput(payload);
18232
+ const rawInput = canonicalToolInput(rawToolName, extractToolInput(payload));
17290
18233
  const metaTag = (() => {
17291
18234
  const m = payload.meta;
17292
18235
  if (m && typeof m === "object") {
@@ -17295,7 +18238,7 @@ function registerLogCommand(program2) {
17295
18238
  }
17296
18239
  return void 0;
17297
18240
  })();
17298
- 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;
18241
+ 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;
17299
18242
  const entry = {
17300
18243
  ts: (/* @__PURE__ */ new Date()).toISOString(),
17301
18244
  tool,
@@ -17305,7 +18248,8 @@ function registerLogCommand(program2) {
17305
18248
  };
17306
18249
  if (agent) entry.agent = agent;
17307
18250
  if (rawToolName !== tool) entry.agentToolName = rawToolName;
17308
- if (payload.session_id) entry.sessionId = payload.session_id;
18251
+ const payloadSessionId = payload.session_id ?? payload.conversationId;
18252
+ if (payloadSessionId) entry.sessionId = payloadSessionId;
17309
18253
  const logPath = path34.join(os29.homedir(), ".node9", "audit.log");
17310
18254
  if (!fs33.existsSync(path34.dirname(logPath)))
17311
18255
  fs33.mkdirSync(path34.dirname(logPath), { recursive: true });
@@ -17342,7 +18286,8 @@ function registerLogCommand(program2) {
17342
18286
  }
17343
18287
  }
17344
18288
  }
17345
- const safeCwd = typeof payload.cwd === "string" && path34.isAbsolute(payload.cwd) ? payload.cwd : void 0;
18289
+ const payloadCwd = typeof payload.cwd === "string" ? payload.cwd : Array.isArray(payload.workspacePaths) && typeof payload.workspacePaths[0] === "string" ? payload.workspacePaths[0] : void 0;
18290
+ const safeCwd = typeof payloadCwd === "string" && path34.isAbsolute(payloadCwd) ? payloadCwd : void 0;
17346
18291
  const config = getConfig(safeCwd);
17347
18292
  if ((tool === "Bash" || tool === "bash") && config.settings.enableUndo !== false) {
17348
18293
  const bashCommand = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
@@ -19430,12 +20375,12 @@ function registerInitCommand(program2) {
19430
20375
  if (found.length === 0) {
19431
20376
  console.log(
19432
20377
  chalk16.gray(
19433
- "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)."
20378
+ "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)."
19434
20379
  )
19435
20380
  );
19436
20381
  console.log(
19437
20382
  chalk16.gray(
19438
- "then run: node9 agents add <claude|codex|gemini|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
20383
+ "then run: node9 agents add <claude|codex|antigravity|gemini|copilot|cursor|windsurf|vscode|claudeDesktop|opencode|pi|hermes>"
19439
20384
  )
19440
20385
  );
19441
20386
  return;
@@ -19449,6 +20394,8 @@ function registerInitCommand(program2) {
19449
20394
  console.log(chalk16.bold(`Wiring ${agent}...`));
19450
20395
  if (agent === "claude") await setupClaude();
19451
20396
  else if (agent === "gemini") await setupGemini();
20397
+ else if (agent === "antigravity") await setupAntigravity();
20398
+ else if (agent === "copilot") await setupCopilot();
19452
20399
  else if (agent === "cursor") await setupCursor();
19453
20400
  else if (agent === "codex") await setupCodex();
19454
20401
  else if (agent === "windsurf") await setupWindsurf();
@@ -21212,6 +22159,8 @@ import chalk23 from "chalk";
21212
22159
  var SETUP_FN = {
21213
22160
  claude: setupClaude,
21214
22161
  gemini: setupGemini,
22162
+ antigravity: setupAntigravity,
22163
+ copilot: setupCopilot,
21215
22164
  cursor: setupCursor,
21216
22165
  codex: setupCodex,
21217
22166
  windsurf: setupWindsurf,
@@ -21224,6 +22173,8 @@ var SETUP_FN = {
21224
22173
  var TEARDOWN_FN = {
21225
22174
  claude: teardownClaude,
21226
22175
  gemini: teardownGemini,
22176
+ antigravity: teardownAntigravity,
22177
+ copilot: teardownCopilot,
21227
22178
  cursor: teardownCursor,
21228
22179
  codex: teardownCodex,
21229
22180
  windsurf: teardownWindsurf,
@@ -21234,6 +22185,10 @@ var TEARDOWN_FN = {
21234
22185
  hermes: teardownHermes
21235
22186
  };
21236
22187
  var AGENT_NAMES = Object.keys(SETUP_FN);
22188
+ function resolveAgentName(agent) {
22189
+ const lower = agent.toLowerCase();
22190
+ return lower === "agy" ? "antigravity" : lower;
22191
+ }
21237
22192
  function registerAgentsCommand(program2) {
21238
22193
  const agents = program2.command("agents").description("List and manage AI agent integrations");
21239
22194
  agents.command("list").description("Show all supported agents and their Node9 status").action(() => {
@@ -21262,9 +22217,16 @@ function registerAgentsCommand(program2) {
21262
22217
  chalk23.yellow(` ${unwired.length} agent(s) not yet wired. Run: `) + chalk23.white(`node9 agents add ${unwired[0].name}`) + "\n"
21263
22218
  );
21264
22219
  }
22220
+ if (statuses.some((s) => s.name === "gemini" && s.installed)) {
22221
+ console.log(
22222
+ chalk23.yellow(
22223
+ " \u26A0\uFE0F Gemini CLI stops serving AI Pro/Ultra and free tiers on 2026-06-18.\n"
22224
+ ) + chalk23.gray(" Migrate to Antigravity: ") + chalk23.white("node9 agents add antigravity") + "\n"
22225
+ );
22226
+ }
21265
22227
  });
21266
22228
  agents.command("add").description("Wire Node9 into an agent").argument("<agent>", `Agent to wire: ${AGENT_NAMES.join(" | ")}`).action(async (agent) => {
21267
- const name = agent.toLowerCase();
22229
+ const name = resolveAgentName(agent);
21268
22230
  const fn = SETUP_FN[name];
21269
22231
  if (!fn) {
21270
22232
  console.error(chalk23.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
@@ -21273,7 +22235,7 @@ function registerAgentsCommand(program2) {
21273
22235
  await fn();
21274
22236
  });
21275
22237
  agents.command("remove").description("Remove Node9 from an agent").argument("<agent>", `Agent to unwire: ${AGENT_NAMES.join(" | ")}`).action((agent) => {
21276
- const name = agent.toLowerCase();
22238
+ const name = resolveAgentName(agent);
21277
22239
  const fn = TEARDOWN_FN[name];
21278
22240
  if (!fn) {
21279
22241
  console.error(chalk23.red(`Unknown agent: "${agent}". Supported: ${AGENT_NAMES.join(", ")}`));
@@ -21291,6 +22253,7 @@ function registerAgentsCommand(program2) {
21291
22253
  init_scan();
21292
22254
 
21293
22255
  // src/cli/commands/sessions.ts
22256
+ init_scan_summary();
21294
22257
  import chalk24 from "chalk";
21295
22258
  import fs41 from "fs";
21296
22259
  import path42 from "path";
@@ -21901,7 +22864,9 @@ function renderList(summaries, totalCost) {
21901
22864
  const cost = s.costUSD > 0 ? chalk24.dim(" " + fmtCost3(s.costUSD).padEnd(8)) : " ";
21902
22865
  const blocked = s.blockedCalls.length > 0 ? chalk24.red(" \u{1F6D1} " + String(s.blockedCalls.length)) : "";
21903
22866
  const snap = s.hasSnapshot ? chalk24.green(" \u{1F4F8}") : "";
21904
- const agentBadge = s.agent === "gemini" ? chalk24.blue(" [Gemini]") : s.agent === "codex" ? chalk24.magenta(" [Codex]") : chalk24.cyan(" [Claude]");
22867
+ const agentBadge = chalk24[agentColorName(s.agent ?? "claude")](
22868
+ " " + agentBadgeText(s.agent ?? "claude", 0)
22869
+ );
21905
22870
  const sid = chalk24.dim(" " + s.sessionId.slice(0, 8));
21906
22871
  console.log(
21907
22872
  ` ${timeStr} ${prompt} ${tools}${cost}${blocked}${snap}${agentBadge}${sid}${dateRange}`
@@ -21921,7 +22886,7 @@ function renderDetail(s) {
21921
22886
  );
21922
22887
  console.log(chalk24.bold(" Project ") + chalk24.white(s.projectLabel));
21923
22888
  if (s.agent) {
21924
- const agentLabel2 = s.agent === "gemini" ? chalk24.blue("Gemini CLI") : s.agent === "codex" ? chalk24.magenta("Codex") : chalk24.cyan("Claude Code");
22889
+ const agentLabel2 = chalk24[agentColorName(s.agent)](agentDisplayName(s.agent));
21925
22890
  console.log(chalk24.bold(" Agent ") + agentLabel2);
21926
22891
  }
21927
22892
  console.log(chalk24.bold(" When ") + chalk24.white(fmtDateTime(s.startTime)));
@@ -22558,12 +23523,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
22558
23523
  });
22559
23524
  program.command("addto", { hidden: true }).description("Integrate Node9 with an AI agent").addHelpText(
22560
23525
  "after",
22561
- "\n Supported targets: claude gemini cursor codex windsurf vscode hud"
23526
+ "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
22562
23527
  ).argument(
22563
23528
  "<target>",
22564
- "The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
23529
+ "The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
22565
23530
  ).action(async (target) => {
22566
23531
  if (target === "gemini") return await setupGemini();
23532
+ if (target === "antigravity" || target === "agy") return await setupAntigravity();
23533
+ if (target === "copilot") return await setupCopilot();
22567
23534
  if (target === "claude") return await setupClaude();
22568
23535
  if (target === "cursor") return await setupCursor();
22569
23536
  if (target === "codex") return await setupCodex();
@@ -22573,17 +23540,17 @@ program.command("addto", { hidden: true }).description("Integrate Node9 with an
22573
23540
  if (target === "hud") return setupHud();
22574
23541
  console.error(
22575
23542
  chalk30.red(
22576
- `Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
23543
+ `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
22577
23544
  )
22578
23545
  );
22579
23546
  process.exit(1);
22580
23547
  });
22581
23548
  program.command("setup", { hidden: true }).description('Alias for "addto" \u2014 integrate Node9 with an AI agent').addHelpText(
22582
23549
  "after",
22583
- "\n Supported targets: claude gemini cursor codex windsurf vscode hud"
23550
+ "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
22584
23551
  ).argument(
22585
23552
  "[target]",
22586
- "The agent to protect: claude | gemini | cursor | codex | windsurf | vscode | hud"
23553
+ "The agent to protect: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
22587
23554
  ).action(async (target) => {
22588
23555
  if (!target) {
22589
23556
  console.log(chalk30.cyan("\n\u{1F6E1}\uFE0F Node9 Setup \u2014 integrate with your AI agent\n"));
@@ -22591,6 +23558,8 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
22591
23558
  console.log(" Targets:");
22592
23559
  console.log(" " + chalk30.green("claude") + " \u2014 Claude Code (hook mode)");
22593
23560
  console.log(" " + chalk30.green("gemini") + " \u2014 Gemini CLI (hook mode)");
23561
+ console.log(" " + chalk30.green("antigravity") + " \u2014 Antigravity / agy (hook mode)");
23562
+ console.log(" " + chalk30.green("copilot") + " \u2014 GitHub Copilot CLI (hook mode)");
22594
23563
  console.log(" " + chalk30.green("cursor") + " \u2014 Cursor (MCP proxy)");
22595
23564
  console.log(" " + chalk30.green("codex") + " \u2014 OpenAI Codex CLI (MCP proxy)");
22596
23565
  console.log(" " + chalk30.green("windsurf") + " \u2014 Windsurf (MCP proxy)");
@@ -22604,6 +23573,8 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
22604
23573
  }
22605
23574
  const t = target.toLowerCase();
22606
23575
  if (t === "gemini") return await setupGemini();
23576
+ if (t === "antigravity" || t === "agy") return await setupAntigravity();
23577
+ if (t === "copilot") return await setupCopilot();
22607
23578
  if (t === "claude") return await setupClaude();
22608
23579
  if (t === "cursor") return await setupCursor();
22609
23580
  if (t === "codex") return await setupCodex();
@@ -22613,21 +23584,23 @@ program.command("setup", { hidden: true }).description('Alias for "addto" \u2014
22613
23584
  if (t === "hud") return setupHud();
22614
23585
  console.error(
22615
23586
  chalk30.red(
22616
- `Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
23587
+ `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
22617
23588
  )
22618
23589
  );
22619
23590
  process.exit(1);
22620
23591
  });
22621
23592
  program.command("removefrom", { hidden: true }).description("Remove Node9 hooks from an AI agent configuration").addHelpText(
22622
23593
  "after",
22623
- "\n Supported targets: claude gemini cursor codex windsurf vscode hud"
23594
+ "\n Supported targets: claude antigravity copilot gemini cursor codex windsurf vscode hud"
22624
23595
  ).argument(
22625
23596
  "<target>",
22626
- "The agent to remove from: claude | gemini | cursor | codex | windsurf | vscode | hud"
23597
+ "The agent to remove from: claude | antigravity | copilot | gemini | cursor | codex | windsurf | vscode | hud"
22627
23598
  ).action((target) => {
22628
23599
  let fn;
22629
23600
  if (target === "claude") fn = teardownClaude;
22630
23601
  else if (target === "gemini") fn = teardownGemini;
23602
+ else if (target === "antigravity" || target === "agy") fn = teardownAntigravity;
23603
+ else if (target === "copilot") fn = teardownCopilot;
22631
23604
  else if (target === "cursor") fn = teardownCursor;
22632
23605
  else if (target === "codex") fn = teardownCodex;
22633
23606
  else if (target === "windsurf") fn = teardownWindsurf;
@@ -22637,7 +23610,7 @@ program.command("removefrom", { hidden: true }).description("Remove Node9 hooks
22637
23610
  else {
22638
23611
  console.error(
22639
23612
  chalk30.red(
22640
- `Unknown target: "${target}". Supported: claude, gemini, cursor, codex, windsurf, vscode, hermes, hud`
23613
+ `Unknown target: "${target}". Supported: claude, antigravity, copilot, gemini, cursor, codex, windsurf, vscode, hermes, hud`
22641
23614
  )
22642
23615
  );
22643
23616
  process.exit(1);
@@ -22894,6 +23867,9 @@ program.command("resume").description("Re-enable Node9 protection immediately").
22894
23867
  var HOOK_BASED_AGENTS = {
22895
23868
  claude: "claude",
22896
23869
  gemini: "gemini",
23870
+ antigravity: "antigravity",
23871
+ agy: "antigravity",
23872
+ copilot: "copilot",
22897
23873
  cursor: "cursor"
22898
23874
  };
22899
23875
  program.argument("[command...]", "The agent command to run (e.g., gemini)").action(async (commandArgs) => {