@node9/proxy 1.35.0 → 1.35.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/dist/cli.js +452 -354
  2. package/dist/cli.mjs +448 -350
  3. package/package.json +1 -1
package/dist/cli.mjs CHANGED
@@ -185,8 +185,8 @@ function sanitizeConfig(raw) {
185
185
  }
186
186
  }
187
187
  const lines = result.error.issues.map((issue) => {
188
- const path54 = issue.path.length > 0 ? issue.path.join(".") : "root";
189
- return ` \u2022 ${path54}: ${issue.message}`;
188
+ const path55 = issue.path.length > 0 ? issue.path.join(".") : "root";
189
+ return ` \u2022 ${path55}: ${issue.message}`;
190
190
  });
191
191
  return {
192
192
  sanitized,
@@ -1240,9 +1240,9 @@ function matchesPattern(text, patterns) {
1240
1240
  const withoutDotSlash = text.replace(/^\.\//, "");
1241
1241
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1242
1242
  }
1243
- function getNestedValue(obj, path54) {
1243
+ function getNestedValue(obj, path55) {
1244
1244
  if (!obj || typeof obj !== "object") return null;
1245
- const segments = path54.split(".");
1245
+ const segments = path55.split(".");
1246
1246
  for (const seg of segments) {
1247
1247
  if (FORBIDDEN_PATH_SEGMENTS.has(seg)) return null;
1248
1248
  }
@@ -16904,9 +16904,9 @@ __export(tail_exports, {
16904
16904
  });
16905
16905
  import http2 from "http";
16906
16906
  import chalk29 from "chalk";
16907
- import fs50 from "fs";
16908
- import os45 from "os";
16909
- import path51 from "path";
16907
+ import fs51 from "fs";
16908
+ import os46 from "os";
16909
+ import path52 from "path";
16910
16910
  import readline6 from "readline";
16911
16911
  import { spawn as spawn8 } from "child_process";
16912
16912
  function shortenPathSummary(s) {
@@ -16930,20 +16930,20 @@ function getModelContextLimit(model) {
16930
16930
  return 2e5;
16931
16931
  }
16932
16932
  function readSessionUsage() {
16933
- const projectsDir = path51.join(os45.homedir(), ".claude", "projects");
16934
- if (!fs50.existsSync(projectsDir)) return null;
16933
+ const projectsDir = path52.join(os46.homedir(), ".claude", "projects");
16934
+ if (!fs51.existsSync(projectsDir)) return null;
16935
16935
  let latestFile = null;
16936
16936
  let latestMtime = 0;
16937
16937
  try {
16938
- for (const dir of fs50.readdirSync(projectsDir)) {
16939
- const dirPath = path51.join(projectsDir, dir);
16938
+ for (const dir of fs51.readdirSync(projectsDir)) {
16939
+ const dirPath = path52.join(projectsDir, dir);
16940
16940
  try {
16941
- if (!fs50.statSync(dirPath).isDirectory()) continue;
16942
- for (const file of fs50.readdirSync(dirPath)) {
16941
+ if (!fs51.statSync(dirPath).isDirectory()) continue;
16942
+ for (const file of fs51.readdirSync(dirPath)) {
16943
16943
  if (!file.endsWith(".jsonl") || file.startsWith("agent-")) continue;
16944
- const filePath = path51.join(dirPath, file);
16944
+ const filePath = path52.join(dirPath, file);
16945
16945
  try {
16946
- const mtime = fs50.statSync(filePath).mtimeMs;
16946
+ const mtime = fs51.statSync(filePath).mtimeMs;
16947
16947
  if (mtime > latestMtime) {
16948
16948
  latestMtime = mtime;
16949
16949
  latestFile = filePath;
@@ -16958,7 +16958,7 @@ function readSessionUsage() {
16958
16958
  }
16959
16959
  if (!latestFile) return null;
16960
16960
  try {
16961
- const lines = fs50.readFileSync(latestFile, "utf-8").split("\n");
16961
+ const lines = fs51.readFileSync(latestFile, "utf-8").split("\n");
16962
16962
  let lastModel = "";
16963
16963
  let lastInput = 0;
16964
16964
  let lastOutput = 0;
@@ -17019,7 +17019,7 @@ function formatBase(activity) {
17019
17019
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
17020
17020
  const icon = getIcon(activity.tool);
17021
17021
  const toolName = activity.tool.slice(0, 16).padEnd(16);
17022
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os45.homedir(), "~");
17022
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(os46.homedir(), "~");
17023
17023
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
17024
17024
  return `${chalk29.gray(time)} ${icon} ${agentLabel(activity.agent, activity.mcpServer, activity.sessionId)}${chalk29.white.bold(toolName)} ${chalk29.dim(argsPreview)}`;
17025
17025
  }
@@ -17058,9 +17058,9 @@ function renderPending(activity) {
17058
17058
  }
17059
17059
  async function ensureDaemon() {
17060
17060
  let pidPort = null;
17061
- if (fs50.existsSync(PID_FILE)) {
17061
+ if (fs51.existsSync(PID_FILE)) {
17062
17062
  try {
17063
- const { port } = JSON.parse(fs50.readFileSync(PID_FILE, "utf-8"));
17063
+ const { port } = JSON.parse(fs51.readFileSync(PID_FILE, "utf-8"));
17064
17064
  pidPort = port;
17065
17065
  } catch {
17066
17066
  console.error(chalk29.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -17216,9 +17216,9 @@ function buildRecoveryCardLines(req) {
17216
17216
  ];
17217
17217
  }
17218
17218
  function readApproversFromDisk() {
17219
- const configPath = path51.join(os45.homedir(), ".node9", "config.json");
17219
+ const configPath = path52.join(os46.homedir(), ".node9", "config.json");
17220
17220
  try {
17221
- const raw = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
17221
+ const raw = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
17222
17222
  const settings = raw.settings ?? {};
17223
17223
  return settings.approvers ?? {};
17224
17224
  } catch {
@@ -17234,15 +17234,15 @@ function approverStatusLine() {
17234
17234
  return `${fmt("native", "native")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
17235
17235
  }
17236
17236
  function toggleApprover(channel) {
17237
- const configPath = path51.join(os45.homedir(), ".node9", "config.json");
17237
+ const configPath = path52.join(os46.homedir(), ".node9", "config.json");
17238
17238
  try {
17239
- const raw = JSON.parse(fs50.readFileSync(configPath, "utf-8"));
17239
+ const raw = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
17240
17240
  const settings = raw.settings ?? {};
17241
17241
  const approvers = settings.approvers ?? {};
17242
17242
  approvers[channel] = approvers[channel] === false;
17243
17243
  settings.approvers = approvers;
17244
17244
  raw.settings = settings;
17245
- fs50.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17245
+ fs51.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
17246
17246
  } catch (err2) {
17247
17247
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
17248
17248
  `);
@@ -17414,8 +17414,8 @@ async function startTail(options = {}) {
17414
17414
  }
17415
17415
  postDecisionHttp(req2.id, httpDecision, authToken, port, httpOpts).catch((err2) => {
17416
17416
  try {
17417
- fs50.appendFileSync(
17418
- path51.join(os45.homedir(), ".node9", "hook-debug.log"),
17417
+ fs51.appendFileSync(
17418
+ path52.join(os46.homedir(), ".node9", "hook-debug.log"),
17419
17419
  `[tail] POST /decision failed: ${String(err2)}
17420
17420
  `
17421
17421
  );
@@ -17479,9 +17479,9 @@ async function startTail(options = {}) {
17479
17479
  };
17480
17480
  process.stdin.on("keypress", onKeypress);
17481
17481
  }
17482
- const auditLog = path51.join(os45.homedir(), ".node9", "audit.log");
17482
+ const auditLog = path52.join(os46.homedir(), ".node9", "audit.log");
17483
17483
  try {
17484
- const unackedDlp = fs50.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17484
+ const unackedDlp = fs51.readFileSync(auditLog, "utf-8").split("\n").filter((l) => l.includes('"response-dlp"')).length;
17485
17485
  if (unackedDlp > 0) {
17486
17486
  console.log("");
17487
17487
  console.log(
@@ -17521,7 +17521,7 @@ async function startTail(options = {}) {
17521
17521
  if (stallWarned) return;
17522
17522
  if (Date.now() - lastActivityFromDaemon < STALL_THRESHOLD_MS) return;
17523
17523
  try {
17524
- const auditMtime = fs50.statSync(auditLog).mtimeMs;
17524
+ const auditMtime = fs51.statSync(auditLog).mtimeMs;
17525
17525
  if (Date.now() - auditMtime >= STALL_THRESHOLD_MS) return;
17526
17526
  console.log("");
17527
17527
  console.log(
@@ -17712,7 +17712,7 @@ var init_tail = __esm({
17712
17712
  "use strict";
17713
17713
  init_daemon2();
17714
17714
  init_daemon();
17715
- PID_FILE = path51.join(os45.homedir(), ".node9", "daemon.pid");
17715
+ PID_FILE = path52.join(os46.homedir(), ".node9", "daemon.pid");
17716
17716
  ICONS = {
17717
17717
  bash: "\u{1F4BB}",
17718
17718
  shell: "\u{1F4BB}",
@@ -17760,9 +17760,9 @@ __export(hud_exports, {
17760
17760
  main: () => main,
17761
17761
  renderEnvironmentLine: () => renderEnvironmentLine
17762
17762
  });
17763
- import fs51 from "fs";
17764
- import path52 from "path";
17765
- import os46 from "os";
17763
+ import fs52 from "fs";
17764
+ import path53 from "path";
17765
+ import os47 from "os";
17766
17766
  import http3 from "http";
17767
17767
  async function readStdin() {
17768
17768
  const chunks = [];
@@ -17838,9 +17838,9 @@ function formatTimeLeft(resetsAt) {
17838
17838
  return ` (${m}m left)`;
17839
17839
  }
17840
17840
  function safeReadJson(filePath) {
17841
- if (!fs51.existsSync(filePath)) return null;
17841
+ if (!fs52.existsSync(filePath)) return null;
17842
17842
  try {
17843
- return JSON.parse(fs51.readFileSync(filePath, "utf-8"));
17843
+ return JSON.parse(fs52.readFileSync(filePath, "utf-8"));
17844
17844
  } catch {
17845
17845
  return null;
17846
17846
  }
@@ -17861,12 +17861,12 @@ function countHooksInFile(filePath) {
17861
17861
  return Object.keys(cfg.hooks).length;
17862
17862
  }
17863
17863
  function countRulesInDir(rulesDir) {
17864
- if (!fs51.existsSync(rulesDir)) return 0;
17864
+ if (!fs52.existsSync(rulesDir)) return 0;
17865
17865
  let count = 0;
17866
17866
  try {
17867
- for (const entry of fs51.readdirSync(rulesDir, { withFileTypes: true })) {
17867
+ for (const entry of fs52.readdirSync(rulesDir, { withFileTypes: true })) {
17868
17868
  if (entry.isDirectory()) {
17869
- count += countRulesInDir(path52.join(rulesDir, entry.name));
17869
+ count += countRulesInDir(path53.join(rulesDir, entry.name));
17870
17870
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
17871
17871
  count++;
17872
17872
  }
@@ -17877,46 +17877,46 @@ function countRulesInDir(rulesDir) {
17877
17877
  }
17878
17878
  function isSamePath(a, b) {
17879
17879
  try {
17880
- return path52.resolve(a) === path52.resolve(b);
17880
+ return path53.resolve(a) === path53.resolve(b);
17881
17881
  } catch {
17882
17882
  return false;
17883
17883
  }
17884
17884
  }
17885
17885
  function countConfigs(cwd) {
17886
- const homeDir2 = os46.homedir();
17887
- const claudeDir = path52.join(homeDir2, ".claude");
17886
+ const homeDir2 = os47.homedir();
17887
+ const claudeDir = path53.join(homeDir2, ".claude");
17888
17888
  let claudeMdCount = 0;
17889
17889
  let rulesCount = 0;
17890
17890
  let hooksCount = 0;
17891
17891
  const userMcpServers = /* @__PURE__ */ new Set();
17892
17892
  const projectMcpServers = /* @__PURE__ */ new Set();
17893
- if (fs51.existsSync(path52.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17894
- rulesCount += countRulesInDir(path52.join(claudeDir, "rules"));
17895
- const userSettings = path52.join(claudeDir, "settings.json");
17893
+ if (fs52.existsSync(path53.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
17894
+ rulesCount += countRulesInDir(path53.join(claudeDir, "rules"));
17895
+ const userSettings = path53.join(claudeDir, "settings.json");
17896
17896
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
17897
17897
  hooksCount += countHooksInFile(userSettings);
17898
- const userClaudeJson = path52.join(homeDir2, ".claude.json");
17898
+ const userClaudeJson = path53.join(homeDir2, ".claude.json");
17899
17899
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
17900
17900
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
17901
17901
  userMcpServers.delete(name);
17902
17902
  }
17903
17903
  if (cwd) {
17904
- if (fs51.existsSync(path52.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17905
- if (fs51.existsSync(path52.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17906
- const projectClaudeDir = path52.join(cwd, ".claude");
17904
+ if (fs52.existsSync(path53.join(cwd, "CLAUDE.md"))) claudeMdCount++;
17905
+ if (fs52.existsSync(path53.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
17906
+ const projectClaudeDir = path53.join(cwd, ".claude");
17907
17907
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
17908
17908
  if (!overlapsUserScope) {
17909
- if (fs51.existsSync(path52.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17910
- rulesCount += countRulesInDir(path52.join(projectClaudeDir, "rules"));
17911
- const projSettings = path52.join(projectClaudeDir, "settings.json");
17909
+ if (fs52.existsSync(path53.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
17910
+ rulesCount += countRulesInDir(path53.join(projectClaudeDir, "rules"));
17911
+ const projSettings = path53.join(projectClaudeDir, "settings.json");
17912
17912
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
17913
17913
  hooksCount += countHooksInFile(projSettings);
17914
17914
  }
17915
- if (fs51.existsSync(path52.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17916
- const localSettings = path52.join(projectClaudeDir, "settings.local.json");
17915
+ if (fs52.existsSync(path53.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
17916
+ const localSettings = path53.join(projectClaudeDir, "settings.local.json");
17917
17917
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
17918
17918
  hooksCount += countHooksInFile(localSettings);
17919
- const mcpJsonServers = getMcpServerNames(path52.join(cwd, ".mcp.json"));
17919
+ const mcpJsonServers = getMcpServerNames(path53.join(cwd, ".mcp.json"));
17920
17920
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
17921
17921
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
17922
17922
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -17949,12 +17949,12 @@ function readActiveShieldsHud() {
17949
17949
  return shieldsCache.value;
17950
17950
  }
17951
17951
  try {
17952
- const shieldsPath = path52.join(os46.homedir(), ".node9", "shields.json");
17953
- if (!fs51.existsSync(shieldsPath)) {
17952
+ const shieldsPath = path53.join(os47.homedir(), ".node9", "shields.json");
17953
+ if (!fs52.existsSync(shieldsPath)) {
17954
17954
  shieldsCache = { value: [], ts: now };
17955
17955
  return [];
17956
17956
  }
17957
- const parsed = JSON.parse(fs51.readFileSync(shieldsPath, "utf-8"));
17957
+ const parsed = JSON.parse(fs52.readFileSync(shieldsPath, "utf-8"));
17958
17958
  if (!Array.isArray(parsed.active)) {
17959
17959
  shieldsCache = { value: [], ts: now };
17960
17960
  return [];
@@ -18056,17 +18056,17 @@ function renderContextLine(stdin) {
18056
18056
  async function main() {
18057
18057
  try {
18058
18058
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
18059
- if (fs51.existsSync(path52.join(os46.homedir(), ".node9", "hud-debug"))) {
18059
+ if (fs52.existsSync(path53.join(os47.homedir(), ".node9", "hud-debug"))) {
18060
18060
  try {
18061
- const logPath = path52.join(os46.homedir(), ".node9", "hud-debug.log");
18061
+ const logPath = path53.join(os47.homedir(), ".node9", "hud-debug.log");
18062
18062
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
18063
18063
  let size = 0;
18064
18064
  try {
18065
- size = fs51.statSync(logPath).size;
18065
+ size = fs52.statSync(logPath).size;
18066
18066
  } catch {
18067
18067
  }
18068
18068
  if (size < MAX_LOG_SIZE) {
18069
- fs51.appendFileSync(
18069
+ fs52.appendFileSync(
18070
18070
  logPath,
18071
18071
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
18072
18072
  );
@@ -18087,11 +18087,11 @@ async function main() {
18087
18087
  try {
18088
18088
  const cwd = stdin.cwd ?? process.cwd();
18089
18089
  for (const configPath of [
18090
- path52.join(cwd, "node9.config.json"),
18091
- path52.join(os46.homedir(), ".node9", "config.json")
18090
+ path53.join(cwd, "node9.config.json"),
18091
+ path53.join(os47.homedir(), ".node9", "config.json")
18092
18092
  ]) {
18093
- if (!fs51.existsSync(configPath)) continue;
18094
- const cfg = JSON.parse(fs51.readFileSync(configPath, "utf-8"));
18093
+ if (!fs52.existsSync(configPath)) continue;
18094
+ const cfg = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
18095
18095
  const hud = cfg.settings?.hud;
18096
18096
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
18097
18097
  }
@@ -18138,9 +18138,9 @@ init_setup();
18138
18138
  init_daemon2();
18139
18139
  import { Command } from "commander";
18140
18140
  import chalk30 from "chalk";
18141
- import fs52 from "fs";
18142
- import path53 from "path";
18143
- import os47 from "os";
18141
+ import fs53 from "fs";
18142
+ import path54 from "path";
18143
+ import os48 from "os";
18144
18144
  import { confirm as confirm2 } from "@inquirer/prompts";
18145
18145
 
18146
18146
  // src/utils/duration.ts
@@ -19930,13 +19930,149 @@ function registerConfigShowCommand(program2) {
19930
19930
  init_daemon();
19931
19931
  init_config();
19932
19932
  import chalk11 from "chalk";
19933
+ import fs39 from "fs";
19934
+ import path40 from "path";
19935
+ import os35 from "os";
19936
+ import { execSync } from "child_process";
19937
+
19938
+ // src/agent-wiring.ts
19939
+ init_setup();
19933
19940
  import fs38 from "fs";
19934
19941
  import path39 from "path";
19935
19942
  import os34 from "os";
19936
- import { execSync } from "child_process";
19943
+ import * as yaml2 from "yaml";
19944
+ function readJson2(filePath) {
19945
+ if (!fs38.existsSync(filePath)) return null;
19946
+ try {
19947
+ return JSON.parse(fs38.readFileSync(filePath, "utf-8"));
19948
+ } catch {
19949
+ return "invalid";
19950
+ }
19951
+ }
19952
+ function matchersHaveNode9Hook(matchers) {
19953
+ return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
19954
+ }
19955
+ function flatHaveNode9Hook(entries) {
19956
+ return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
19957
+ }
19958
+ function jsonWire(filePath, read) {
19959
+ const parsed = readJson2(filePath);
19960
+ if (parsed === null) return "absent";
19961
+ if (parsed === "invalid") return "invalid";
19962
+ return read(parsed) ? "wired" : "unwired";
19963
+ }
19964
+ function hermesWire(home) {
19965
+ const configPath = hermesConfigPath(home);
19966
+ if (!fs38.existsSync(configPath)) return "absent";
19967
+ let raw;
19968
+ try {
19969
+ raw = fs38.readFileSync(configPath, "utf-8");
19970
+ } catch {
19971
+ return "absent";
19972
+ }
19973
+ try {
19974
+ const cfg = yaml2.parse(raw);
19975
+ const pre = (cfg?.hooks?.pre_tool_call ?? []).some(
19976
+ (e) => typeof e?.command === "string" && isNode9Hook(e.command)
19977
+ );
19978
+ return pre ? "wired" : "unwired";
19979
+ } catch {
19980
+ return "invalid";
19981
+ }
19982
+ }
19983
+ var AGENT_SPECS = [
19984
+ {
19985
+ id: "claude",
19986
+ label: "Claude Code",
19987
+ hookLabel: "PreToolUse hook",
19988
+ setupCommand: "node9 setup claude",
19989
+ settingsPath: (h) => path39.join(h, ".claude", "settings.json"),
19990
+ wireState: (h) => jsonWire(
19991
+ path39.join(h, ".claude", "settings.json"),
19992
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
19993
+ )
19994
+ },
19995
+ {
19996
+ id: "gemini",
19997
+ label: "Gemini CLI",
19998
+ hookLabel: "BeforeTool hook",
19999
+ setupCommand: "node9 setup gemini",
20000
+ settingsPath: (h) => path39.join(h, ".gemini", "settings.json"),
20001
+ wireState: (h) => jsonWire(
20002
+ path39.join(h, ".gemini", "settings.json"),
20003
+ (p) => matchersHaveNode9Hook(p.hooks?.BeforeTool)
20004
+ )
20005
+ },
20006
+ {
20007
+ id: "codex",
20008
+ label: "Codex",
20009
+ hookLabel: "PreToolUse hook",
20010
+ setupCommand: "node9 setup codex",
20011
+ settingsPath: (h) => path39.join(h, ".codex", "hooks.json"),
20012
+ wireState: (h) => jsonWire(
20013
+ path39.join(h, ".codex", "hooks.json"),
20014
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20015
+ )
20016
+ },
20017
+ {
20018
+ id: "antigravity",
20019
+ label: "Antigravity",
20020
+ hookLabel: "PreToolUse hook",
20021
+ setupCommand: "node9 setup antigravity",
20022
+ settingsPath: (h) => path39.join(h, ".gemini", "config", "hooks.json"),
20023
+ wireState: (h) => jsonWire(
20024
+ path39.join(h, ".gemini", "config", "hooks.json"),
20025
+ (p) => matchersHaveNode9Hook(p.hooks?.PreToolUse)
20026
+ )
20027
+ },
20028
+ {
20029
+ id: "copilot",
20030
+ label: "GitHub Copilot",
20031
+ hookLabel: "PreToolUse hook",
20032
+ setupCommand: "node9 setup copilot",
20033
+ settingsPath: (h) => path39.join(h, ".copilot", "hooks", "node9.json"),
20034
+ wireState: (h) => jsonWire(
20035
+ path39.join(h, ".copilot", "hooks", "node9.json"),
20036
+ (p) => flatHaveNode9Hook(p.hooks?.PreToolUse)
20037
+ )
20038
+ },
20039
+ {
20040
+ id: "cursor",
20041
+ label: "Cursor",
20042
+ hookLabel: "preToolUse hook",
20043
+ setupCommand: "node9 setup cursor",
20044
+ settingsPath: (h) => path39.join(h, ".cursor", "hooks.json"),
20045
+ wireState: (h) => jsonWire(
20046
+ path39.join(h, ".cursor", "hooks.json"),
20047
+ (p) => flatHaveNode9Hook(p.hooks?.preToolUse)
20048
+ )
20049
+ },
20050
+ {
20051
+ id: "hermes",
20052
+ label: "Hermes Agent",
20053
+ hookLabel: "pre_tool_call hook",
20054
+ setupCommand: "node9 setup hermes",
20055
+ settingsPath: (h) => hermesConfigPath(h),
20056
+ wireState: (h) => hermesWire(h)
20057
+ }
20058
+ ];
20059
+ function getAgentWiring(home = os34.homedir()) {
20060
+ const detected = detectAgents(home);
20061
+ return AGENT_SPECS.map((spec) => ({
20062
+ id: spec.id,
20063
+ label: spec.label,
20064
+ hookLabel: spec.hookLabel,
20065
+ setupCommand: spec.setupCommand,
20066
+ settingsPath: spec.settingsPath(home),
20067
+ installed: detected[spec.id],
20068
+ wireState: spec.wireState(home)
20069
+ }));
20070
+ }
20071
+
20072
+ // src/cli/commands/doctor.ts
19937
20073
  function registerDoctorCommand(program2, version2) {
19938
20074
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(async () => {
19939
- const homeDir2 = os34.homedir();
20075
+ const homeDir2 = os35.homedir();
19940
20076
  let failures = 0;
19941
20077
  function pass(msg) {
19942
20078
  console.log(chalk11.green(" \u2705 ") + msg);
@@ -19985,10 +20121,10 @@ function registerDoctorCommand(program2, version2) {
19985
20121
  );
19986
20122
  }
19987
20123
  section("Configuration");
19988
- const globalConfigPath = path39.join(homeDir2, ".node9", "config.json");
19989
- if (fs38.existsSync(globalConfigPath)) {
20124
+ const globalConfigPath = path40.join(homeDir2, ".node9", "config.json");
20125
+ if (fs39.existsSync(globalConfigPath)) {
19990
20126
  try {
19991
- JSON.parse(fs38.readFileSync(globalConfigPath, "utf-8"));
20127
+ JSON.parse(fs39.readFileSync(globalConfigPath, "utf-8"));
19992
20128
  pass("~/.node9/config.json found and valid");
19993
20129
  } catch {
19994
20130
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -19996,10 +20132,10 @@ function registerDoctorCommand(program2, version2) {
19996
20132
  } else {
19997
20133
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
19998
20134
  }
19999
- const projectConfigPath = path39.join(process.cwd(), "node9.config.json");
20000
- if (fs38.existsSync(projectConfigPath)) {
20135
+ const projectConfigPath = path40.join(process.cwd(), "node9.config.json");
20136
+ if (fs39.existsSync(projectConfigPath)) {
20001
20137
  try {
20002
- JSON.parse(fs38.readFileSync(projectConfigPath, "utf-8"));
20138
+ JSON.parse(fs39.readFileSync(projectConfigPath, "utf-8"));
20003
20139
  pass("node9.config.json found and valid (project)");
20004
20140
  } catch {
20005
20141
  fail(
@@ -20008,8 +20144,8 @@ function registerDoctorCommand(program2, version2) {
20008
20144
  );
20009
20145
  }
20010
20146
  }
20011
- const credsPath = path39.join(homeDir2, ".node9", "credentials.json");
20012
- if (fs38.existsSync(credsPath)) {
20147
+ const credsPath = path40.join(homeDir2, ".node9", "credentials.json");
20148
+ if (fs39.existsSync(credsPath)) {
20013
20149
  pass("Cloud credentials found (~/.node9/credentials.json)");
20014
20150
  } else {
20015
20151
  warn(
@@ -20018,62 +20154,24 @@ function registerDoctorCommand(program2, version2) {
20018
20154
  );
20019
20155
  }
20020
20156
  section("Agent Hooks");
20021
- const claudeSettingsPath = path39.join(homeDir2, ".claude", "settings.json");
20022
- if (fs38.existsSync(claudeSettingsPath)) {
20023
- try {
20024
- const cs = JSON.parse(fs38.readFileSync(claudeSettingsPath, "utf-8"));
20025
- const hasHook = cs.hooks?.PreToolUse?.some(
20026
- (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
20027
- );
20028
- if (hasHook) pass("Claude Code \u2014 PreToolUse hook active");
20029
- else
20030
- fail(
20031
- "Claude Code \u2014 hooks file found but node9 hook missing",
20032
- "Run: node9 setup claude"
20033
- );
20034
- } catch {
20035
- fail("Claude Code \u2014 ~/.claude/settings.json is invalid JSON");
20036
- }
20037
- } else {
20038
- warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
20039
- }
20040
- const geminiSettingsPath = path39.join(homeDir2, ".gemini", "settings.json");
20041
- if (fs38.existsSync(geminiSettingsPath)) {
20042
- try {
20043
- const gs = JSON.parse(fs38.readFileSync(geminiSettingsPath, "utf-8"));
20044
- const hasHook = gs.hooks?.BeforeTool?.some(
20045
- (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
20046
- );
20047
- if (hasHook) pass("Gemini CLI \u2014 BeforeTool hook active");
20048
- else
20049
- fail(
20050
- "Gemini CLI \u2014 hooks file found but node9 hook missing",
20051
- "Run: node9 setup gemini"
20052
- );
20053
- } catch {
20054
- fail("Gemini CLI \u2014 ~/.gemini/settings.json is invalid JSON");
20157
+ const notConfigured = [];
20158
+ for (const a of getAgentWiring(homeDir2)) {
20159
+ if (a.wireState === "wired") {
20160
+ pass(`${a.label} \u2014 ${a.hookLabel} active`);
20161
+ } else if (a.wireState === "unwired") {
20162
+ fail(`${a.label} \u2014 settings found but node9 hook missing`, `Run: ${a.setupCommand}`);
20163
+ } else if (a.wireState === "invalid") {
20164
+ fail(`${a.label} \u2014 settings file is invalid JSON`, a.settingsPath);
20165
+ } else {
20166
+ notConfigured.push(a.label);
20055
20167
  }
20056
- } else {
20057
- warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
20058
20168
  }
20059
- const cursorHooksPath = path39.join(homeDir2, ".cursor", "hooks.json");
20060
- if (fs38.existsSync(cursorHooksPath)) {
20061
- try {
20062
- const cur = JSON.parse(fs38.readFileSync(cursorHooksPath, "utf-8"));
20063
- const hasHook = cur.hooks?.preToolUse?.some(
20064
- (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
20065
- );
20066
- if (hasHook) pass("Cursor \u2014 preToolUse hook active");
20067
- else
20068
- fail(
20069
- "Cursor \u2014 hooks file found but node9 hook missing",
20070
- "Run: node9 setup cursor"
20071
- );
20072
- } catch {
20073
- fail("Cursor \u2014 ~/.cursor/hooks.json is invalid JSON");
20074
- }
20075
- } else {
20076
- warn("Cursor \u2014 not configured", "Run: node9 setup cursor (skip if not using Cursor)");
20169
+ if (notConfigured.length > 0) {
20170
+ console.log(
20171
+ chalk11.gray(
20172
+ ` \xB7 Not configured: ${notConfigured.join(", ")} \u2014 run \`node9 setup <agent>\` if you use one`
20173
+ )
20174
+ );
20077
20175
  }
20078
20176
  section("Daemon (optional)");
20079
20177
  if (isDaemonRunning()) {
@@ -20090,7 +20188,7 @@ function registerDoctorCommand(program2, version2) {
20090
20188
  try {
20091
20189
  const { shipLagBytes: shipLagBytes2, readWatermark: readWatermark2, AUDIT_SHIP_WATERMARK: AUDIT_SHIP_WATERMARK2 } = await Promise.resolve().then(() => (init_audit_shipper(), audit_shipper_exports));
20092
20190
  const cfg = getConfig();
20093
- const creds = fs38.existsSync(path39.join(os34.homedir(), ".node9", "credentials.json"));
20191
+ const creds = fs39.existsSync(path40.join(os35.homedir(), ".node9", "credentials.json"));
20094
20192
  if (!creds) {
20095
20193
  warn("Not logged in \u2014 audit rows stay local", "Run: node9 login <api-key>");
20096
20194
  } else if (!cfg.settings.approvers.cloud) {
@@ -20140,9 +20238,9 @@ function registerDoctorCommand(program2, version2) {
20140
20238
 
20141
20239
  // src/cli/commands/audit.ts
20142
20240
  import chalk12 from "chalk";
20143
- import fs39 from "fs";
20144
- import path40 from "path";
20145
- import os35 from "os";
20241
+ import fs40 from "fs";
20242
+ import path41 from "path";
20243
+ import os36 from "os";
20146
20244
  function formatRelativeTime(timestamp) {
20147
20245
  const diff = Date.now() - new Date(timestamp).getTime();
20148
20246
  const sec = Math.floor(diff / 1e3);
@@ -20155,14 +20253,14 @@ function formatRelativeTime(timestamp) {
20155
20253
  }
20156
20254
  function registerAuditCommand(program2) {
20157
20255
  program2.command("audit").description("View local execution audit log").option("--tail <n>", "Number of entries to show", "20").option("--tool <pattern>", "Filter by tool name (substring match)").option("--deny", "Show only denied actions").option("--json", "Output raw JSON").action((options) => {
20158
- const logPath = path40.join(os35.homedir(), ".node9", "audit.log");
20159
- if (!fs39.existsSync(logPath)) {
20256
+ const logPath = path41.join(os36.homedir(), ".node9", "audit.log");
20257
+ if (!fs40.existsSync(logPath)) {
20160
20258
  console.log(
20161
20259
  chalk12.yellow("No audit logs found. Run node9 with an agent to generate entries.")
20162
20260
  );
20163
20261
  return;
20164
20262
  }
20165
- const raw = fs39.readFileSync(logPath, "utf-8");
20263
+ const raw = fs40.readFileSync(logPath, "utf-8");
20166
20264
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
20167
20265
  let entries = lines.flatMap((line) => {
20168
20266
  try {
@@ -20221,9 +20319,9 @@ import chalk13 from "chalk";
20221
20319
  init_costSync();
20222
20320
  init_litellm();
20223
20321
  init_cost_codex();
20224
- import fs40 from "fs";
20225
- import os36 from "os";
20226
- import path41 from "path";
20322
+ import fs41 from "fs";
20323
+ import os37 from "os";
20324
+ import path42 from "path";
20227
20325
  var TEST_COMMAND_RE3 = /(?:^|\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;
20228
20326
  function buildTestTimestamps(allEntries) {
20229
20327
  const testTs = /* @__PURE__ */ new Set();
@@ -20303,8 +20401,8 @@ function getDateRange(period, now) {
20303
20401
  }
20304
20402
  }
20305
20403
  function parseAuditLog(logPath) {
20306
- if (!fs40.existsSync(logPath)) return [];
20307
- const raw = fs40.readFileSync(logPath, "utf-8");
20404
+ if (!fs41.existsSync(logPath)) return [];
20405
+ const raw = fs41.readFileSync(logPath, "utf-8");
20308
20406
  return raw.split("\n").flatMap((line) => {
20309
20407
  if (!line.trim()) return [];
20310
20408
  try {
@@ -20351,25 +20449,25 @@ function freezeClaudeCost(acc) {
20351
20449
  };
20352
20450
  }
20353
20451
  function processClaudeCostProject(proj, projectsDir, start, end, acc) {
20354
- const projPath = path41.join(projectsDir, proj);
20452
+ const projPath = path42.join(projectsDir, proj);
20355
20453
  let files;
20356
20454
  try {
20357
- const stat = fs40.statSync(projPath);
20455
+ const stat = fs41.statSync(projPath);
20358
20456
  if (!stat.isDirectory()) return;
20359
- files = fs40.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
20457
+ files = fs41.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
20360
20458
  } catch {
20361
20459
  return;
20362
20460
  }
20363
20461
  const startMs = start.getTime();
20364
20462
  for (const file of files) {
20365
- const filePath = path41.join(projPath, file);
20463
+ const filePath = path42.join(projPath, file);
20366
20464
  try {
20367
- if (fs40.statSync(filePath).mtimeMs < startMs) continue;
20465
+ if (fs41.statSync(filePath).mtimeMs < startMs) continue;
20368
20466
  } catch {
20369
20467
  continue;
20370
20468
  }
20371
20469
  try {
20372
- const raw = fs40.readFileSync(filePath, "utf-8");
20470
+ const raw = fs41.readFileSync(filePath, "utf-8");
20373
20471
  for (const line of raw.split("\n")) {
20374
20472
  if (!line.trim()) continue;
20375
20473
  let entry;
@@ -20419,10 +20517,10 @@ function processClaudeCostProject(proj, projectsDir, start, end, acc) {
20419
20517
  }
20420
20518
  function loadClaudeCost(start, end, projectsDir) {
20421
20519
  const acc = emptyClaudeCostAccumulator();
20422
- if (!fs40.existsSync(projectsDir)) return freezeClaudeCost(acc);
20520
+ if (!fs41.existsSync(projectsDir)) return freezeClaudeCost(acc);
20423
20521
  let dirs;
20424
20522
  try {
20425
- dirs = fs40.readdirSync(projectsDir);
20523
+ dirs = fs41.readdirSync(projectsDir);
20426
20524
  } catch {
20427
20525
  return freezeClaudeCost(acc);
20428
20526
  }
@@ -20434,7 +20532,7 @@ function loadClaudeCost(start, end, projectsDir) {
20434
20532
  function processCodexCostFile(filePath, start, end, acc) {
20435
20533
  let lines;
20436
20534
  try {
20437
- lines = fs40.readFileSync(filePath, "utf-8").split("\n");
20535
+ lines = fs41.readFileSync(filePath, "utf-8").split("\n");
20438
20536
  } catch {
20439
20537
  return;
20440
20538
  }
@@ -20489,31 +20587,31 @@ function processCodexCostFile(filePath, start, end, acc) {
20489
20587
  }
20490
20588
  function listCodexSessionFiles2(sessionsBase) {
20491
20589
  const jsonlFiles = [];
20492
- if (!fs40.existsSync(sessionsBase)) return jsonlFiles;
20590
+ if (!fs41.existsSync(sessionsBase)) return jsonlFiles;
20493
20591
  try {
20494
- for (const year of fs40.readdirSync(sessionsBase)) {
20495
- const yearPath = path41.join(sessionsBase, year);
20592
+ for (const year of fs41.readdirSync(sessionsBase)) {
20593
+ const yearPath = path42.join(sessionsBase, year);
20496
20594
  try {
20497
- if (!fs40.statSync(yearPath).isDirectory()) continue;
20595
+ if (!fs41.statSync(yearPath).isDirectory()) continue;
20498
20596
  } catch {
20499
20597
  continue;
20500
20598
  }
20501
- for (const month of fs40.readdirSync(yearPath)) {
20502
- const monthPath = path41.join(yearPath, month);
20599
+ for (const month of fs41.readdirSync(yearPath)) {
20600
+ const monthPath = path42.join(yearPath, month);
20503
20601
  try {
20504
- if (!fs40.statSync(monthPath).isDirectory()) continue;
20602
+ if (!fs41.statSync(monthPath).isDirectory()) continue;
20505
20603
  } catch {
20506
20604
  continue;
20507
20605
  }
20508
- for (const day of fs40.readdirSync(monthPath)) {
20509
- const dayPath = path41.join(monthPath, day);
20606
+ for (const day of fs41.readdirSync(monthPath)) {
20607
+ const dayPath = path42.join(monthPath, day);
20510
20608
  try {
20511
- if (!fs40.statSync(dayPath).isDirectory()) continue;
20609
+ if (!fs41.statSync(dayPath).isDirectory()) continue;
20512
20610
  } catch {
20513
20611
  continue;
20514
20612
  }
20515
- for (const file of fs40.readdirSync(dayPath)) {
20516
- if (file.endsWith(".jsonl")) jsonlFiles.push(path41.join(dayPath, file));
20613
+ for (const file of fs41.readdirSync(dayPath)) {
20614
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path42.join(dayPath, file));
20517
20615
  }
20518
20616
  }
20519
20617
  }
@@ -20578,13 +20676,13 @@ function freezeGeminiCost(acc) {
20578
20676
  function processGeminiCostFile(filePath, projectKey, start, end, acc) {
20579
20677
  const startMs = start.getTime();
20580
20678
  try {
20581
- if (fs40.statSync(filePath).mtimeMs < startMs) return;
20679
+ if (fs41.statSync(filePath).mtimeMs < startMs) return;
20582
20680
  } catch {
20583
20681
  return;
20584
20682
  }
20585
20683
  let raw;
20586
20684
  try {
20587
- raw = fs40.readFileSync(filePath, "utf-8");
20685
+ raw = fs41.readFileSync(filePath, "utf-8");
20588
20686
  } catch {
20589
20687
  return;
20590
20688
  }
@@ -20633,30 +20731,30 @@ function listGeminiSessionFiles2(geminiTmpDir2) {
20633
20731
  const out = [];
20634
20732
  let dirs;
20635
20733
  try {
20636
- if (!fs40.statSync(geminiTmpDir2).isDirectory()) return out;
20637
- dirs = fs40.readdirSync(geminiTmpDir2);
20734
+ if (!fs41.statSync(geminiTmpDir2).isDirectory()) return out;
20735
+ dirs = fs41.readdirSync(geminiTmpDir2);
20638
20736
  } catch {
20639
20737
  return out;
20640
20738
  }
20641
20739
  for (const proj of dirs) {
20642
- const chatsDir = path41.join(geminiTmpDir2, proj, "chats");
20740
+ const chatsDir = path42.join(geminiTmpDir2, proj, "chats");
20643
20741
  let files;
20644
20742
  try {
20645
- if (!fs40.statSync(chatsDir).isDirectory()) continue;
20646
- files = fs40.readdirSync(chatsDir);
20743
+ if (!fs41.statSync(chatsDir).isDirectory()) continue;
20744
+ files = fs41.readdirSync(chatsDir);
20647
20745
  } catch {
20648
20746
  continue;
20649
20747
  }
20650
20748
  for (const f of files) {
20651
20749
  if (!f.endsWith(".jsonl")) continue;
20652
- out.push({ projectKey: proj, file: path41.join(chatsDir, f) });
20750
+ out.push({ projectKey: proj, file: path42.join(chatsDir, f) });
20653
20751
  }
20654
20752
  }
20655
20753
  return out;
20656
20754
  }
20657
20755
  function loadGeminiCost(start, end, geminiTmpDir2) {
20658
20756
  const acc = emptyGeminiAccumulator();
20659
- if (!fs40.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
20757
+ if (!fs41.existsSync(geminiTmpDir2)) return freezeGeminiCost(acc);
20660
20758
  for (const { projectKey, file } of listGeminiSessionFiles2(geminiTmpDir2)) {
20661
20759
  processGeminiCostFile(file, projectKey, start, end, acc);
20662
20760
  }
@@ -20664,11 +20762,11 @@ function loadGeminiCost(start, end, geminiTmpDir2) {
20664
20762
  }
20665
20763
  function aggregateReportFromAudit(period, opts = {}) {
20666
20764
  const now = opts.now ?? /* @__PURE__ */ new Date();
20667
- const auditLogPath = opts.auditLogPath ?? path41.join(os36.homedir(), ".node9", "audit.log");
20668
- const claudeProjectsDir = opts.claudeProjectsDir ?? path41.join(os36.homedir(), ".claude", "projects");
20669
- const codexSessionsDir2 = opts.codexSessionsDir ?? path41.join(os36.homedir(), ".codex", "sessions");
20670
- const geminiTmpDir2 = opts.geminiTmpDir ?? path41.join(os36.homedir(), ".gemini", "tmp");
20671
- const hasAuditFile = fs40.existsSync(auditLogPath);
20765
+ const auditLogPath = opts.auditLogPath ?? path42.join(os37.homedir(), ".node9", "audit.log");
20766
+ const claudeProjectsDir = opts.claudeProjectsDir ?? path42.join(os37.homedir(), ".claude", "projects");
20767
+ const codexSessionsDir2 = opts.codexSessionsDir ?? path42.join(os37.homedir(), ".codex", "sessions");
20768
+ const geminiTmpDir2 = opts.geminiTmpDir ?? path42.join(os37.homedir(), ".gemini", "tmp");
20769
+ const hasAuditFile = fs41.existsSync(auditLogPath);
20672
20770
  const allEntries = opts.preloadedAuditEntries ?? parseAuditLog(auditLogPath);
20673
20771
  const unackedDlp = allEntries.filter((e) => e.source === "response-dlp");
20674
20772
  const { start, end } = getDateRange(period, now);
@@ -21367,20 +21465,20 @@ init_core();
21367
21465
  init_daemon();
21368
21466
  init_setup();
21369
21467
  import chalk15 from "chalk";
21370
- import fs41 from "fs";
21371
- import path42 from "path";
21372
- import os37 from "os";
21373
- import * as yaml2 from "yaml";
21468
+ import fs42 from "fs";
21469
+ import path43 from "path";
21470
+ import os38 from "os";
21471
+ import * as yaml3 from "yaml";
21374
21472
  function readHermesHooks(configPath) {
21375
- if (!fs41.existsSync(configPath)) return null;
21473
+ if (!fs42.existsSync(configPath)) return null;
21376
21474
  let raw;
21377
21475
  try {
21378
- raw = fs41.readFileSync(configPath, "utf-8");
21476
+ raw = fs42.readFileSync(configPath, "utf-8");
21379
21477
  } catch {
21380
21478
  return null;
21381
21479
  }
21382
21480
  try {
21383
- const cfg = yaml2.parse(raw);
21481
+ const cfg = yaml3.parse(raw);
21384
21482
  const has = (event) => (cfg?.hooks?.[event] ?? []).some(
21385
21483
  (e) => typeof e?.command === "string" && isNode9Hook(e.command)
21386
21484
  );
@@ -21394,17 +21492,17 @@ function readHermesHooks(configPath) {
21394
21492
  return { pre: false, post: false };
21395
21493
  }
21396
21494
  }
21397
- function readJson2(filePath) {
21495
+ function readJson3(filePath) {
21398
21496
  try {
21399
- if (fs41.existsSync(filePath)) return JSON.parse(fs41.readFileSync(filePath, "utf-8"));
21497
+ if (fs42.existsSync(filePath)) return JSON.parse(fs42.readFileSync(filePath, "utf-8"));
21400
21498
  } catch {
21401
21499
  }
21402
21500
  return null;
21403
21501
  }
21404
- function matchersHaveNode9Hook(matchers) {
21502
+ function matchersHaveNode9Hook2(matchers) {
21405
21503
  return (matchers ?? []).some((m) => (m.hooks ?? []).some((h) => isNode9Hook(h.command)));
21406
21504
  }
21407
- function flatHaveNode9Hook(entries) {
21505
+ function flatHaveNode9Hook2(entries) {
21408
21506
  return (Array.isArray(entries) ? entries : []).some((h) => isNode9Hook(h.command));
21409
21507
  }
21410
21508
  function wrappedMcpServers(servers) {
@@ -21464,42 +21562,42 @@ function registerStatusCommand(program2) {
21464
21562
  console.log("");
21465
21563
  const modeLabel = settings.mode === "audit" ? chalk15.blue("audit") : settings.mode === "strict" ? chalk15.red("strict") : chalk15.white("standard");
21466
21564
  console.log(` Mode: ${modeLabel}`);
21467
- const projectConfig = path42.join(process.cwd(), "node9.config.json");
21468
- const globalConfig = path42.join(os37.homedir(), ".node9", "config.json");
21565
+ const projectConfig = path43.join(process.cwd(), "node9.config.json");
21566
+ const globalConfig = path43.join(os38.homedir(), ".node9", "config.json");
21469
21567
  console.log(
21470
- ` Local: ${fs41.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
21568
+ ` Local: ${fs42.existsSync(projectConfig) ? chalk15.green("Active (node9.config.json)") : chalk15.gray("Not present")}`
21471
21569
  );
21472
21570
  console.log(
21473
- ` Global: ${fs41.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
21571
+ ` Global: ${fs42.existsSync(globalConfig) ? chalk15.green("Active (~/.node9/config.json)") : chalk15.gray("Not present")}`
21474
21572
  );
21475
21573
  if (mergedConfig.policy.sandboxPaths.length > 0) {
21476
21574
  console.log(
21477
21575
  ` Sandbox: ${chalk15.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
21478
21576
  );
21479
21577
  }
21480
- const homeDir2 = os37.homedir();
21481
- const claudeSettings = readJson2(
21482
- path42.join(homeDir2, ".claude", "settings.json")
21578
+ const homeDir2 = os38.homedir();
21579
+ const claudeSettings = readJson3(
21580
+ path43.join(homeDir2, ".claude", "settings.json")
21483
21581
  );
21484
- const claudeConfig = readJson2(path42.join(homeDir2, ".claude.json"));
21485
- const geminiSettings = readJson2(
21486
- path42.join(homeDir2, ".gemini", "settings.json")
21582
+ const claudeConfig = readJson3(path43.join(homeDir2, ".claude.json"));
21583
+ const geminiSettings = readJson3(
21584
+ path43.join(homeDir2, ".gemini", "settings.json")
21487
21585
  );
21488
- const cursorConfig = readJson2(path42.join(homeDir2, ".cursor", "mcp.json"));
21489
- const antigravityHooks = readJson2(
21490
- path42.join(homeDir2, ".gemini", "config", "hooks.json")
21586
+ const cursorConfig = readJson3(path43.join(homeDir2, ".cursor", "mcp.json"));
21587
+ const antigravityHooks = readJson3(
21588
+ path43.join(homeDir2, ".gemini", "config", "hooks.json")
21491
21589
  );
21492
- const antigravityMcp = readJson2(
21493
- path42.join(homeDir2, ".gemini", "config", "mcp_config.json")
21590
+ const antigravityMcp = readJson3(
21591
+ path43.join(homeDir2, ".gemini", "config", "mcp_config.json")
21494
21592
  );
21495
- const antigravityPresent = antigravityHooks !== null || fs41.existsSync(path42.join(homeDir2, ".gemini", "antigravity-cli")) || fs41.existsSync(path42.join(homeDir2, ".gemini", "antigravity-ide"));
21496
- const copilotHooks = readJson2(
21497
- path42.join(homeDir2, ".copilot", "hooks", "node9.json")
21593
+ const antigravityPresent = antigravityHooks !== null || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-cli")) || fs42.existsSync(path43.join(homeDir2, ".gemini", "antigravity-ide"));
21594
+ const copilotHooks = readJson3(
21595
+ path43.join(homeDir2, ".copilot", "hooks", "node9.json")
21498
21596
  );
21499
- const copilotMcp = readJson2(
21500
- path42.join(homeDir2, ".copilot", "mcp-config.json")
21597
+ const copilotMcp = readJson3(
21598
+ path43.join(homeDir2, ".copilot", "mcp-config.json")
21501
21599
  );
21502
- const copilotPresent = fs41.existsSync(path42.join(homeDir2, ".copilot"));
21600
+ const copilotPresent = fs42.existsSync(path43.join(homeDir2, ".copilot"));
21503
21601
  const hermesHooks = readHermesHooks(hermesConfigPath(homeDir2));
21504
21602
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig || antigravityPresent || copilotPresent || hermesHooks;
21505
21603
  if (agentFound) {
@@ -21507,8 +21605,8 @@ function registerStatusCommand(program2) {
21507
21605
  console.log(chalk15.bold(" Agent Wiring:"));
21508
21606
  console.log("");
21509
21607
  if (claudeSettings || claudeConfig) {
21510
- const preHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PreToolUse);
21511
- const postHook = matchersHaveNode9Hook(claudeSettings?.hooks?.PostToolUse);
21608
+ const preHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PreToolUse);
21609
+ const postHook = matchersHaveNode9Hook2(claudeSettings?.hooks?.PostToolUse);
21512
21610
  printAgentSection(
21513
21611
  "Claude Code",
21514
21612
  [
@@ -21520,8 +21618,8 @@ function registerStatusCommand(program2) {
21520
21618
  console.log("");
21521
21619
  }
21522
21620
  if (geminiSettings) {
21523
- const beforeHook = matchersHaveNode9Hook(geminiSettings.hooks?.BeforeTool);
21524
- const afterHook = matchersHaveNode9Hook(geminiSettings.hooks?.AfterTool);
21621
+ const beforeHook = matchersHaveNode9Hook2(geminiSettings.hooks?.BeforeTool);
21622
+ const afterHook = matchersHaveNode9Hook2(geminiSettings.hooks?.AfterTool);
21525
21623
  printAgentSection(
21526
21624
  "Gemini CLI",
21527
21625
  [
@@ -21533,8 +21631,8 @@ function registerStatusCommand(program2) {
21533
21631
  console.log("");
21534
21632
  }
21535
21633
  if (antigravityPresent) {
21536
- const preHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PreToolUse);
21537
- const postHook = matchersHaveNode9Hook(antigravityHooks?.hooks?.PostToolUse);
21634
+ const preHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PreToolUse);
21635
+ const postHook = matchersHaveNode9Hook2(antigravityHooks?.hooks?.PostToolUse);
21538
21636
  printAgentSection(
21539
21637
  "Antigravity",
21540
21638
  [
@@ -21546,9 +21644,9 @@ function registerStatusCommand(program2) {
21546
21644
  console.log("");
21547
21645
  }
21548
21646
  if (copilotPresent) {
21549
- const preHook = flatHaveNode9Hook(copilotHooks?.hooks?.PreToolUse);
21550
- const postHook = flatHaveNode9Hook(copilotHooks?.hooks?.PostToolUse);
21551
- const promptHook = flatHaveNode9Hook(copilotHooks?.hooks?.UserPromptSubmit);
21647
+ const preHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PreToolUse);
21648
+ const postHook = flatHaveNode9Hook2(copilotHooks?.hooks?.PostToolUse);
21649
+ const promptHook = flatHaveNode9Hook2(copilotHooks?.hooks?.UserPromptSubmit);
21552
21650
  printAgentSection(
21553
21651
  "GitHub Copilot",
21554
21652
  [
@@ -21595,9 +21693,9 @@ init_setup();
21595
21693
  init_shields();
21596
21694
  init_service();
21597
21695
  import chalk16 from "chalk";
21598
- import fs42 from "fs";
21599
- import path43 from "path";
21600
- import os38 from "os";
21696
+ import fs43 from "fs";
21697
+ import path44 from "path";
21698
+ import os39 from "os";
21601
21699
  import https4 from "https";
21602
21700
  var DEFAULT_SHIELDS = ["bash-safe", "filesystem", "project-jail"];
21603
21701
  function buildTelemetryPayload(agents, firstInstall) {
@@ -21683,16 +21781,16 @@ function registerInitCommand(program2) {
21683
21781
  }
21684
21782
  console.log("");
21685
21783
  }
21686
- const configPath = path43.join(os38.homedir(), ".node9", "config.json");
21687
- const isFirstInstall = !fs42.existsSync(configPath);
21688
- if (fs42.existsSync(configPath) && !options.force) {
21784
+ const configPath = path44.join(os39.homedir(), ".node9", "config.json");
21785
+ const isFirstInstall = !fs43.existsSync(configPath);
21786
+ if (fs43.existsSync(configPath) && !options.force) {
21689
21787
  try {
21690
- const existing = JSON.parse(fs42.readFileSync(configPath, "utf-8"));
21788
+ const existing = JSON.parse(fs43.readFileSync(configPath, "utf-8"));
21691
21789
  const settings = existing.settings ?? {};
21692
21790
  if (settings.mode !== chosenMode) {
21693
21791
  settings.mode = chosenMode;
21694
21792
  existing.settings = settings;
21695
- fs42.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21793
+ fs43.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
21696
21794
  console.log(chalk16.green(`\u2705 Mode updated: ${chosenMode}`));
21697
21795
  } else {
21698
21796
  console.log(chalk16.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -21705,9 +21803,9 @@ function registerInitCommand(program2) {
21705
21803
  ...DEFAULT_CONFIG,
21706
21804
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
21707
21805
  };
21708
- const dir = path43.dirname(configPath);
21709
- if (!fs42.existsSync(dir)) fs42.mkdirSync(dir, { recursive: true });
21710
- fs42.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21806
+ const dir = path44.dirname(configPath);
21807
+ if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
21808
+ fs43.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
21711
21809
  console.log(chalk16.green(`\u2705 Config created: ${configPath}`));
21712
21810
  console.log(chalk16.gray(` Mode: ${chosenMode}`));
21713
21811
  }
@@ -21812,7 +21910,7 @@ function registerInitCommand(program2) {
21812
21910
  }
21813
21911
 
21814
21912
  // src/cli/commands/undo.ts
21815
- import path44 from "path";
21913
+ import path45 from "path";
21816
21914
  import chalk18 from "chalk";
21817
21915
 
21818
21916
  // src/tui/undo-navigator.ts
@@ -21971,7 +22069,7 @@ function findMatchingCwd(startDir, history) {
21971
22069
  let dir = startDir;
21972
22070
  while (true) {
21973
22071
  if (cwds.has(dir)) return dir;
21974
- const parent = path44.dirname(dir);
22072
+ const parent = path45.dirname(dir);
21975
22073
  if (parent === dir) return null;
21976
22074
  dir = parent;
21977
22075
  }
@@ -22606,9 +22704,9 @@ function registerMcpGatewayCommand(program2) {
22606
22704
 
22607
22705
  // src/mcp-server/index.ts
22608
22706
  import readline5 from "readline";
22609
- import fs43 from "fs";
22610
- import os39 from "os";
22611
- import path45 from "path";
22707
+ import fs44 from "fs";
22708
+ import os40 from "os";
22709
+ import path46 from "path";
22612
22710
  import { spawnSync as spawnSync4 } from "child_process";
22613
22711
  init_core();
22614
22712
  init_daemon();
@@ -22859,13 +22957,13 @@ function handleStatus() {
22859
22957
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
22860
22958
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
22861
22959
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
22862
- const projectConfig = path45.join(process.cwd(), "node9.config.json");
22863
- const globalConfig = path45.join(os39.homedir(), ".node9", "config.json");
22960
+ const projectConfig = path46.join(process.cwd(), "node9.config.json");
22961
+ const globalConfig = path46.join(os40.homedir(), ".node9", "config.json");
22864
22962
  lines.push(
22865
- `Project config (node9.config.json): ${fs43.existsSync(projectConfig) ? "present" : "not found"}`
22963
+ `Project config (node9.config.json): ${fs44.existsSync(projectConfig) ? "present" : "not found"}`
22866
22964
  );
22867
22965
  lines.push(
22868
- `Global config (~/.node9/config.json): ${fs43.existsSync(globalConfig) ? "present" : "not found"}`
22966
+ `Global config (~/.node9/config.json): ${fs44.existsSync(globalConfig) ? "present" : "not found"}`
22869
22967
  );
22870
22968
  return lines.join("\n");
22871
22969
  }
@@ -22939,21 +23037,21 @@ function handleShieldDisable(args) {
22939
23037
  writeActiveShields(active.filter((s) => s !== name));
22940
23038
  return `Shield "${name}" disabled.`;
22941
23039
  }
22942
- var GLOBAL_CONFIG_PATH = path45.join(os39.homedir(), ".node9", "config.json");
23040
+ var GLOBAL_CONFIG_PATH = path46.join(os40.homedir(), ".node9", "config.json");
22943
23041
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
22944
23042
  function readGlobalConfigRaw() {
22945
23043
  try {
22946
- if (fs43.existsSync(GLOBAL_CONFIG_PATH)) {
22947
- return JSON.parse(fs43.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
23044
+ if (fs44.existsSync(GLOBAL_CONFIG_PATH)) {
23045
+ return JSON.parse(fs44.readFileSync(GLOBAL_CONFIG_PATH, "utf-8"));
22948
23046
  }
22949
23047
  } catch {
22950
23048
  }
22951
23049
  return {};
22952
23050
  }
22953
23051
  function writeGlobalConfigRaw(data) {
22954
- const dir = path45.dirname(GLOBAL_CONFIG_PATH);
22955
- if (!fs43.existsSync(dir)) fs43.mkdirSync(dir, { recursive: true });
22956
- fs43.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
23052
+ const dir = path46.dirname(GLOBAL_CONFIG_PATH);
23053
+ if (!fs44.existsSync(dir)) fs44.mkdirSync(dir, { recursive: true });
23054
+ fs44.writeFileSync(GLOBAL_CONFIG_PATH, JSON.stringify(data, null, 2) + "\n");
22957
23055
  }
22958
23056
  function handleApproverList() {
22959
23057
  const config = getConfig();
@@ -22997,9 +23095,9 @@ function handleApproverSet(args) {
22997
23095
  function handleAuditGet(args) {
22998
23096
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
22999
23097
  const filter = typeof args.filter === "string" && args.filter !== "all" ? args.filter : null;
23000
- const auditPath = path45.join(os39.homedir(), ".node9", "audit.log");
23001
- if (!fs43.existsSync(auditPath)) return "No audit log found.";
23002
- const rawLines = fs43.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
23098
+ const auditPath = path46.join(os40.homedir(), ".node9", "audit.log");
23099
+ if (!fs44.existsSync(auditPath)) return "No audit log found.";
23100
+ const rawLines = fs44.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
23003
23101
  const parsed = [];
23004
23102
  for (const line of rawLines) {
23005
23103
  try {
@@ -23334,7 +23432,7 @@ function registerTrustCommand(program2) {
23334
23432
  // src/cli/commands/mcp-pin.ts
23335
23433
  init_mcp_pin();
23336
23434
  import chalk21 from "chalk";
23337
- import fs44 from "fs";
23435
+ import fs45 from "fs";
23338
23436
  function registerMcpPinCommand(program2) {
23339
23437
  const pinCmd = program2.command("mcp").description("Manage MCP server tool definition pinning (rug pull defense)");
23340
23438
  const pinSubCmd = pinCmd.command("pin").description("Manage pinned MCP server tool definitions");
@@ -23345,7 +23443,7 @@ function registerMcpPinCommand(program2) {
23345
23443
  let repoCorrupt = false;
23346
23444
  if (found.source === "repo") {
23347
23445
  try {
23348
- const raw = fs44.readFileSync(found.path, "utf-8");
23446
+ const raw = fs45.readFileSync(found.path, "utf-8");
23349
23447
  const parsed = JSON.parse(raw);
23350
23448
  repoEntries = parsed.servers ?? {};
23351
23449
  } catch {
@@ -23662,9 +23760,9 @@ init_litellm();
23662
23760
  init_cost_gemini();
23663
23761
  init_cost_codex();
23664
23762
  import chalk24 from "chalk";
23665
- import fs45 from "fs";
23666
- import path46 from "path";
23667
- import os40 from "os";
23763
+ import fs46 from "fs";
23764
+ import path47 from "path";
23765
+ import os41 from "os";
23668
23766
  function modelPrice(model) {
23669
23767
  const t = pricingFor(model);
23670
23768
  if (!t) return null;
@@ -23681,10 +23779,10 @@ function encodeProjectPath(projectPath) {
23681
23779
  }
23682
23780
  function sessionJsonlPath(projectPath, sessionId) {
23683
23781
  const encoded = encodeProjectPath(projectPath);
23684
- return path46.join(os40.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23782
+ return path47.join(os41.homedir(), ".claude", "projects", encoded, `${sessionId}.jsonl`);
23685
23783
  }
23686
23784
  function projectLabel(projectPath) {
23687
- return projectPath.replace(os40.homedir(), "~");
23785
+ return projectPath.replace(os41.homedir(), "~");
23688
23786
  }
23689
23787
  function parseHistoryLines(lines) {
23690
23788
  const entries = [];
@@ -23753,10 +23851,10 @@ function parseSessionLines(lines) {
23753
23851
  return { toolCalls, costUSD, hasSnapshot, modifiedFiles };
23754
23852
  }
23755
23853
  function loadAuditEntries(auditPath) {
23756
- const aPath = auditPath ?? path46.join(os40.homedir(), ".node9", "audit.log");
23854
+ const aPath = auditPath ?? path47.join(os41.homedir(), ".node9", "audit.log");
23757
23855
  let raw;
23758
23856
  try {
23759
- raw = fs45.readFileSync(aPath, "utf-8");
23857
+ raw = fs46.readFileSync(aPath, "utf-8");
23760
23858
  } catch {
23761
23859
  return [];
23762
23860
  }
@@ -23792,8 +23890,8 @@ function auditEntriesInWindow(entries, windowStart, windowEnd) {
23792
23890
  return result;
23793
23891
  }
23794
23892
  function buildGeminiSessions(days, allAuditEntries) {
23795
- const tmpDir = path46.join(os40.homedir(), ".gemini", "tmp");
23796
- if (!fs45.existsSync(tmpDir)) return [];
23893
+ const tmpDir = path47.join(os41.homedir(), ".gemini", "tmp");
23894
+ if (!fs46.existsSync(tmpDir)) return [];
23797
23895
  const cutoff = days !== null ? (() => {
23798
23896
  const d = /* @__PURE__ */ new Date();
23799
23897
  d.setDate(d.getDate() - days);
@@ -23802,35 +23900,35 @@ function buildGeminiSessions(days, allAuditEntries) {
23802
23900
  })() : null;
23803
23901
  let slugDirs;
23804
23902
  try {
23805
- slugDirs = fs45.readdirSync(tmpDir);
23903
+ slugDirs = fs46.readdirSync(tmpDir);
23806
23904
  } catch {
23807
23905
  return [];
23808
23906
  }
23809
23907
  const summaries = [];
23810
23908
  for (const slug of slugDirs) {
23811
- const slugPath = path46.join(tmpDir, slug);
23909
+ const slugPath = path47.join(tmpDir, slug);
23812
23910
  try {
23813
- if (!fs45.statSync(slugPath).isDirectory()) continue;
23911
+ if (!fs46.statSync(slugPath).isDirectory()) continue;
23814
23912
  } catch {
23815
23913
  continue;
23816
23914
  }
23817
- let projectRoot = path46.join(os40.homedir(), slug);
23915
+ let projectRoot = path47.join(os41.homedir(), slug);
23818
23916
  try {
23819
- projectRoot = fs45.readFileSync(path46.join(slugPath, ".project_root"), "utf-8").trim();
23917
+ projectRoot = fs46.readFileSync(path47.join(slugPath, ".project_root"), "utf-8").trim();
23820
23918
  } catch {
23821
23919
  }
23822
- const chatsDir = path46.join(slugPath, "chats");
23823
- if (!fs45.existsSync(chatsDir)) continue;
23920
+ const chatsDir = path47.join(slugPath, "chats");
23921
+ if (!fs46.existsSync(chatsDir)) continue;
23824
23922
  let chatFiles;
23825
23923
  try {
23826
- chatFiles = fs45.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23924
+ chatFiles = fs46.readdirSync(chatsDir).filter((f) => f.endsWith(".json"));
23827
23925
  } catch {
23828
23926
  continue;
23829
23927
  }
23830
23928
  for (const chatFile of chatFiles) {
23831
23929
  let raw;
23832
23930
  try {
23833
- raw = fs45.readFileSync(path46.join(chatsDir, chatFile), "utf-8");
23931
+ raw = fs46.readFileSync(path47.join(chatsDir, chatFile), "utf-8");
23834
23932
  } catch {
23835
23933
  continue;
23836
23934
  }
@@ -23910,8 +24008,8 @@ function buildGeminiSessions(days, allAuditEntries) {
23910
24008
  return summaries;
23911
24009
  }
23912
24010
  function buildCodexSessions(days, allAuditEntries) {
23913
- const sessionsBase = path46.join(os40.homedir(), ".codex", "sessions");
23914
- if (!fs45.existsSync(sessionsBase)) return [];
24011
+ const sessionsBase = path47.join(os41.homedir(), ".codex", "sessions");
24012
+ if (!fs46.existsSync(sessionsBase)) return [];
23915
24013
  const cutoff = days !== null ? (() => {
23916
24014
  const d = /* @__PURE__ */ new Date();
23917
24015
  d.setDate(d.getDate() - days);
@@ -23920,29 +24018,29 @@ function buildCodexSessions(days, allAuditEntries) {
23920
24018
  })() : null;
23921
24019
  const jsonlFiles = [];
23922
24020
  try {
23923
- for (const year of fs45.readdirSync(sessionsBase)) {
23924
- const yearPath = path46.join(sessionsBase, year);
24021
+ for (const year of fs46.readdirSync(sessionsBase)) {
24022
+ const yearPath = path47.join(sessionsBase, year);
23925
24023
  try {
23926
- if (!fs45.statSync(yearPath).isDirectory()) continue;
24024
+ if (!fs46.statSync(yearPath).isDirectory()) continue;
23927
24025
  } catch {
23928
24026
  continue;
23929
24027
  }
23930
- for (const month of fs45.readdirSync(yearPath)) {
23931
- const monthPath = path46.join(yearPath, month);
24028
+ for (const month of fs46.readdirSync(yearPath)) {
24029
+ const monthPath = path47.join(yearPath, month);
23932
24030
  try {
23933
- if (!fs45.statSync(monthPath).isDirectory()) continue;
24031
+ if (!fs46.statSync(monthPath).isDirectory()) continue;
23934
24032
  } catch {
23935
24033
  continue;
23936
24034
  }
23937
- for (const day of fs45.readdirSync(monthPath)) {
23938
- const dayPath = path46.join(monthPath, day);
24035
+ for (const day of fs46.readdirSync(monthPath)) {
24036
+ const dayPath = path47.join(monthPath, day);
23939
24037
  try {
23940
- if (!fs45.statSync(dayPath).isDirectory()) continue;
24038
+ if (!fs46.statSync(dayPath).isDirectory()) continue;
23941
24039
  } catch {
23942
24040
  continue;
23943
24041
  }
23944
- for (const file of fs45.readdirSync(dayPath)) {
23945
- if (file.endsWith(".jsonl")) jsonlFiles.push(path46.join(dayPath, file));
24042
+ for (const file of fs46.readdirSync(dayPath)) {
24043
+ if (file.endsWith(".jsonl")) jsonlFiles.push(path47.join(dayPath, file));
23946
24044
  }
23947
24045
  }
23948
24046
  }
@@ -23954,7 +24052,7 @@ function buildCodexSessions(days, allAuditEntries) {
23954
24052
  for (const filePath of jsonlFiles) {
23955
24053
  let lines;
23956
24054
  try {
23957
- lines = fs45.readFileSync(filePath, "utf-8").split("\n");
24055
+ lines = fs46.readFileSync(filePath, "utf-8").split("\n");
23958
24056
  } catch {
23959
24057
  continue;
23960
24058
  }
@@ -24040,10 +24138,10 @@ function buildCodexSessions(days, allAuditEntries) {
24040
24138
  return summaries;
24041
24139
  }
24042
24140
  function buildSessions(days, historyPath) {
24043
- const hPath = historyPath ?? path46.join(os40.homedir(), ".claude", "history.jsonl");
24141
+ const hPath = historyPath ?? path47.join(os41.homedir(), ".claude", "history.jsonl");
24044
24142
  let historyRaw = "";
24045
24143
  try {
24046
- historyRaw = fs45.readFileSync(hPath, "utf-8");
24144
+ historyRaw = fs46.readFileSync(hPath, "utf-8");
24047
24145
  } catch {
24048
24146
  }
24049
24147
  const cutoff = days !== null ? (() => {
@@ -24067,7 +24165,7 @@ function buildSessions(days, historyPath) {
24067
24165
  const jsonlFile = sessionJsonlPath(entry.project, entry.sessionId);
24068
24166
  let sessionLines = [];
24069
24167
  try {
24070
- sessionLines = fs45.readFileSync(jsonlFile, "utf-8").split("\n");
24168
+ sessionLines = fs46.readFileSync(jsonlFile, "utf-8").split("\n");
24071
24169
  } catch {
24072
24170
  }
24073
24171
  const { toolCalls, costUSD, hasSnapshot, modifiedFiles } = parseSessionLines(sessionLines);
@@ -24367,12 +24465,12 @@ function registerSessionsCommand(program2) {
24367
24465
 
24368
24466
  // src/cli/commands/skill-pin.ts
24369
24467
  import chalk25 from "chalk";
24370
- import fs46 from "fs";
24371
- import os41 from "os";
24372
- import path47 from "path";
24468
+ import fs47 from "fs";
24469
+ import os42 from "os";
24470
+ import path48 from "path";
24373
24471
  function wipeSkillSessions() {
24374
24472
  try {
24375
- fs46.rmSync(path47.join(os41.homedir(), ".node9", "skill-sessions"), {
24473
+ fs47.rmSync(path48.join(os42.homedir(), ".node9", "skill-sessions"), {
24376
24474
  recursive: true,
24377
24475
  force: true
24378
24476
  });
@@ -24454,15 +24552,15 @@ function registerSkillPinCommand(program2) {
24454
24552
  }
24455
24553
 
24456
24554
  // src/cli/commands/decisions.ts
24457
- import fs47 from "fs";
24458
- import os42 from "os";
24459
- import path48 from "path";
24555
+ import fs48 from "fs";
24556
+ import os43 from "os";
24557
+ import path49 from "path";
24460
24558
  import chalk26 from "chalk";
24461
- var DECISIONS_FILE2 = path48.join(os42.homedir(), ".node9", "decisions.json");
24559
+ var DECISIONS_FILE2 = path49.join(os43.homedir(), ".node9", "decisions.json");
24462
24560
  function readDecisions() {
24463
24561
  try {
24464
- if (!fs47.existsSync(DECISIONS_FILE2)) return {};
24465
- const raw = fs47.readFileSync(DECISIONS_FILE2, "utf-8");
24562
+ if (!fs48.existsSync(DECISIONS_FILE2)) return {};
24563
+ const raw = fs48.readFileSync(DECISIONS_FILE2, "utf-8");
24466
24564
  const parsed = JSON.parse(raw);
24467
24565
  const out = {};
24468
24566
  for (const [k, v] of Object.entries(parsed)) {
@@ -24474,11 +24572,11 @@ function readDecisions() {
24474
24572
  }
24475
24573
  }
24476
24574
  function writeDecisions(d) {
24477
- const dir = path48.dirname(DECISIONS_FILE2);
24478
- if (!fs47.existsSync(dir)) fs47.mkdirSync(dir, { recursive: true });
24575
+ const dir = path49.dirname(DECISIONS_FILE2);
24576
+ if (!fs48.existsSync(dir)) fs48.mkdirSync(dir, { recursive: true });
24479
24577
  const tmp = `${DECISIONS_FILE2}.${process.pid}.tmp`;
24480
- fs47.writeFileSync(tmp, JSON.stringify(d, null, 2));
24481
- fs47.renameSync(tmp, DECISIONS_FILE2);
24578
+ fs48.writeFileSync(tmp, JSON.stringify(d, null, 2));
24579
+ fs48.renameSync(tmp, DECISIONS_FILE2);
24482
24580
  }
24483
24581
  function registerDecisionsCommand(program2) {
24484
24582
  const cmd = program2.command("decisions").description('Manage persistent "Always Allow" / "Always Deny" tool decisions');
@@ -24535,18 +24633,18 @@ Persistent decisions (${entries.length})
24535
24633
 
24536
24634
  // src/cli/commands/dlp.ts
24537
24635
  import chalk27 from "chalk";
24538
- import fs48 from "fs";
24539
- import path49 from "path";
24540
- import os43 from "os";
24541
- var AUDIT_LOG = path49.join(os43.homedir(), ".node9", "audit.log");
24542
- var RESOLVED_FILE = path49.join(os43.homedir(), ".node9", "dlp-resolved.json");
24636
+ import fs49 from "fs";
24637
+ import path50 from "path";
24638
+ import os44 from "os";
24639
+ var AUDIT_LOG = path50.join(os44.homedir(), ".node9", "audit.log");
24640
+ var RESOLVED_FILE = path50.join(os44.homedir(), ".node9", "dlp-resolved.json");
24543
24641
  var ANSI_RE = /\x1b(?:\[[0-9;?]*[a-zA-Z]|\][^\x07\x1b]*(?:\x07|\x1b\\)|[@-_])/g;
24544
24642
  function stripAnsi(s) {
24545
24643
  return s.replace(ANSI_RE, "");
24546
24644
  }
24547
24645
  function loadResolved() {
24548
24646
  try {
24549
- const raw = JSON.parse(fs48.readFileSync(RESOLVED_FILE, "utf-8"));
24647
+ const raw = JSON.parse(fs49.readFileSync(RESOLVED_FILE, "utf-8"));
24550
24648
  return new Set(raw);
24551
24649
  } catch {
24552
24650
  return /* @__PURE__ */ new Set();
@@ -24554,13 +24652,13 @@ function loadResolved() {
24554
24652
  }
24555
24653
  function saveResolved(resolved) {
24556
24654
  try {
24557
- fs48.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24655
+ fs49.writeFileSync(RESOLVED_FILE, JSON.stringify([...resolved], null, 2), { mode: 384 });
24558
24656
  } catch {
24559
24657
  }
24560
24658
  }
24561
24659
  function loadDlpFindings() {
24562
- if (!fs48.existsSync(AUDIT_LOG)) return [];
24563
- return fs48.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24660
+ if (!fs49.existsSync(AUDIT_LOG)) return [];
24661
+ return fs49.readFileSync(AUDIT_LOG, "utf-8").split("\n").flatMap((line) => {
24564
24662
  if (!line.trim()) return [];
24565
24663
  try {
24566
24664
  const e = JSON.parse(line);
@@ -24659,14 +24757,14 @@ function registerDlpCommand(program2) {
24659
24757
  // src/cli/commands/mask.ts
24660
24758
  init_dlp();
24661
24759
  import chalk28 from "chalk";
24662
- import fs49 from "fs";
24663
- import path50 from "path";
24664
- import os44 from "os";
24760
+ import fs50 from "fs";
24761
+ import path51 from "path";
24762
+ import os45 from "os";
24665
24763
  function findJsonlFiles(dir) {
24666
24764
  const results = [];
24667
- if (!fs49.existsSync(dir)) return results;
24668
- for (const entry of fs49.readdirSync(dir, { withFileTypes: true })) {
24669
- const full = path50.join(dir, entry.name);
24765
+ if (!fs50.existsSync(dir)) return results;
24766
+ for (const entry of fs50.readdirSync(dir, { withFileTypes: true })) {
24767
+ const full = path51.join(dir, entry.name);
24670
24768
  if (entry.isDirectory()) results.push(...findJsonlFiles(full));
24671
24769
  else if (entry.isFile() && entry.name.endsWith(".jsonl")) results.push(full);
24672
24770
  }
@@ -24709,7 +24807,7 @@ function redactJson(obj) {
24709
24807
  function processFile(filePath, dryRun) {
24710
24808
  let raw;
24711
24809
  try {
24712
- raw = fs49.readFileSync(filePath, "utf-8");
24810
+ raw = fs50.readFileSync(filePath, "utf-8");
24713
24811
  } catch {
24714
24812
  return { redactedLines: 0, patterns: [] };
24715
24813
  }
@@ -24741,14 +24839,14 @@ function processFile(filePath, dryRun) {
24741
24839
  }
24742
24840
  }
24743
24841
  if (!dryRun && redactedLines > 0) {
24744
- fs49.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24842
+ fs50.writeFileSync(filePath, newLines.join("\n"), "utf-8");
24745
24843
  }
24746
24844
  return { redactedLines, patterns };
24747
24845
  }
24748
24846
  function processJsonFile(filePath, dryRun) {
24749
24847
  let raw;
24750
24848
  try {
24751
- raw = fs49.readFileSync(filePath, "utf-8");
24849
+ raw = fs50.readFileSync(filePath, "utf-8");
24752
24850
  } catch {
24753
24851
  return { redactedLines: 0, patterns: [] };
24754
24852
  }
@@ -24761,15 +24859,15 @@ function processJsonFile(filePath, dryRun) {
24761
24859
  const { value, modified, found } = redactJson(parsed);
24762
24860
  if (!modified) return { redactedLines: 0, patterns: [] };
24763
24861
  if (!dryRun) {
24764
- fs49.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24862
+ fs50.writeFileSync(filePath, JSON.stringify(value, null, 2), "utf-8");
24765
24863
  }
24766
24864
  return { redactedLines: 1, patterns: found };
24767
24865
  }
24768
24866
  function findJsonFiles(dir) {
24769
24867
  const results = [];
24770
- if (!fs49.existsSync(dir)) return results;
24771
- for (const entry of fs49.readdirSync(dir, { withFileTypes: true })) {
24772
- const full = path50.join(dir, entry.name);
24868
+ if (!fs50.existsSync(dir)) return results;
24869
+ for (const entry of fs50.readdirSync(dir, { withFileTypes: true })) {
24870
+ const full = path51.join(dir, entry.name);
24773
24871
  if (entry.isDirectory()) results.push(...findJsonFiles(full));
24774
24872
  else if (entry.isFile() && entry.name.endsWith(".json")) results.push(full);
24775
24873
  }
@@ -24778,9 +24876,9 @@ function findJsonFiles(dir) {
24778
24876
  function registerMaskCommand(program2) {
24779
24877
  program2.command("mask").description("Redact plaintext secrets from local AI session history files").option("--dry-run", "show what would be redacted without making changes").option("--all", "scan all history (default: last 30 days)").action(async (options) => {
24780
24878
  const dryRun = !!options.dryRun;
24781
- const home = os44.homedir();
24782
- const claudeDir = path50.join(home, ".claude", "projects");
24783
- const geminiDir = path50.join(home, ".gemini", "tmp");
24879
+ const home = os45.homedir();
24880
+ const claudeDir = path51.join(home, ".claude", "projects");
24881
+ const geminiDir = path51.join(home, ".gemini", "tmp");
24784
24882
  const allFiles = [
24785
24883
  ...findJsonlFiles(claudeDir).map((p) => ({ path: p, type: "jsonl" })),
24786
24884
  ...findJsonFiles(geminiDir).map((p) => ({ path: p, type: "json" }))
@@ -24788,7 +24886,7 @@ function registerMaskCommand(program2) {
24788
24886
  const cutoff = options.all ? null : new Date(Date.now() - 30 * 24 * 60 * 60 * 1e3);
24789
24887
  const filtered = cutoff ? allFiles.filter((f) => {
24790
24888
  try {
24791
- return fs49.statSync(f.path).mtime >= cutoff;
24889
+ return fs50.statSync(f.path).mtime >= cutoff;
24792
24890
  } catch {
24793
24891
  return false;
24794
24892
  }
@@ -24844,20 +24942,20 @@ function registerMaskCommand(program2) {
24844
24942
  // src/cli.ts
24845
24943
  init_blast();
24846
24944
  var { version } = JSON.parse(
24847
- fs52.readFileSync(path53.join(__dirname, "../package.json"), "utf-8")
24945
+ fs53.readFileSync(path54.join(__dirname, "../package.json"), "utf-8")
24848
24946
  );
24849
24947
  var program = new Command();
24850
24948
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
24851
24949
  program.command("login").argument("<apiKey>").option("--local", "Save key for audit/logging only \u2014 local config still controls all decisions").option("--profile <name>", 'Save as a named profile (default: "default")').action((apiKey, options) => {
24852
24950
  const DEFAULT_API_URL2 = "https://api.node9.ai/api/v1/intercept";
24853
- const credPath = path53.join(os47.homedir(), ".node9", "credentials.json");
24854
- if (!fs52.existsSync(path53.dirname(credPath)))
24855
- fs52.mkdirSync(path53.dirname(credPath), { recursive: true });
24951
+ const credPath = path54.join(os48.homedir(), ".node9", "credentials.json");
24952
+ if (!fs53.existsSync(path54.dirname(credPath)))
24953
+ fs53.mkdirSync(path54.dirname(credPath), { recursive: true });
24856
24954
  const profileName = options.profile || "default";
24857
24955
  let existingCreds = {};
24858
24956
  try {
24859
- if (fs52.existsSync(credPath)) {
24860
- const raw = JSON.parse(fs52.readFileSync(credPath, "utf-8"));
24957
+ if (fs53.existsSync(credPath)) {
24958
+ const raw = JSON.parse(fs53.readFileSync(credPath, "utf-8"));
24861
24959
  if (raw.apiKey) {
24862
24960
  existingCreds = {
24863
24961
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL2 }
@@ -24869,14 +24967,14 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24869
24967
  } catch {
24870
24968
  }
24871
24969
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL2 };
24872
- fs52.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24970
+ fs53.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
24873
24971
  let effectiveCloud = null;
24874
24972
  if (profileName === "default") {
24875
- const configPath = path53.join(os47.homedir(), ".node9", "config.json");
24973
+ const configPath = path54.join(os48.homedir(), ".node9", "config.json");
24876
24974
  let config = {};
24877
24975
  try {
24878
- if (fs52.existsSync(configPath))
24879
- config = JSON.parse(fs52.readFileSync(configPath, "utf-8"));
24976
+ if (fs53.existsSync(configPath))
24977
+ config = JSON.parse(fs53.readFileSync(configPath, "utf-8"));
24880
24978
  } catch {
24881
24979
  }
24882
24980
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -24891,9 +24989,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
24891
24989
  approvers.cloud = false;
24892
24990
  }
24893
24991
  s.approvers = approvers;
24894
- if (!fs52.existsSync(path53.dirname(configPath)))
24895
- fs52.mkdirSync(path53.dirname(configPath), { recursive: true });
24896
- fs52.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
24992
+ if (!fs53.existsSync(path54.dirname(configPath)))
24993
+ fs53.mkdirSync(path54.dirname(configPath), { recursive: true });
24994
+ fs53.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
24897
24995
  effectiveCloud = approvers.cloud === true;
24898
24996
  }
24899
24997
  if (options.profile && profileName !== "default") {
@@ -25052,15 +25150,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
25052
25150
  }
25053
25151
  }
25054
25152
  if (options.purge) {
25055
- const node9Dir = path53.join(os47.homedir(), ".node9");
25056
- if (fs52.existsSync(node9Dir)) {
25153
+ const node9Dir = path54.join(os48.homedir(), ".node9");
25154
+ if (fs53.existsSync(node9Dir)) {
25057
25155
  const confirmed = await confirm2({
25058
25156
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
25059
25157
  default: false
25060
25158
  });
25061
25159
  if (confirmed) {
25062
- fs52.rmSync(node9Dir, { recursive: true });
25063
- if (fs52.existsSync(node9Dir)) {
25160
+ fs53.rmSync(node9Dir, { recursive: true });
25161
+ if (fs53.existsSync(node9Dir)) {
25064
25162
  console.error(
25065
25163
  chalk30.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
25066
25164
  );
@@ -25175,7 +25273,7 @@ program.command("tail").description("Stream live agent activity to the terminal"
25175
25273
  });
25176
25274
  program.command("monitor").description("Live interactive dashboard \u2014 activity feed, approvals, security signals").action(async () => {
25177
25275
  try {
25178
- const dashboardPath = path53.join(__dirname, "dashboard.mjs");
25276
+ const dashboardPath = path54.join(__dirname, "dashboard.mjs");
25179
25277
  const dynamicImport = new Function("id", "return import(id)");
25180
25278
  const mod = await dynamicImport(`file://${dashboardPath}`);
25181
25279
  await mod.startMonitor();
@@ -25213,14 +25311,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
25213
25311
  Run "node9 addto claude" to register it as the statusLine.`
25214
25312
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
25215
25313
  if (subcommand === "debug") {
25216
- const flagFile = path53.join(os47.homedir(), ".node9", "hud-debug");
25314
+ const flagFile = path54.join(os48.homedir(), ".node9", "hud-debug");
25217
25315
  if (state === "on") {
25218
- fs52.mkdirSync(path53.dirname(flagFile), { recursive: true });
25219
- fs52.writeFileSync(flagFile, "");
25316
+ fs53.mkdirSync(path54.dirname(flagFile), { recursive: true });
25317
+ fs53.writeFileSync(flagFile, "");
25220
25318
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
25221
25319
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
25222
25320
  } else if (state === "off") {
25223
- if (fs52.existsSync(flagFile)) fs52.unlinkSync(flagFile);
25321
+ if (fs53.existsSync(flagFile)) fs53.unlinkSync(flagFile);
25224
25322
  console.log("HUD debug logging disabled.");
25225
25323
  } else {
25226
25324
  console.error("Usage: node9 hud debug on|off");
@@ -25337,9 +25435,9 @@ if (process.argv[2] !== "daemon") {
25337
25435
  const isCheckHook = process.argv[2] === "check";
25338
25436
  if (isCheckHook) {
25339
25437
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
25340
- const logPath = path53.join(os47.homedir(), ".node9", "hook-debug.log");
25438
+ const logPath = path54.join(os48.homedir(), ".node9", "hook-debug.log");
25341
25439
  const msg = reason instanceof Error ? reason.message : String(reason);
25342
- fs52.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25440
+ fs53.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
25343
25441
  `);
25344
25442
  }
25345
25443
  process.exit(0);