@node9/proxy 1.10.0 → 1.10.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.
package/dist/cli.js CHANGED
@@ -168,8 +168,8 @@ function sanitizeConfig(raw) {
168
168
  }
169
169
  }
170
170
  const lines = result.error.issues.map((issue) => {
171
- const path34 = issue.path.length > 0 ? issue.path.join(".") : "root";
172
- return ` \u2022 ${path34}: ${issue.message}`;
171
+ const path35 = issue.path.length > 0 ? issue.path.join(".") : "root";
172
+ return ` \u2022 ${path35}: ${issue.message}`;
173
173
  });
174
174
  return {
175
175
  sanitized,
@@ -253,7 +253,8 @@ var init_config_schema = __esm({
253
253
  slackEnabled: import_zod.z.boolean().optional(),
254
254
  enableTrustSessions: import_zod.z.boolean().optional(),
255
255
  allowGlobalPause: import_zod.z.boolean().optional(),
256
- auditHashArgs: import_zod.z.boolean().optional()
256
+ auditHashArgs: import_zod.z.boolean().optional(),
257
+ agentPolicy: import_zod.z.enum(["require_approval", "block_on_rules"]).optional()
257
258
  }).optional(),
258
259
  policy: import_zod.z.object({
259
260
  sandboxPaths: import_zod.z.array(import_zod.z.string()).optional(),
@@ -1726,9 +1727,9 @@ function matchesPattern(text, patterns) {
1726
1727
  const withoutDotSlash = text.replace(/^\.\//, "");
1727
1728
  return isMatch(withoutDotSlash) || isMatch(`./${withoutDotSlash}`);
1728
1729
  }
1729
- function getNestedValue(obj, path34) {
1730
+ function getNestedValue(obj, path35) {
1730
1731
  if (!obj || typeof obj !== "object") return null;
1731
- return path34.split(".").reduce((prev, curr) => prev?.[curr], obj);
1732
+ return path35.split(".").reduce((prev, curr) => prev?.[curr], obj);
1732
1733
  }
1733
1734
  function shouldSnapshot(toolName, args, config) {
1734
1735
  if (!config.settings.enableUndo) return false;
@@ -2811,11 +2812,12 @@ ${smartTruncate(str, 500)}`
2811
2812
  function escapePango(text) {
2812
2813
  return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2813
2814
  }
2814
- function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1) {
2815
+ function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
2815
2816
  const lines = [];
2816
2817
  if (locked) lines.push("\u26A0\uFE0F LOCKED BY ADMIN POLICY\n");
2817
2818
  lines.push(`\u{1F916} ${agent || "AI Agent"} | \u{1F527} ${toolName}`);
2818
2819
  lines.push(`\u{1F6E1}\uFE0F ${explainableLabel || "Security Policy"}`);
2820
+ if (ruleDescription) lines.push(`\u2139 ${ruleDescription}`);
2819
2821
  lines.push("");
2820
2822
  lines.push(formattedArgs);
2821
2823
  if (allowCount >= 3) {
@@ -2828,7 +2830,7 @@ function buildPlainMessage(toolName, formattedArgs, agent, explainableLabel, loc
2828
2830
  }
2829
2831
  return lines.join("\n");
2830
2832
  }
2831
- function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1) {
2833
+ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, locked, allowCount = 1, ruleDescription) {
2832
2834
  const lines = [];
2833
2835
  if (locked) {
2834
2836
  lines.push('<span foreground="red" weight="bold">\u26A0\uFE0F LOCKED BY ADMIN POLICY</span>');
@@ -2838,6 +2840,7 @@ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, loc
2838
2840
  `<b>\u{1F916} ${escapePango(agent || "AI Agent")}</b> | <b>\u{1F527} <tt>${escapePango(toolName)}</tt></b>`
2839
2841
  );
2840
2842
  lines.push(`<i>\u{1F6E1}\uFE0F ${escapePango(explainableLabel || "Security Policy")}</i>`);
2843
+ if (ruleDescription) lines.push(`<i>\u2139 ${escapePango(ruleDescription)}</i>`);
2841
2844
  lines.push("");
2842
2845
  lines.push(`<tt>${escapePango(formattedArgs)}</tt>`);
2843
2846
  if (allowCount >= 3) {
@@ -2854,7 +2857,7 @@ function buildPangoMessage(toolName, formattedArgs, agent, explainableLabel, loc
2854
2857
  }
2855
2858
  return lines.join("\n");
2856
2859
  }
2857
- async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal, matchedField, matchedWord, allowCount = 1) {
2860
+ async function askNativePopup(toolName, args, agent, explainableLabel, locked = false, signal, matchedField, matchedWord, allowCount = 1, ruleDescription) {
2858
2861
  if (isTestEnv()) return "deny";
2859
2862
  const { message: formattedArgs, intent } = formatArgs(args, matchedField, matchedWord);
2860
2863
  const intentLabel = intent === "EDIT" ? "Code Edit" : "Action Approval";
@@ -2865,7 +2868,8 @@ async function askNativePopup(toolName, args, agent, explainableLabel, locked =
2865
2868
  agent,
2866
2869
  explainableLabel,
2867
2870
  locked,
2868
- allowCount
2871
+ allowCount,
2872
+ ruleDescription
2869
2873
  );
2870
2874
  return new Promise((resolve) => {
2871
2875
  let childProcess = null;
@@ -2899,7 +2903,8 @@ end run`;
2899
2903
  agent,
2900
2904
  explainableLabel,
2901
2905
  locked,
2902
- allowCount
2906
+ allowCount,
2907
+ ruleDescription
2903
2908
  );
2904
2909
  const argsList = [
2905
2910
  locked ? "--info" : "--question",
@@ -2972,7 +2977,7 @@ function auditLocalAllow(toolName, args, checkedBy, creds, meta) {
2972
2977
  }).catch(() => {
2973
2978
  });
2974
2979
  }
2975
- async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
2980
+ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata, agentPolicy, forceReview) {
2976
2981
  const controller = new AbortController();
2977
2982
  const timeout = setTimeout(() => controller.abort(), 1e4);
2978
2983
  if (!creds.apiKey) throw new Error("Node9 API Key is missing");
@@ -3017,7 +3022,9 @@ async function initNode9SaaS(toolName, args, creds, meta, riskMetadata) {
3017
3022
  platform: import_os9.default.platform()
3018
3023
  },
3019
3024
  ...riskMetadata && { riskMetadata },
3020
- ...ciContext && { ciContext }
3025
+ ...ciContext && { ciContext },
3026
+ ...agentPolicy && { policy: agentPolicy },
3027
+ ...forceReview && { forceReview: true }
3021
3028
  }),
3022
3029
  signal: controller.signal
3023
3030
  });
@@ -3414,6 +3421,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
3414
3421
  policyMatchedWord,
3415
3422
  policyResult.ruleName
3416
3423
  );
3424
+ if (policyRuleDescription) riskMetadata.ruleDescription = policyRuleDescription.slice(0, 200);
3417
3425
  const persistent = policyResult.ruleName ? null : getPersistentDecision(toolName);
3418
3426
  if (persistent === "allow") {
3419
3427
  if (approvers.cloud && creds?.apiKey)
@@ -3448,9 +3456,18 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
3448
3456
  }
3449
3457
  let cloudRequestId = null;
3450
3458
  const cloudEnforced = approvers.cloud && !!creds?.apiKey;
3451
- if (cloudEnforced && !localSmartRuleMatched && !options?.localSmartRuleMatched) {
3459
+ const forceReview = localSmartRuleMatched === true || options?.localSmartRuleMatched === true || void 0;
3460
+ if (cloudEnforced) {
3452
3461
  try {
3453
- const initResult = await initNode9SaaS(toolName, args, creds, meta, riskMetadata);
3462
+ const initResult = await initNode9SaaS(
3463
+ toolName,
3464
+ args,
3465
+ creds,
3466
+ meta,
3467
+ riskMetadata,
3468
+ config.settings.agentPolicy,
3469
+ forceReview
3470
+ );
3454
3471
  if (!initResult.pending) {
3455
3472
  if (initResult.shadowMode) {
3456
3473
  return { approved: true, checkedBy: "cloud" };
@@ -3465,9 +3482,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
3465
3482
  };
3466
3483
  }
3467
3484
  }
3468
- if (!localSmartRuleMatched && !options?.localSmartRuleMatched) {
3469
- cloudRequestId = initResult.requestId || null;
3470
- }
3485
+ if (initResult.pending) cloudRequestId = initResult.requestId || null;
3471
3486
  if (!taintWarning) explainableLabel = "Organization Policy (SaaS)";
3472
3487
  } catch {
3473
3488
  }
@@ -3524,7 +3539,7 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
3524
3539
  }
3525
3540
  }
3526
3541
  }
3527
- if (cloudEnforced && cloudRequestId && !localSmartRuleMatched && !options?.localSmartRuleMatched) {
3542
+ if (cloudEnforced && cloudRequestId) {
3528
3543
  racePromises.push(
3529
3544
  (async () => {
3530
3545
  try {
@@ -3556,7 +3571,8 @@ async function _authorizeHeadlessCore(toolName, args, meta, options) {
3556
3571
  signal,
3557
3572
  policyMatchedField,
3558
3573
  policyMatchedWord,
3559
- daemonAllowCount
3574
+ daemonAllowCount,
3575
+ riskMetadata?.ruleDescription
3560
3576
  );
3561
3577
  if (decision === "always_allow") {
3562
3578
  writeTrustSession(toolName, 36e5);
@@ -6059,8 +6075,175 @@ var init_patch = __esm({
6059
6075
  }
6060
6076
  });
6061
6077
 
6078
+ // src/costSync.ts
6079
+ function normalizeModel(raw) {
6080
+ return raw.replace(/-\d{8}$/, "");
6081
+ }
6082
+ function pricingFor(model) {
6083
+ const norm = normalizeModel(model);
6084
+ if (PRICING[norm]) return PRICING[norm];
6085
+ let best = null;
6086
+ for (const key of Object.keys(PRICING)) {
6087
+ if (norm.startsWith(key) && (best === null || key.length > best.length)) best = key;
6088
+ }
6089
+ return best ? PRICING[best] : null;
6090
+ }
6091
+ function parseJSONLFile(filePath) {
6092
+ let content;
6093
+ try {
6094
+ content = import_fs16.default.readFileSync(filePath, "utf8");
6095
+ } catch {
6096
+ return /* @__PURE__ */ new Map();
6097
+ }
6098
+ const daily = /* @__PURE__ */ new Map();
6099
+ for (const line of content.split("\n")) {
6100
+ if (!line.trim()) continue;
6101
+ let row;
6102
+ try {
6103
+ row = JSON.parse(line);
6104
+ } catch {
6105
+ continue;
6106
+ }
6107
+ if (row["type"] !== "assistant") continue;
6108
+ const msg = row["message"];
6109
+ if (!msg?.["usage"] || typeof msg["model"] !== "string") continue;
6110
+ const usage = msg["usage"];
6111
+ const model = msg["model"];
6112
+ const timestamp = row["timestamp"];
6113
+ if (typeof timestamp !== "string" || timestamp.length < 10) continue;
6114
+ const date = timestamp.slice(0, 10);
6115
+ const p = pricingFor(model);
6116
+ if (!p) continue;
6117
+ const inp = Number(usage["input_tokens"] ?? 0);
6118
+ const out = Number(usage["output_tokens"] ?? 0);
6119
+ const cw = Number(usage["cache_creation_input_tokens"] ?? 0);
6120
+ const cr = Number(usage["cache_read_input_tokens"] ?? 0);
6121
+ const cost = inp * p[0] + out * p[1] + cw * p[2] + cr * p[3];
6122
+ const norm = normalizeModel(model);
6123
+ const key = `${date}::${norm}`;
6124
+ const prev = daily.get(key);
6125
+ if (prev) {
6126
+ prev.costUSD += cost;
6127
+ prev.inputTokens += inp;
6128
+ prev.outputTokens += out;
6129
+ prev.cacheWriteTokens += cw;
6130
+ prev.cacheReadTokens += cr;
6131
+ } else {
6132
+ daily.set(key, {
6133
+ date,
6134
+ model: norm,
6135
+ costUSD: cost,
6136
+ inputTokens: inp,
6137
+ outputTokens: out,
6138
+ cacheWriteTokens: cw,
6139
+ cacheReadTokens: cr
6140
+ });
6141
+ }
6142
+ }
6143
+ return daily;
6144
+ }
6145
+ function collectEntries() {
6146
+ const projectsDir = import_path19.default.join(import_os14.default.homedir(), ".claude", "projects");
6147
+ if (!import_fs16.default.existsSync(projectsDir)) return [];
6148
+ const combined = /* @__PURE__ */ new Map();
6149
+ let dirs;
6150
+ try {
6151
+ dirs = import_fs16.default.readdirSync(projectsDir);
6152
+ } catch {
6153
+ return [];
6154
+ }
6155
+ for (const dir of dirs) {
6156
+ const dirPath = import_path19.default.join(projectsDir, dir);
6157
+ try {
6158
+ if (!import_fs16.default.statSync(dirPath).isDirectory()) continue;
6159
+ } catch {
6160
+ continue;
6161
+ }
6162
+ let files;
6163
+ try {
6164
+ files = import_fs16.default.readdirSync(dirPath).filter((f) => f.endsWith(".jsonl"));
6165
+ } catch {
6166
+ continue;
6167
+ }
6168
+ for (const file of files) {
6169
+ const entries = parseJSONLFile(import_path19.default.join(dirPath, file));
6170
+ for (const [key, e] of entries) {
6171
+ const prev = combined.get(key);
6172
+ if (prev) {
6173
+ prev.costUSD += e.costUSD;
6174
+ prev.inputTokens += e.inputTokens;
6175
+ prev.outputTokens += e.outputTokens;
6176
+ prev.cacheWriteTokens += e.cacheWriteTokens;
6177
+ prev.cacheReadTokens += e.cacheReadTokens;
6178
+ } else {
6179
+ combined.set(key, { ...e });
6180
+ }
6181
+ }
6182
+ }
6183
+ }
6184
+ return [...combined.values()];
6185
+ }
6186
+ async function syncCost() {
6187
+ const creds = getCredentials();
6188
+ if (!creds?.apiKey || !creds?.apiUrl) return;
6189
+ const entries = collectEntries();
6190
+ if (entries.length === 0) return;
6191
+ let username = "unknown";
6192
+ try {
6193
+ username = import_os14.default.userInfo().username;
6194
+ } catch {
6195
+ }
6196
+ const machineId = `${import_os14.default.hostname()}:${username}`;
6197
+ try {
6198
+ const res = await fetch(`${creds.apiUrl}/cost-sync`, {
6199
+ method: "POST",
6200
+ headers: { "Content-Type": "application/json", Authorization: `Bearer ${creds.apiKey}` },
6201
+ body: JSON.stringify({ machineId, entries }),
6202
+ signal: AbortSignal.timeout(15e3)
6203
+ });
6204
+ if (!res.ok) {
6205
+ import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] HTTP ${res.status}
6206
+ `);
6207
+ }
6208
+ } catch (err2) {
6209
+ import_fs16.default.appendFileSync(HOOK_DEBUG_LOG, `[cost-sync] ${err2.message}
6210
+ `);
6211
+ }
6212
+ }
6213
+ function startCostSync() {
6214
+ syncCost().catch(() => {
6215
+ });
6216
+ const timer = setInterval(() => {
6217
+ syncCost().catch(() => {
6218
+ });
6219
+ }, SYNC_INTERVAL_MS);
6220
+ timer.unref();
6221
+ }
6222
+ var import_fs16, import_path19, import_os14, SYNC_INTERVAL_MS, PRICING;
6223
+ var init_costSync = __esm({
6224
+ "src/costSync.ts"() {
6225
+ "use strict";
6226
+ import_fs16 = __toESM(require("fs"));
6227
+ import_path19 = __toESM(require("path"));
6228
+ import_os14 = __toESM(require("os"));
6229
+ init_config();
6230
+ init_audit();
6231
+ SYNC_INTERVAL_MS = 10 * 60 * 1e3;
6232
+ PRICING = {
6233
+ "claude-opus-4": [5e-6, 25e-6, 625e-8, 5e-7],
6234
+ "claude-sonnet-4": [3e-6, 15e-6, 375e-8, 3e-7],
6235
+ "claude-haiku-4": [8e-7, 4e-6, 1e-6, 8e-8],
6236
+ "claude-3-7-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
6237
+ "claude-3-5-sonnet": [3e-6, 15e-6, 375e-8, 3e-7],
6238
+ "claude-3-5-haiku": [8e-7, 4e-6, 1e-6, 8e-8],
6239
+ "claude-3-haiku": [25e-8, 125e-8, 3e-7, 3e-8]
6240
+ };
6241
+ }
6242
+ });
6243
+
6062
6244
  // src/daemon/server.ts
6063
6245
  function startDaemon() {
6246
+ startCostSync();
6064
6247
  loadInsightCounts();
6065
6248
  const csrfToken = (0, import_crypto7.randomUUID)();
6066
6249
  const internalToken = (0, import_crypto7.randomUUID)();
@@ -6076,7 +6259,7 @@ function startDaemon() {
6076
6259
  idleTimer = setTimeout(() => {
6077
6260
  if (autoStarted) {
6078
6261
  try {
6079
- import_fs16.default.unlinkSync(DAEMON_PID_FILE);
6262
+ import_fs17.default.unlinkSync(DAEMON_PID_FILE);
6080
6263
  } catch {
6081
6264
  }
6082
6265
  }
@@ -6239,7 +6422,7 @@ data: ${JSON.stringify(item.data)}
6239
6422
  status: "pending"
6240
6423
  });
6241
6424
  }
6242
- const projectCwd = typeof cwd === "string" && import_path19.default.isAbsolute(cwd) ? cwd : void 0;
6425
+ const projectCwd = typeof cwd === "string" && import_path20.default.isAbsolute(cwd) ? cwd : void 0;
6243
6426
  const projectConfig = getConfig(projectCwd);
6244
6427
  const browserEnabled = projectConfig.settings.approvers?.browser !== false;
6245
6428
  const terminalEnabled = projectConfig.settings.approvers?.terminal !== false;
@@ -6629,8 +6812,8 @@ data: ${JSON.stringify(item.data)}
6629
6812
  const body = await readBody(req);
6630
6813
  const data = body ? JSON.parse(body) : {};
6631
6814
  const configPath = data.configPath ?? GLOBAL_CONFIG_PATH;
6632
- const node9Dir = import_path19.default.dirname(GLOBAL_CONFIG_PATH);
6633
- if (!import_path19.default.resolve(configPath).startsWith(node9Dir + import_path19.default.sep)) {
6815
+ const node9Dir = import_path20.default.dirname(GLOBAL_CONFIG_PATH);
6816
+ if (!import_path20.default.resolve(configPath).startsWith(node9Dir + import_path20.default.sep)) {
6634
6817
  res.writeHead(400, { "Content-Type": "application/json" });
6635
6818
  return res.end(
6636
6819
  JSON.stringify({ error: "configPath must be within the node9 config directory" })
@@ -6741,14 +6924,14 @@ data: ${JSON.stringify(item.data)}
6741
6924
  server.on("error", (e) => {
6742
6925
  if (e.code === "EADDRINUSE") {
6743
6926
  try {
6744
- if (import_fs16.default.existsSync(DAEMON_PID_FILE)) {
6745
- const { pid } = JSON.parse(import_fs16.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
6927
+ if (import_fs17.default.existsSync(DAEMON_PID_FILE)) {
6928
+ const { pid } = JSON.parse(import_fs17.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
6746
6929
  process.kill(pid, 0);
6747
6930
  return process.exit(0);
6748
6931
  }
6749
6932
  } catch {
6750
6933
  try {
6751
- import_fs16.default.unlinkSync(DAEMON_PID_FILE);
6934
+ import_fs17.default.unlinkSync(DAEMON_PID_FILE);
6752
6935
  } catch {
6753
6936
  }
6754
6937
  server.listen(DAEMON_PORT, DAEMON_HOST);
@@ -6807,13 +6990,13 @@ data: ${JSON.stringify(item.data)}
6807
6990
  }
6808
6991
  startActivitySocket();
6809
6992
  }
6810
- var import_http, import_fs16, import_path19, import_crypto7, import_child_process4, import_chalk2;
6993
+ var import_http, import_fs17, import_path20, import_crypto7, import_child_process4, import_chalk2;
6811
6994
  var init_server = __esm({
6812
6995
  "src/daemon/server.ts"() {
6813
6996
  "use strict";
6814
6997
  import_http = __toESM(require("http"));
6815
- import_fs16 = __toESM(require("fs"));
6816
- import_path19 = __toESM(require("path"));
6998
+ import_fs17 = __toESM(require("fs"));
6999
+ import_path20 = __toESM(require("path"));
6817
7000
  import_crypto7 = require("crypto");
6818
7001
  import_child_process4 = require("child_process");
6819
7002
  import_chalk2 = __toESM(require("chalk"));
@@ -6823,29 +7006,30 @@ var init_server = __esm({
6823
7006
  init_state2();
6824
7007
  init_patch();
6825
7008
  init_config_schema();
7009
+ init_costSync();
6826
7010
  }
6827
7011
  });
6828
7012
 
6829
7013
  // src/daemon/index.ts
6830
7014
  function stopDaemon() {
6831
- if (!import_fs17.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
7015
+ if (!import_fs18.default.existsSync(DAEMON_PID_FILE)) return console.log(import_chalk3.default.yellow("Not running."));
6832
7016
  try {
6833
- const { pid } = JSON.parse(import_fs17.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
7017
+ const { pid } = JSON.parse(import_fs18.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
6834
7018
  process.kill(pid, "SIGTERM");
6835
7019
  console.log(import_chalk3.default.green("\u2705 Stopped."));
6836
7020
  } catch {
6837
7021
  console.log(import_chalk3.default.gray("Cleaned up stale PID file."));
6838
7022
  } finally {
6839
7023
  try {
6840
- import_fs17.default.unlinkSync(DAEMON_PID_FILE);
7024
+ import_fs18.default.unlinkSync(DAEMON_PID_FILE);
6841
7025
  } catch {
6842
7026
  }
6843
7027
  }
6844
7028
  }
6845
7029
  function daemonStatus() {
6846
- if (import_fs17.default.existsSync(DAEMON_PID_FILE)) {
7030
+ if (import_fs18.default.existsSync(DAEMON_PID_FILE)) {
6847
7031
  try {
6848
- const { pid } = JSON.parse(import_fs17.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
7032
+ const { pid } = JSON.parse(import_fs18.default.readFileSync(DAEMON_PID_FILE, "utf-8"));
6849
7033
  process.kill(pid, 0);
6850
7034
  console.log(import_chalk3.default.green("Node9 daemon: running"));
6851
7035
  return;
@@ -6864,11 +7048,11 @@ function daemonStatus() {
6864
7048
  console.log(import_chalk3.default.yellow("Node9 daemon: not running"));
6865
7049
  }
6866
7050
  }
6867
- var import_fs17, import_chalk3, import_child_process5;
7051
+ var import_fs18, import_chalk3, import_child_process5;
6868
7052
  var init_daemon2 = __esm({
6869
7053
  "src/daemon/index.ts"() {
6870
7054
  "use strict";
6871
- import_fs17 = __toESM(require("fs"));
7055
+ import_fs18 = __toESM(require("fs"));
6872
7056
  import_chalk3 = __toESM(require("chalk"));
6873
7057
  import_child_process5 = require("child_process");
6874
7058
  init_server();
@@ -6902,7 +7086,7 @@ function formatBase(activity) {
6902
7086
  const time = new Date(activity.ts).toLocaleTimeString([], { hour12: false });
6903
7087
  const icon = getIcon(activity.tool);
6904
7088
  const toolName = activity.tool.slice(0, 16).padEnd(16);
6905
- const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os24.default.homedir(), "~");
7089
+ const argsStr = JSON.stringify(activity.args ?? {}).replace(/\s+/g, " ").replaceAll(import_os25.default.homedir(), "~");
6906
7090
  const argsPreview = argsStr.length > 70 ? argsStr.slice(0, 70) + "\u2026" : argsStr;
6907
7091
  return `${import_chalk19.default.gray(time)} ${icon} ${import_chalk19.default.white.bold(toolName)} ${import_chalk19.default.dim(argsPreview)}`;
6908
7092
  }
@@ -6941,9 +7125,9 @@ function renderPending(activity) {
6941
7125
  }
6942
7126
  async function ensureDaemon() {
6943
7127
  let pidPort = null;
6944
- if (import_fs28.default.existsSync(PID_FILE)) {
7128
+ if (import_fs29.default.existsSync(PID_FILE)) {
6945
7129
  try {
6946
- const { port } = JSON.parse(import_fs28.default.readFileSync(PID_FILE, "utf-8"));
7130
+ const { port } = JSON.parse(import_fs29.default.readFileSync(PID_FILE, "utf-8"));
6947
7131
  pidPort = port;
6948
7132
  } catch {
6949
7133
  console.error(import_chalk19.default.dim("\u26A0\uFE0F Could not read PID file; falling back to default port."));
@@ -7020,6 +7204,9 @@ function buildCardLines(req, localCount = 0) {
7020
7204
  `${CYAN}\u2551${RESET2} Tool: ${BOLD2}${req.toolName}${RESET2}`,
7021
7205
  `${CYAN}\u2551${RESET2} Reason: ${tierLabel} \u2014 ${blockedBy}${RESET2}`
7022
7206
  ];
7207
+ if (req.riskMetadata?.ruleDescription) {
7208
+ lines.push(`${CYAN}\u2551${RESET2} ${YELLOW}\u2139 ${req.riskMetadata.ruleDescription}${RESET2}`);
7209
+ }
7023
7210
  if (req.riskMetadata?.ruleName && blockedBy.includes("Taint")) {
7024
7211
  lines.push(`${CYAN}\u2551${RESET2} ${YELLOW}\u26A0 ${req.riskMetadata.ruleName}${RESET2}`);
7025
7212
  }
@@ -7063,9 +7250,9 @@ function buildRecoveryCardLines(req) {
7063
7250
  ];
7064
7251
  }
7065
7252
  function readApproversFromDisk() {
7066
- const configPath = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
7253
+ const configPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json");
7067
7254
  try {
7068
- const raw = JSON.parse(import_fs28.default.readFileSync(configPath, "utf-8"));
7255
+ const raw = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
7069
7256
  const settings = raw.settings ?? {};
7070
7257
  return settings.approvers ?? {};
7071
7258
  } catch {
@@ -7081,15 +7268,15 @@ function approverStatusLine() {
7081
7268
  return `${fmt("native", "native")} ${fmt("browser", "browser")} ${fmt("cloud", "cloud")} ${fmt("terminal", "terminal")}`;
7082
7269
  }
7083
7270
  function toggleApprover(channel) {
7084
- const configPath = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
7271
+ const configPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json");
7085
7272
  try {
7086
- const raw = JSON.parse(import_fs28.default.readFileSync(configPath, "utf-8"));
7273
+ const raw = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
7087
7274
  const settings = raw.settings ?? {};
7088
7275
  const approvers = settings.approvers ?? {};
7089
7276
  approvers[channel] = approvers[channel] === false;
7090
7277
  settings.approvers = approvers;
7091
7278
  raw.settings = settings;
7092
- import_fs28.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
7279
+ import_fs29.default.writeFileSync(configPath, JSON.stringify(raw, null, 2) + "\n");
7093
7280
  } catch (err2) {
7094
7281
  process.stderr.write(`[node9] toggleApprover failed: ${String(err2)}
7095
7282
  `);
@@ -7259,8 +7446,8 @@ async function startTail(options = {}) {
7259
7446
  }
7260
7447
  postDecisionHttp(req2.id, httpDecision, csrfToken, port, httpOpts).catch((err2) => {
7261
7448
  try {
7262
- import_fs28.default.appendFileSync(
7263
- import_path31.default.join(import_os24.default.homedir(), ".node9", "hook-debug.log"),
7449
+ import_fs29.default.appendFileSync(
7450
+ import_path32.default.join(import_os25.default.homedir(), ".node9", "hook-debug.log"),
7264
7451
  `[tail] POST /decision failed: ${String(err2)}
7265
7452
  `
7266
7453
  );
@@ -7513,21 +7700,21 @@ async function startTail(options = {}) {
7513
7700
  process.exit(1);
7514
7701
  });
7515
7702
  }
7516
- var import_http2, import_chalk19, import_fs28, import_os24, import_path31, import_readline5, import_child_process14, PID_FILE, ICONS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
7703
+ var import_http2, import_chalk19, import_fs29, import_os25, import_path32, import_readline5, import_child_process14, PID_FILE, ICONS, RESET2, BOLD2, RED, YELLOW, CYAN, GRAY, GREEN, HIDE_CURSOR, SHOW_CURSOR, ERASE_DOWN, pendingShownForId, pendingWrappedLines, DIVIDER;
7517
7704
  var init_tail = __esm({
7518
7705
  "src/tui/tail.ts"() {
7519
7706
  "use strict";
7520
7707
  import_http2 = __toESM(require("http"));
7521
7708
  import_chalk19 = __toESM(require("chalk"));
7522
- import_fs28 = __toESM(require("fs"));
7523
- import_os24 = __toESM(require("os"));
7524
- import_path31 = __toESM(require("path"));
7709
+ import_fs29 = __toESM(require("fs"));
7710
+ import_os25 = __toESM(require("os"));
7711
+ import_path32 = __toESM(require("path"));
7525
7712
  import_readline5 = __toESM(require("readline"));
7526
7713
  import_child_process14 = require("child_process");
7527
7714
  init_daemon2();
7528
7715
  init_daemon();
7529
7716
  init_core();
7530
- PID_FILE = import_path31.default.join(import_os24.default.homedir(), ".node9", "daemon.pid");
7717
+ PID_FILE = import_path32.default.join(import_os25.default.homedir(), ".node9", "daemon.pid");
7531
7718
  ICONS = {
7532
7719
  bash: "\u{1F4BB}",
7533
7720
  shell: "\u{1F4BB}",
@@ -7642,9 +7829,9 @@ function formatTimeLeft(resetsAt) {
7642
7829
  return ` (${m}m left)`;
7643
7830
  }
7644
7831
  function safeReadJson(filePath) {
7645
- if (!import_fs29.default.existsSync(filePath)) return null;
7832
+ if (!import_fs30.default.existsSync(filePath)) return null;
7646
7833
  try {
7647
- return JSON.parse(import_fs29.default.readFileSync(filePath, "utf-8"));
7834
+ return JSON.parse(import_fs30.default.readFileSync(filePath, "utf-8"));
7648
7835
  } catch {
7649
7836
  return null;
7650
7837
  }
@@ -7665,12 +7852,12 @@ function countHooksInFile(filePath) {
7665
7852
  return Object.keys(cfg.hooks).length;
7666
7853
  }
7667
7854
  function countRulesInDir(rulesDir) {
7668
- if (!import_fs29.default.existsSync(rulesDir)) return 0;
7855
+ if (!import_fs30.default.existsSync(rulesDir)) return 0;
7669
7856
  let count = 0;
7670
7857
  try {
7671
- for (const entry of import_fs29.default.readdirSync(rulesDir, { withFileTypes: true })) {
7858
+ for (const entry of import_fs30.default.readdirSync(rulesDir, { withFileTypes: true })) {
7672
7859
  if (entry.isDirectory()) {
7673
- count += countRulesInDir(import_path32.default.join(rulesDir, entry.name));
7860
+ count += countRulesInDir(import_path33.default.join(rulesDir, entry.name));
7674
7861
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
7675
7862
  count++;
7676
7863
  }
@@ -7681,46 +7868,46 @@ function countRulesInDir(rulesDir) {
7681
7868
  }
7682
7869
  function isSamePath(a, b) {
7683
7870
  try {
7684
- return import_path32.default.resolve(a) === import_path32.default.resolve(b);
7871
+ return import_path33.default.resolve(a) === import_path33.default.resolve(b);
7685
7872
  } catch {
7686
7873
  return false;
7687
7874
  }
7688
7875
  }
7689
7876
  function countConfigs(cwd) {
7690
- const homeDir2 = import_os25.default.homedir();
7691
- const claudeDir = import_path32.default.join(homeDir2, ".claude");
7877
+ const homeDir2 = import_os26.default.homedir();
7878
+ const claudeDir = import_path33.default.join(homeDir2, ".claude");
7692
7879
  let claudeMdCount = 0;
7693
7880
  let rulesCount = 0;
7694
7881
  let hooksCount = 0;
7695
7882
  const userMcpServers = /* @__PURE__ */ new Set();
7696
7883
  const projectMcpServers = /* @__PURE__ */ new Set();
7697
- if (import_fs29.default.existsSync(import_path32.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
7698
- rulesCount += countRulesInDir(import_path32.default.join(claudeDir, "rules"));
7699
- const userSettings = import_path32.default.join(claudeDir, "settings.json");
7884
+ if (import_fs30.default.existsSync(import_path33.default.join(claudeDir, "CLAUDE.md"))) claudeMdCount++;
7885
+ rulesCount += countRulesInDir(import_path33.default.join(claudeDir, "rules"));
7886
+ const userSettings = import_path33.default.join(claudeDir, "settings.json");
7700
7887
  for (const name of getMcpServerNames(userSettings)) userMcpServers.add(name);
7701
7888
  hooksCount += countHooksInFile(userSettings);
7702
- const userClaudeJson = import_path32.default.join(homeDir2, ".claude.json");
7889
+ const userClaudeJson = import_path33.default.join(homeDir2, ".claude.json");
7703
7890
  for (const name of getMcpServerNames(userClaudeJson)) userMcpServers.add(name);
7704
7891
  for (const name of getDisabledMcpServers(userClaudeJson, "disabledMcpServers")) {
7705
7892
  userMcpServers.delete(name);
7706
7893
  }
7707
7894
  if (cwd) {
7708
- if (import_fs29.default.existsSync(import_path32.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
7709
- if (import_fs29.default.existsSync(import_path32.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
7710
- const projectClaudeDir = import_path32.default.join(cwd, ".claude");
7895
+ if (import_fs30.default.existsSync(import_path33.default.join(cwd, "CLAUDE.md"))) claudeMdCount++;
7896
+ if (import_fs30.default.existsSync(import_path33.default.join(cwd, "CLAUDE.local.md"))) claudeMdCount++;
7897
+ const projectClaudeDir = import_path33.default.join(cwd, ".claude");
7711
7898
  const overlapsUserScope = isSamePath(projectClaudeDir, claudeDir);
7712
7899
  if (!overlapsUserScope) {
7713
- if (import_fs29.default.existsSync(import_path32.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
7714
- rulesCount += countRulesInDir(import_path32.default.join(projectClaudeDir, "rules"));
7715
- const projSettings = import_path32.default.join(projectClaudeDir, "settings.json");
7900
+ if (import_fs30.default.existsSync(import_path33.default.join(projectClaudeDir, "CLAUDE.md"))) claudeMdCount++;
7901
+ rulesCount += countRulesInDir(import_path33.default.join(projectClaudeDir, "rules"));
7902
+ const projSettings = import_path33.default.join(projectClaudeDir, "settings.json");
7716
7903
  for (const name of getMcpServerNames(projSettings)) projectMcpServers.add(name);
7717
7904
  hooksCount += countHooksInFile(projSettings);
7718
7905
  }
7719
- if (import_fs29.default.existsSync(import_path32.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
7720
- const localSettings = import_path32.default.join(projectClaudeDir, "settings.local.json");
7906
+ if (import_fs30.default.existsSync(import_path33.default.join(projectClaudeDir, "CLAUDE.local.md"))) claudeMdCount++;
7907
+ const localSettings = import_path33.default.join(projectClaudeDir, "settings.local.json");
7721
7908
  for (const name of getMcpServerNames(localSettings)) projectMcpServers.add(name);
7722
7909
  hooksCount += countHooksInFile(localSettings);
7723
- const mcpJsonServers = getMcpServerNames(import_path32.default.join(cwd, ".mcp.json"));
7910
+ const mcpJsonServers = getMcpServerNames(import_path33.default.join(cwd, ".mcp.json"));
7724
7911
  const disabledMcpJson = getDisabledMcpServers(localSettings, "disabledMcpjsonServers");
7725
7912
  for (const name of disabledMcpJson) mcpJsonServers.delete(name);
7726
7913
  for (const name of mcpJsonServers) projectMcpServers.add(name);
@@ -7753,12 +7940,12 @@ function readActiveShieldsHud() {
7753
7940
  return shieldsCache.value;
7754
7941
  }
7755
7942
  try {
7756
- const shieldsPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "shields.json");
7757
- if (!import_fs29.default.existsSync(shieldsPath)) {
7943
+ const shieldsPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "shields.json");
7944
+ if (!import_fs30.default.existsSync(shieldsPath)) {
7758
7945
  shieldsCache = { value: [], ts: now };
7759
7946
  return [];
7760
7947
  }
7761
- const parsed = JSON.parse(import_fs29.default.readFileSync(shieldsPath, "utf-8"));
7948
+ const parsed = JSON.parse(import_fs30.default.readFileSync(shieldsPath, "utf-8"));
7762
7949
  if (!Array.isArray(parsed.active)) {
7763
7950
  shieldsCache = { value: [], ts: now };
7764
7951
  return [];
@@ -7860,17 +8047,17 @@ function renderContextLine(stdin) {
7860
8047
  async function main() {
7861
8048
  try {
7862
8049
  const [stdin, daemonStatus2] = await Promise.all([readStdin(), queryDaemon()]);
7863
- if (import_fs29.default.existsSync(import_path32.default.join(import_os25.default.homedir(), ".node9", "hud-debug"))) {
8050
+ if (import_fs30.default.existsSync(import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug"))) {
7864
8051
  try {
7865
- const logPath = import_path32.default.join(import_os25.default.homedir(), ".node9", "hud-debug.log");
8052
+ const logPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug.log");
7866
8053
  const MAX_LOG_SIZE = 10 * 1024 * 1024;
7867
8054
  let size = 0;
7868
8055
  try {
7869
- size = import_fs29.default.statSync(logPath).size;
8056
+ size = import_fs30.default.statSync(logPath).size;
7870
8057
  } catch {
7871
8058
  }
7872
8059
  if (size < MAX_LOG_SIZE) {
7873
- import_fs29.default.appendFileSync(
8060
+ import_fs30.default.appendFileSync(
7874
8061
  logPath,
7875
8062
  JSON.stringify({ ts: (/* @__PURE__ */ new Date()).toISOString(), stdin }) + "\n"
7876
8063
  );
@@ -7891,11 +8078,11 @@ async function main() {
7891
8078
  try {
7892
8079
  const cwd = stdin.cwd ?? process.cwd();
7893
8080
  for (const configPath of [
7894
- import_path32.default.join(cwd, "node9.config.json"),
7895
- import_path32.default.join(import_os25.default.homedir(), ".node9", "config.json")
8081
+ import_path33.default.join(cwd, "node9.config.json"),
8082
+ import_path33.default.join(import_os26.default.homedir(), ".node9", "config.json")
7896
8083
  ]) {
7897
- if (!import_fs29.default.existsSync(configPath)) continue;
7898
- const cfg = JSON.parse(import_fs29.default.readFileSync(configPath, "utf-8"));
8084
+ if (!import_fs30.default.existsSync(configPath)) continue;
8085
+ const cfg = JSON.parse(import_fs30.default.readFileSync(configPath, "utf-8"));
7899
8086
  const hud = cfg.settings?.hud;
7900
8087
  if (hud && "showEnvironmentCounts" in hud) return hud.showEnvironmentCounts !== false;
7901
8088
  }
@@ -7913,13 +8100,13 @@ async function main() {
7913
8100
  renderOffline();
7914
8101
  }
7915
8102
  }
7916
- var import_fs29, import_path32, import_os25, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
8103
+ var import_fs30, import_path33, import_os26, import_http3, RESET3, BOLD3, DIM, RED2, GREEN2, YELLOW2, BLUE, MAGENTA, CYAN2, WHITE, BAR_FILLED, BAR_EMPTY, BAR_WIDTH, shieldsCache, SHIELDS_CACHE_TTL_MS;
7917
8104
  var init_hud = __esm({
7918
8105
  "src/cli/hud.ts"() {
7919
8106
  "use strict";
7920
- import_fs29 = __toESM(require("fs"));
7921
- import_path32 = __toESM(require("path"));
7922
- import_os25 = __toESM(require("os"));
8107
+ import_fs30 = __toESM(require("fs"));
8108
+ import_path33 = __toESM(require("path"));
8109
+ import_os26 = __toESM(require("os"));
7923
8110
  import_http3 = __toESM(require("http"));
7924
8111
  init_daemon();
7925
8112
  RESET3 = "\x1B[0m";
@@ -8529,9 +8716,9 @@ function teardownHud() {
8529
8716
  // src/cli.ts
8530
8717
  init_daemon2();
8531
8718
  var import_chalk20 = __toESM(require("chalk"));
8532
- var import_fs30 = __toESM(require("fs"));
8533
- var import_path33 = __toESM(require("path"));
8534
- var import_os26 = __toESM(require("os"));
8719
+ var import_fs31 = __toESM(require("fs"));
8720
+ var import_path34 = __toESM(require("path"));
8721
+ var import_os27 = __toESM(require("os"));
8535
8722
  var import_prompts2 = require("@inquirer/prompts");
8536
8723
 
8537
8724
  // src/utils/duration.ts
@@ -8756,10 +8943,10 @@ async function autoStartDaemonAndWait() {
8756
8943
 
8757
8944
  // src/cli/commands/check.ts
8758
8945
  var import_chalk5 = __toESM(require("chalk"));
8759
- var import_fs19 = __toESM(require("fs"));
8946
+ var import_fs20 = __toESM(require("fs"));
8760
8947
  var import_child_process9 = require("child_process");
8761
- var import_path21 = __toESM(require("path"));
8762
- var import_os15 = __toESM(require("os"));
8948
+ var import_path22 = __toESM(require("path"));
8949
+ var import_os16 = __toESM(require("os"));
8763
8950
  init_orchestrator();
8764
8951
  init_daemon();
8765
8952
  init_config();
@@ -8768,11 +8955,11 @@ init_policy();
8768
8955
  // src/undo.ts
8769
8956
  var import_child_process8 = require("child_process");
8770
8957
  var import_crypto8 = __toESM(require("crypto"));
8771
- var import_fs18 = __toESM(require("fs"));
8958
+ var import_fs19 = __toESM(require("fs"));
8772
8959
  var import_net3 = __toESM(require("net"));
8773
- var import_path20 = __toESM(require("path"));
8774
- var import_os14 = __toESM(require("os"));
8775
- var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path20.default.join(import_os14.default.tmpdir(), "node9-activity.sock");
8960
+ var import_path21 = __toESM(require("path"));
8961
+ var import_os15 = __toESM(require("os"));
8962
+ var ACTIVITY_SOCKET_PATH3 = process.platform === "win32" ? "\\\\.\\pipe\\node9-activity" : import_path21.default.join(import_os15.default.tmpdir(), "node9-activity.sock");
8776
8963
  function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
8777
8964
  try {
8778
8965
  const payload = JSON.stringify({
@@ -8792,22 +8979,22 @@ function notifySnapshotTaken(hash, tool, argsSummary, fileCount) {
8792
8979
  } catch {
8793
8980
  }
8794
8981
  }
8795
- var SNAPSHOT_STACK_PATH = import_path20.default.join(import_os14.default.homedir(), ".node9", "snapshots.json");
8796
- var UNDO_LATEST_PATH = import_path20.default.join(import_os14.default.homedir(), ".node9", "undo_latest.txt");
8982
+ var SNAPSHOT_STACK_PATH = import_path21.default.join(import_os15.default.homedir(), ".node9", "snapshots.json");
8983
+ var UNDO_LATEST_PATH = import_path21.default.join(import_os15.default.homedir(), ".node9", "undo_latest.txt");
8797
8984
  var MAX_SNAPSHOTS = 10;
8798
8985
  var GIT_TIMEOUT = 15e3;
8799
8986
  function readStack() {
8800
8987
  try {
8801
- if (import_fs18.default.existsSync(SNAPSHOT_STACK_PATH))
8802
- return JSON.parse(import_fs18.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
8988
+ if (import_fs19.default.existsSync(SNAPSHOT_STACK_PATH))
8989
+ return JSON.parse(import_fs19.default.readFileSync(SNAPSHOT_STACK_PATH, "utf-8"));
8803
8990
  } catch {
8804
8991
  }
8805
8992
  return [];
8806
8993
  }
8807
8994
  function writeStack(stack) {
8808
- const dir = import_path20.default.dirname(SNAPSHOT_STACK_PATH);
8809
- if (!import_fs18.default.existsSync(dir)) import_fs18.default.mkdirSync(dir, { recursive: true });
8810
- import_fs18.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
8995
+ const dir = import_path21.default.dirname(SNAPSHOT_STACK_PATH);
8996
+ if (!import_fs19.default.existsSync(dir)) import_fs19.default.mkdirSync(dir, { recursive: true });
8997
+ import_fs19.default.writeFileSync(SNAPSHOT_STACK_PATH, JSON.stringify(stack, null, 2));
8811
8998
  }
8812
8999
  function extractFilePath(args) {
8813
9000
  if (!args || typeof args !== "object") return null;
@@ -8827,12 +9014,12 @@ function buildArgsSummary(tool, args) {
8827
9014
  return "";
8828
9015
  }
8829
9016
  function findProjectRoot(filePath) {
8830
- let dir = import_path20.default.dirname(filePath);
9017
+ let dir = import_path21.default.dirname(filePath);
8831
9018
  while (true) {
8832
- if (import_fs18.default.existsSync(import_path20.default.join(dir, ".git")) || import_fs18.default.existsSync(import_path20.default.join(dir, "package.json"))) {
9019
+ if (import_fs19.default.existsSync(import_path21.default.join(dir, ".git")) || import_fs19.default.existsSync(import_path21.default.join(dir, "package.json"))) {
8833
9020
  return dir;
8834
9021
  }
8835
- const parent = import_path20.default.dirname(dir);
9022
+ const parent = import_path21.default.dirname(dir);
8836
9023
  if (parent === dir) return process.cwd();
8837
9024
  dir = parent;
8838
9025
  }
@@ -8840,7 +9027,7 @@ function findProjectRoot(filePath) {
8840
9027
  function normalizeCwdForHash(cwd) {
8841
9028
  let normalized;
8842
9029
  try {
8843
- normalized = import_fs18.default.realpathSync(cwd);
9030
+ normalized = import_fs19.default.realpathSync(cwd);
8844
9031
  } catch {
8845
9032
  normalized = cwd;
8846
9033
  }
@@ -8850,16 +9037,16 @@ function normalizeCwdForHash(cwd) {
8850
9037
  }
8851
9038
  function getShadowRepoDir(cwd) {
8852
9039
  const hash = import_crypto8.default.createHash("sha256").update(normalizeCwdForHash(cwd)).digest("hex").slice(0, 16);
8853
- return import_path20.default.join(import_os14.default.homedir(), ".node9", "snapshots", hash);
9040
+ return import_path21.default.join(import_os15.default.homedir(), ".node9", "snapshots", hash);
8854
9041
  }
8855
9042
  function cleanOrphanedIndexFiles(shadowDir) {
8856
9043
  try {
8857
9044
  const cutoff = Date.now() - 6e4;
8858
- for (const f of import_fs18.default.readdirSync(shadowDir)) {
9045
+ for (const f of import_fs19.default.readdirSync(shadowDir)) {
8859
9046
  if (f.startsWith("index_")) {
8860
- const fp = import_path20.default.join(shadowDir, f);
9047
+ const fp = import_path21.default.join(shadowDir, f);
8861
9048
  try {
8862
- if (import_fs18.default.statSync(fp).mtimeMs < cutoff) import_fs18.default.unlinkSync(fp);
9049
+ if (import_fs19.default.statSync(fp).mtimeMs < cutoff) import_fs19.default.unlinkSync(fp);
8863
9050
  } catch {
8864
9051
  }
8865
9052
  }
@@ -8871,7 +9058,7 @@ function writeShadowExcludes(shadowDir, ignorePaths) {
8871
9058
  const hardcoded = [".git", ".node9"];
8872
9059
  const lines = [...hardcoded, ...ignorePaths].join("\n");
8873
9060
  try {
8874
- import_fs18.default.writeFileSync(import_path20.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
9061
+ import_fs19.default.writeFileSync(import_path21.default.join(shadowDir, "info", "exclude"), lines + "\n", "utf8");
8875
9062
  } catch {
8876
9063
  }
8877
9064
  }
@@ -8884,25 +9071,25 @@ function ensureShadowRepo(shadowDir, cwd) {
8884
9071
  timeout: 3e3
8885
9072
  });
8886
9073
  if (check.status === 0) {
8887
- const ptPath = import_path20.default.join(shadowDir, "project-path.txt");
9074
+ const ptPath = import_path21.default.join(shadowDir, "project-path.txt");
8888
9075
  try {
8889
- const stored = import_fs18.default.readFileSync(ptPath, "utf8").trim();
9076
+ const stored = import_fs19.default.readFileSync(ptPath, "utf8").trim();
8890
9077
  if (stored === normalizedCwd) return true;
8891
9078
  if (process.env.NODE9_DEBUG === "1")
8892
9079
  console.error(
8893
9080
  `[Node9] Shadow repo path mismatch: stored="${stored}" expected="${normalizedCwd}" \u2014 reinitializing`
8894
9081
  );
8895
- import_fs18.default.rmSync(shadowDir, { recursive: true, force: true });
9082
+ import_fs19.default.rmSync(shadowDir, { recursive: true, force: true });
8896
9083
  } catch {
8897
9084
  try {
8898
- import_fs18.default.writeFileSync(ptPath, normalizedCwd, "utf8");
9085
+ import_fs19.default.writeFileSync(ptPath, normalizedCwd, "utf8");
8899
9086
  } catch {
8900
9087
  }
8901
9088
  return true;
8902
9089
  }
8903
9090
  }
8904
9091
  try {
8905
- import_fs18.default.mkdirSync(shadowDir, { recursive: true });
9092
+ import_fs19.default.mkdirSync(shadowDir, { recursive: true });
8906
9093
  } catch {
8907
9094
  }
8908
9095
  const init = (0, import_child_process8.spawnSync)("git", ["init", "--bare", shadowDir], { timeout: 5e3 });
@@ -8911,7 +9098,7 @@ function ensureShadowRepo(shadowDir, cwd) {
8911
9098
  if (process.env.NODE9_DEBUG === "1") console.error("[Node9] git init --bare failed:", reason);
8912
9099
  return false;
8913
9100
  }
8914
- const configFile = import_path20.default.join(shadowDir, "config");
9101
+ const configFile = import_path21.default.join(shadowDir, "config");
8915
9102
  (0, import_child_process8.spawnSync)("git", ["config", "--file", configFile, "core.untrackedCache", "true"], {
8916
9103
  timeout: 3e3
8917
9104
  });
@@ -8919,7 +9106,7 @@ function ensureShadowRepo(shadowDir, cwd) {
8919
9106
  timeout: 3e3
8920
9107
  });
8921
9108
  try {
8922
- import_fs18.default.writeFileSync(import_path20.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
9109
+ import_fs19.default.writeFileSync(import_path21.default.join(shadowDir, "project-path.txt"), normalizedCwd, "utf8");
8923
9110
  } catch {
8924
9111
  }
8925
9112
  return true;
@@ -8939,12 +9126,12 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
8939
9126
  let indexFile = null;
8940
9127
  try {
8941
9128
  const rawFilePath = extractFilePath(args);
8942
- const absFilePath = rawFilePath && import_path20.default.isAbsolute(rawFilePath) ? rawFilePath : null;
9129
+ const absFilePath = rawFilePath && import_path21.default.isAbsolute(rawFilePath) ? rawFilePath : null;
8943
9130
  const cwd = absFilePath ? findProjectRoot(absFilePath) : process.cwd();
8944
9131
  const shadowDir = getShadowRepoDir(cwd);
8945
9132
  if (!ensureShadowRepo(shadowDir, cwd)) return null;
8946
9133
  writeShadowExcludes(shadowDir, ignorePaths);
8947
- indexFile = import_path20.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
9134
+ indexFile = import_path21.default.join(shadowDir, `index_${process.pid}_${Date.now()}`);
8948
9135
  const shadowEnv = {
8949
9136
  ...process.env,
8950
9137
  GIT_DIR: shadowDir,
@@ -9016,7 +9203,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
9016
9203
  writeStack(stack);
9017
9204
  const entry = stack[stack.length - 1];
9018
9205
  notifySnapshotTaken(commitHash.slice(0, 7), tool, entry.argsSummary, capturedFiles.length);
9019
- import_fs18.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
9206
+ import_fs19.default.writeFileSync(UNDO_LATEST_PATH, commitHash);
9020
9207
  if (shouldGc) {
9021
9208
  (0, import_child_process8.spawn)("git", ["gc", "--auto"], { env: shadowEnv, detached: true, stdio: "ignore" }).unref();
9022
9209
  }
@@ -9027,7 +9214,7 @@ async function createShadowSnapshot(tool = "unknown", args = {}, ignorePaths = [
9027
9214
  } finally {
9028
9215
  if (indexFile) {
9029
9216
  try {
9030
- import_fs18.default.unlinkSync(indexFile);
9217
+ import_fs19.default.unlinkSync(indexFile);
9031
9218
  } catch {
9032
9219
  }
9033
9220
  }
@@ -9103,9 +9290,9 @@ function applyUndo(hash, cwd) {
9103
9290
  timeout: GIT_TIMEOUT
9104
9291
  }).stdout?.toString().trim().split("\n").filter(Boolean) ?? [];
9105
9292
  for (const file of [...tracked, ...untracked]) {
9106
- const fullPath = import_path20.default.join(dir, file);
9107
- if (!snapshotFiles.has(file) && import_fs18.default.existsSync(fullPath)) {
9108
- import_fs18.default.unlinkSync(fullPath);
9293
+ const fullPath = import_path21.default.join(dir, file);
9294
+ if (!snapshotFiles.has(file) && import_fs19.default.existsSync(fullPath)) {
9295
+ import_fs19.default.unlinkSync(fullPath);
9109
9296
  }
9110
9297
  }
9111
9298
  return true;
@@ -9129,9 +9316,9 @@ function registerCheckCommand(program2) {
9129
9316
  } catch (err2) {
9130
9317
  const tempConfig = getConfig();
9131
9318
  if (process.env.NODE9_DEBUG === "1" || tempConfig.settings.enableHookLogDebug) {
9132
- const logPath = import_path21.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
9319
+ const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
9133
9320
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
9134
- import_fs19.default.appendFileSync(
9321
+ import_fs20.default.appendFileSync(
9135
9322
  logPath,
9136
9323
  `[${(/* @__PURE__ */ new Date()).toISOString()}] JSON_PARSE_ERROR: ${errMsg}
9137
9324
  RAW: ${raw}
@@ -9144,10 +9331,10 @@ RAW: ${raw}
9144
9331
  if (config.settings.autoStartDaemon && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON) {
9145
9332
  try {
9146
9333
  const scriptPath = process.argv[1];
9147
- if (typeof scriptPath !== "string" || !import_path21.default.isAbsolute(scriptPath))
9334
+ if (typeof scriptPath !== "string" || !import_path22.default.isAbsolute(scriptPath))
9148
9335
  throw new Error("node9: argv[1] is not an absolute path");
9149
- const resolvedScript = import_fs19.default.realpathSync(scriptPath);
9150
- const expectedCli = import_fs19.default.realpathSync(import_path21.default.resolve(__dirname, "../../cli.js"));
9336
+ const resolvedScript = import_fs20.default.realpathSync(scriptPath);
9337
+ const expectedCli = import_fs20.default.realpathSync(import_path22.default.resolve(__dirname, "../../cli.js"));
9151
9338
  if (resolvedScript !== expectedCli)
9152
9339
  throw new Error(
9153
9340
  "node9: daemon spawn aborted \u2014 argv[1] does not resolve to the node9 CLI"
@@ -9173,10 +9360,10 @@ RAW: ${raw}
9173
9360
  }
9174
9361
  }
9175
9362
  if (process.env.NODE9_DEBUG === "1" || config.settings.enableHookLogDebug) {
9176
- const logPath = import_path21.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
9177
- if (!import_fs19.default.existsSync(import_path21.default.dirname(logPath)))
9178
- import_fs19.default.mkdirSync(import_path21.default.dirname(logPath), { recursive: true });
9179
- import_fs19.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
9363
+ const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
9364
+ if (!import_fs20.default.existsSync(import_path22.default.dirname(logPath)))
9365
+ import_fs20.default.mkdirSync(import_path22.default.dirname(logPath), { recursive: true });
9366
+ import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] STDIN: ${raw}
9180
9367
  `);
9181
9368
  }
9182
9369
  const toolName = sanitize2(payload.tool_name ?? payload.name ?? "");
@@ -9189,8 +9376,8 @@ RAW: ${raw}
9189
9376
  const isHumanDecision = blockedByContext.toLowerCase().includes("user") || blockedByContext.toLowerCase().includes("daemon") || blockedByContext.toLowerCase().includes("decision");
9190
9377
  let ttyFd = null;
9191
9378
  try {
9192
- ttyFd = import_fs19.default.openSync("/dev/tty", "w");
9193
- const writeTty = (line) => import_fs19.default.writeSync(ttyFd, line + "\n");
9379
+ ttyFd = import_fs20.default.openSync("/dev/tty", "w");
9380
+ const writeTty = (line) => import_fs20.default.writeSync(ttyFd, line + "\n");
9194
9381
  if (blockedByContext.includes("DLP") || blockedByContext.includes("Secret Detected") || blockedByContext.includes("Credential Review")) {
9195
9382
  writeTty(import_chalk5.default.bgRed.white.bold(`
9196
9383
  \u{1F6A8} NODE9 DLP ALERT \u2014 CREDENTIAL DETECTED `));
@@ -9209,7 +9396,7 @@ RAW: ${raw}
9209
9396
  } finally {
9210
9397
  if (ttyFd !== null)
9211
9398
  try {
9212
- import_fs19.default.closeSync(ttyFd);
9399
+ import_fs20.default.closeSync(ttyFd);
9213
9400
  } catch {
9214
9401
  }
9215
9402
  }
@@ -9241,7 +9428,7 @@ RAW: ${raw}
9241
9428
  if (shouldSnapshot(toolName, toolInput, config)) {
9242
9429
  await createShadowSnapshot(toolName, toolInput, config.policy.snapshot.ignorePaths);
9243
9430
  }
9244
- const safeCwdForAuth = typeof payload.cwd === "string" && import_path21.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
9431
+ const safeCwdForAuth = typeof payload.cwd === "string" && import_path22.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
9245
9432
  const result = await authorizeHeadless(toolName, toolInput, meta, {
9246
9433
  cwd: safeCwdForAuth
9247
9434
  });
@@ -9253,12 +9440,12 @@ RAW: ${raw}
9253
9440
  }
9254
9441
  if (result.noApprovalMechanism && !isDaemonRunning() && !process.env.NODE9_NO_AUTO_DAEMON && !process.stdout.isTTY && config.settings.autoStartDaemon) {
9255
9442
  try {
9256
- const tty = import_fs19.default.openSync("/dev/tty", "w");
9257
- import_fs19.default.writeSync(
9443
+ const tty = import_fs20.default.openSync("/dev/tty", "w");
9444
+ import_fs20.default.writeSync(
9258
9445
  tty,
9259
9446
  import_chalk5.default.cyan("\n\u{1F6E1}\uFE0F Node9: Starting approval daemon automatically...\n")
9260
9447
  );
9261
- import_fs19.default.closeSync(tty);
9448
+ import_fs20.default.closeSync(tty);
9262
9449
  } catch {
9263
9450
  }
9264
9451
  const daemonReady = await autoStartDaemonAndWait();
@@ -9285,9 +9472,9 @@ RAW: ${raw}
9285
9472
  });
9286
9473
  } catch (err2) {
9287
9474
  if (process.env.NODE9_DEBUG === "1") {
9288
- const logPath = import_path21.default.join(import_os15.default.homedir(), ".node9", "hook-debug.log");
9475
+ const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
9289
9476
  const errMsg = err2 instanceof Error ? err2.message : String(err2);
9290
- import_fs19.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
9477
+ import_fs20.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] ERROR: ${errMsg}
9291
9478
  `);
9292
9479
  }
9293
9480
  process.exit(0);
@@ -9321,9 +9508,9 @@ RAW: ${raw}
9321
9508
  }
9322
9509
 
9323
9510
  // src/cli/commands/log.ts
9324
- var import_fs20 = __toESM(require("fs"));
9325
- var import_path22 = __toESM(require("path"));
9326
- var import_os16 = __toESM(require("os"));
9511
+ var import_fs21 = __toESM(require("fs"));
9512
+ var import_path23 = __toESM(require("path"));
9513
+ var import_os17 = __toESM(require("os"));
9327
9514
  init_audit();
9328
9515
  init_config();
9329
9516
  init_policy();
@@ -9399,10 +9586,10 @@ function registerLogCommand(program2) {
9399
9586
  decision: "allowed",
9400
9587
  source: "post-hook"
9401
9588
  };
9402
- const logPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "audit.log");
9403
- if (!import_fs20.default.existsSync(import_path22.default.dirname(logPath)))
9404
- import_fs20.default.mkdirSync(import_path22.default.dirname(logPath), { recursive: true });
9405
- import_fs20.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
9589
+ const logPath = import_path23.default.join(import_os17.default.homedir(), ".node9", "audit.log");
9590
+ if (!import_fs21.default.existsSync(import_path23.default.dirname(logPath)))
9591
+ import_fs21.default.mkdirSync(import_path23.default.dirname(logPath), { recursive: true });
9592
+ import_fs21.default.appendFileSync(logPath, JSON.stringify(entry) + "\n");
9406
9593
  if ((tool === "Bash" || tool === "bash") && isDaemonRunning()) {
9407
9594
  const command = typeof rawInput === "object" && rawInput !== null && "command" in rawInput && typeof rawInput.command === "string" ? rawInput.command : null;
9408
9595
  if (command) {
@@ -9435,7 +9622,7 @@ function registerLogCommand(program2) {
9435
9622
  }
9436
9623
  }
9437
9624
  }
9438
- const safeCwd = typeof payload.cwd === "string" && import_path22.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
9625
+ const safeCwd = typeof payload.cwd === "string" && import_path23.default.isAbsolute(payload.cwd) ? payload.cwd : void 0;
9439
9626
  const config = getConfig(safeCwd);
9440
9627
  if (shouldSnapshot(tool, {}, config)) {
9441
9628
  await createShadowSnapshot("unknown", {}, config.policy.snapshot.ignorePaths);
@@ -9444,9 +9631,9 @@ function registerLogCommand(program2) {
9444
9631
  const msg = err2 instanceof Error ? err2.message : String(err2);
9445
9632
  process.stderr.write(`[Node9] audit log error: ${msg}
9446
9633
  `);
9447
- const debugPath = import_path22.default.join(import_os16.default.homedir(), ".node9", "hook-debug.log");
9634
+ const debugPath = import_path23.default.join(import_os17.default.homedir(), ".node9", "hook-debug.log");
9448
9635
  try {
9449
- import_fs20.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
9636
+ import_fs21.default.appendFileSync(debugPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] LOG_ERROR: ${msg}
9450
9637
  `);
9451
9638
  } catch {
9452
9639
  }
@@ -9846,14 +10033,14 @@ function registerConfigShowCommand(program2) {
9846
10033
 
9847
10034
  // src/cli/commands/doctor.ts
9848
10035
  var import_chalk7 = __toESM(require("chalk"));
9849
- var import_fs21 = __toESM(require("fs"));
9850
- var import_path23 = __toESM(require("path"));
9851
- var import_os17 = __toESM(require("os"));
10036
+ var import_fs22 = __toESM(require("fs"));
10037
+ var import_path24 = __toESM(require("path"));
10038
+ var import_os18 = __toESM(require("os"));
9852
10039
  var import_child_process10 = require("child_process");
9853
10040
  init_daemon();
9854
10041
  function registerDoctorCommand(program2, version2) {
9855
10042
  program2.command("doctor").description("Check that Node9 is installed and configured correctly").action(() => {
9856
- const homeDir2 = import_os17.default.homedir();
10043
+ const homeDir2 = import_os18.default.homedir();
9857
10044
  let failures = 0;
9858
10045
  function pass(msg) {
9859
10046
  console.log(import_chalk7.default.green(" \u2705 ") + msg);
@@ -9902,10 +10089,10 @@ function registerDoctorCommand(program2, version2) {
9902
10089
  );
9903
10090
  }
9904
10091
  section("Configuration");
9905
- const globalConfigPath = import_path23.default.join(homeDir2, ".node9", "config.json");
9906
- if (import_fs21.default.existsSync(globalConfigPath)) {
10092
+ const globalConfigPath = import_path24.default.join(homeDir2, ".node9", "config.json");
10093
+ if (import_fs22.default.existsSync(globalConfigPath)) {
9907
10094
  try {
9908
- JSON.parse(import_fs21.default.readFileSync(globalConfigPath, "utf-8"));
10095
+ JSON.parse(import_fs22.default.readFileSync(globalConfigPath, "utf-8"));
9909
10096
  pass("~/.node9/config.json found and valid");
9910
10097
  } catch {
9911
10098
  fail("~/.node9/config.json is invalid JSON", "Run: node9 init --force");
@@ -9913,10 +10100,10 @@ function registerDoctorCommand(program2, version2) {
9913
10100
  } else {
9914
10101
  warn("~/.node9/config.json not found (using defaults)", "Run: node9 init");
9915
10102
  }
9916
- const projectConfigPath = import_path23.default.join(process.cwd(), "node9.config.json");
9917
- if (import_fs21.default.existsSync(projectConfigPath)) {
10103
+ const projectConfigPath = import_path24.default.join(process.cwd(), "node9.config.json");
10104
+ if (import_fs22.default.existsSync(projectConfigPath)) {
9918
10105
  try {
9919
- JSON.parse(import_fs21.default.readFileSync(projectConfigPath, "utf-8"));
10106
+ JSON.parse(import_fs22.default.readFileSync(projectConfigPath, "utf-8"));
9920
10107
  pass("node9.config.json found and valid (project)");
9921
10108
  } catch {
9922
10109
  fail(
@@ -9925,8 +10112,8 @@ function registerDoctorCommand(program2, version2) {
9925
10112
  );
9926
10113
  }
9927
10114
  }
9928
- const credsPath = import_path23.default.join(homeDir2, ".node9", "credentials.json");
9929
- if (import_fs21.default.existsSync(credsPath)) {
10115
+ const credsPath = import_path24.default.join(homeDir2, ".node9", "credentials.json");
10116
+ if (import_fs22.default.existsSync(credsPath)) {
9930
10117
  pass("Cloud credentials found (~/.node9/credentials.json)");
9931
10118
  } else {
9932
10119
  warn(
@@ -9935,10 +10122,10 @@ function registerDoctorCommand(program2, version2) {
9935
10122
  );
9936
10123
  }
9937
10124
  section("Agent Hooks");
9938
- const claudeSettingsPath = import_path23.default.join(homeDir2, ".claude", "settings.json");
9939
- if (import_fs21.default.existsSync(claudeSettingsPath)) {
10125
+ const claudeSettingsPath = import_path24.default.join(homeDir2, ".claude", "settings.json");
10126
+ if (import_fs22.default.existsSync(claudeSettingsPath)) {
9940
10127
  try {
9941
- const cs = JSON.parse(import_fs21.default.readFileSync(claudeSettingsPath, "utf-8"));
10128
+ const cs = JSON.parse(import_fs22.default.readFileSync(claudeSettingsPath, "utf-8"));
9942
10129
  const hasHook = cs.hooks?.PreToolUse?.some(
9943
10130
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
9944
10131
  );
@@ -9954,10 +10141,10 @@ function registerDoctorCommand(program2, version2) {
9954
10141
  } else {
9955
10142
  warn("Claude Code \u2014 not configured", "Run: node9 setup claude");
9956
10143
  }
9957
- const geminiSettingsPath = import_path23.default.join(homeDir2, ".gemini", "settings.json");
9958
- if (import_fs21.default.existsSync(geminiSettingsPath)) {
10144
+ const geminiSettingsPath = import_path24.default.join(homeDir2, ".gemini", "settings.json");
10145
+ if (import_fs22.default.existsSync(geminiSettingsPath)) {
9959
10146
  try {
9960
- const gs = JSON.parse(import_fs21.default.readFileSync(geminiSettingsPath, "utf-8"));
10147
+ const gs = JSON.parse(import_fs22.default.readFileSync(geminiSettingsPath, "utf-8"));
9961
10148
  const hasHook = gs.hooks?.BeforeTool?.some(
9962
10149
  (m) => m.hooks.some((h) => h.command?.includes("node9") || h.command?.includes("cli.js"))
9963
10150
  );
@@ -9973,10 +10160,10 @@ function registerDoctorCommand(program2, version2) {
9973
10160
  } else {
9974
10161
  warn("Gemini CLI \u2014 not configured", "Run: node9 setup gemini (skip if not using Gemini)");
9975
10162
  }
9976
- const cursorHooksPath = import_path23.default.join(homeDir2, ".cursor", "hooks.json");
9977
- if (import_fs21.default.existsSync(cursorHooksPath)) {
10163
+ const cursorHooksPath = import_path24.default.join(homeDir2, ".cursor", "hooks.json");
10164
+ if (import_fs22.default.existsSync(cursorHooksPath)) {
9978
10165
  try {
9979
- const cur = JSON.parse(import_fs21.default.readFileSync(cursorHooksPath, "utf-8"));
10166
+ const cur = JSON.parse(import_fs22.default.readFileSync(cursorHooksPath, "utf-8"));
9980
10167
  const hasHook = cur.hooks?.preToolUse?.some(
9981
10168
  (h) => h.command?.includes("node9") || h.command?.includes("cli.js")
9982
10169
  );
@@ -10014,9 +10201,9 @@ function registerDoctorCommand(program2, version2) {
10014
10201
 
10015
10202
  // src/cli/commands/audit.ts
10016
10203
  var import_chalk8 = __toESM(require("chalk"));
10017
- var import_fs22 = __toESM(require("fs"));
10018
- var import_path24 = __toESM(require("path"));
10019
- var import_os18 = __toESM(require("os"));
10204
+ var import_fs23 = __toESM(require("fs"));
10205
+ var import_path25 = __toESM(require("path"));
10206
+ var import_os19 = __toESM(require("os"));
10020
10207
  function formatRelativeTime(timestamp) {
10021
10208
  const diff = Date.now() - new Date(timestamp).getTime();
10022
10209
  const sec = Math.floor(diff / 1e3);
@@ -10029,14 +10216,14 @@ function formatRelativeTime(timestamp) {
10029
10216
  }
10030
10217
  function registerAuditCommand(program2) {
10031
10218
  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) => {
10032
- const logPath = import_path24.default.join(import_os18.default.homedir(), ".node9", "audit.log");
10033
- if (!import_fs22.default.existsSync(logPath)) {
10219
+ const logPath = import_path25.default.join(import_os19.default.homedir(), ".node9", "audit.log");
10220
+ if (!import_fs23.default.existsSync(logPath)) {
10034
10221
  console.log(
10035
10222
  import_chalk8.default.yellow("No audit logs found. Run node9 with an agent to generate entries.")
10036
10223
  );
10037
10224
  return;
10038
10225
  }
10039
- const raw = import_fs22.default.readFileSync(logPath, "utf-8");
10226
+ const raw = import_fs23.default.readFileSync(logPath, "utf-8");
10040
10227
  const lines = raw.split("\n").filter((l) => l.trim() !== "");
10041
10228
  let entries = lines.flatMap((line) => {
10042
10229
  try {
@@ -10090,9 +10277,9 @@ function registerAuditCommand(program2) {
10090
10277
 
10091
10278
  // src/cli/commands/report.ts
10092
10279
  var import_chalk9 = __toESM(require("chalk"));
10093
- var import_fs23 = __toESM(require("fs"));
10094
- var import_path25 = __toESM(require("path"));
10095
- var import_os19 = __toESM(require("os"));
10280
+ var import_fs24 = __toESM(require("fs"));
10281
+ var import_path26 = __toESM(require("path"));
10282
+ var import_os20 = __toESM(require("os"));
10096
10283
  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;
10097
10284
  function buildTestTimestamps(allEntries) {
10098
10285
  const testTs = /* @__PURE__ */ new Set();
@@ -10139,8 +10326,8 @@ function getDateRange(period) {
10139
10326
  }
10140
10327
  }
10141
10328
  function parseAuditLog(logPath) {
10142
- if (!import_fs23.default.existsSync(logPath)) return [];
10143
- const raw = import_fs23.default.readFileSync(logPath, "utf-8");
10329
+ if (!import_fs24.default.existsSync(logPath)) return [];
10330
+ const raw = import_fs24.default.readFileSync(logPath, "utf-8");
10144
10331
  return raw.split("\n").flatMap((line) => {
10145
10332
  if (!line.trim()) return [];
10146
10333
  try {
@@ -10202,29 +10389,39 @@ function claudeModelPrice(model) {
10202
10389
  return null;
10203
10390
  }
10204
10391
  function loadClaudeCost(start, end) {
10205
- const projectsDir = import_path25.default.join(import_os19.default.homedir(), ".claude", "projects");
10206
- if (!import_fs23.default.existsSync(projectsDir)) return { total: 0, byDay: /* @__PURE__ */ new Map() };
10392
+ const empty = {
10393
+ total: 0,
10394
+ byDay: /* @__PURE__ */ new Map(),
10395
+ byModel: /* @__PURE__ */ new Map(),
10396
+ inputTokens: 0,
10397
+ cacheReadTokens: 0
10398
+ };
10399
+ const projectsDir = import_path26.default.join(import_os20.default.homedir(), ".claude", "projects");
10400
+ if (!import_fs24.default.existsSync(projectsDir)) return empty;
10207
10401
  let dirs;
10208
10402
  try {
10209
- dirs = import_fs23.default.readdirSync(projectsDir);
10403
+ dirs = import_fs24.default.readdirSync(projectsDir);
10210
10404
  } catch {
10211
- return { total: 0, byDay: /* @__PURE__ */ new Map() };
10405
+ return empty;
10212
10406
  }
10213
10407
  let total = 0;
10408
+ let inputTokens = 0;
10409
+ let cacheReadTokens = 0;
10214
10410
  const byDay = /* @__PURE__ */ new Map();
10411
+ const byModel = /* @__PURE__ */ new Map();
10215
10412
  for (const proj of dirs) {
10216
- const projPath = import_path25.default.join(projectsDir, proj);
10413
+ const projPath = import_path26.default.join(projectsDir, proj);
10217
10414
  let files;
10218
10415
  try {
10219
- const stat = import_fs23.default.statSync(projPath);
10416
+ const stat = import_fs24.default.statSync(projPath);
10220
10417
  if (!stat.isDirectory()) continue;
10221
- files = import_fs23.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10418
+ files = import_fs24.default.readdirSync(projPath).filter((f) => f.endsWith(".jsonl") && !f.startsWith("agent-"));
10222
10419
  } catch {
10223
10420
  continue;
10224
10421
  }
10225
10422
  for (const file of files) {
10226
10423
  try {
10227
- const raw = import_fs23.default.readFileSync(import_path25.default.join(projPath, file), "utf-8");
10424
+ const raw = import_fs24.default.readFileSync(import_path26.default.join(projPath, file), "utf-8");
10228
10425
  for (const line of raw.split("\n")) {
10229
10426
  if (!line.trim()) continue;
10230
10427
  let entry;
@@ -10242,24 +10439,32 @@ function loadClaudeCost(start, end) {
10242
10439
  if (!usage || !model) continue;
10243
10440
  const p = claudeModelPrice(model);
10244
10441
  if (!p) continue;
10245
- const cost = (usage.input_tokens ?? 0) * p.i + (usage.output_tokens ?? 0) * p.o + (usage.cache_creation_input_tokens ?? 0) * p.cw + (usage.cache_read_input_tokens ?? 0) * p.cr;
10442
+ const inp = usage.input_tokens ?? 0;
10443
+ const out = usage.output_tokens ?? 0;
10444
+ const cw = usage.cache_creation_input_tokens ?? 0;
10445
+ const cr = usage.cache_read_input_tokens ?? 0;
10446
+ const cost = inp * p.i + out * p.o + cw * p.cw + cr * p.cr;
10246
10447
  total += cost;
10448
+ inputTokens += inp;
10449
+ cacheReadTokens += cr;
10247
10450
  const dateKey = entry.timestamp.slice(0, 10);
10248
10451
  byDay.set(dateKey, (byDay.get(dateKey) ?? 0) + cost);
10452
+ const normModel = model.replace(/@.*$/, "").replace(/-\d{8}$/, "");
10453
+ byModel.set(normModel, (byModel.get(normModel) ?? 0) + cost);
10249
10454
  }
10250
10455
  } catch {
10251
10456
  continue;
10252
10457
  }
10253
10458
  }
10254
10459
  }
10255
- return { total, byDay };
10460
+ return { total, byDay, byModel, inputTokens, cacheReadTokens };
10256
10461
  }
10257
10462
  function registerReportCommand(program2) {
10258
10463
  program2.command("report").description("Activity and security report \u2014 what Claude did, what was blocked").option("--period <period>", "today | 7d | 30d | month", "7d").option("--no-tests", "exclude test runner calls (npm test, vitest, pytest\u2026) from stats").action((options) => {
10259
10464
  const period = ["today", "7d", "30d", "month"].includes(
10260
10465
  options.period
10261
10466
  ) ? options.period : "7d";
10262
- const logPath = import_path25.default.join(import_os19.default.homedir(), ".node9", "audit.log");
10467
+ const logPath = import_path26.default.join(import_os20.default.homedir(), ".node9", "audit.log");
10263
10468
  const allEntries = parseAuditLog(logPath);
10264
10469
  if (allEntries.length === 0) {
10265
10470
  console.log(
@@ -10268,7 +10473,13 @@ function registerReportCommand(program2) {
10268
10473
  return;
10269
10474
  }
10270
10475
  const { start, end } = getDateRange(period);
10271
- const { total: costUSD, byDay: costByDay } = loadClaudeCost(start, end);
10476
+ const {
10477
+ total: costUSD,
10478
+ byDay: costByDay,
10479
+ byModel: costByModel,
10480
+ inputTokens: costInputTokens,
10481
+ cacheReadTokens: costCacheRead
10482
+ } = loadClaudeCost(start, end);
10272
10483
  const periodMs = end.getTime() - start.getTime();
10273
10484
  const priorEnd = new Date(start.getTime() - 1);
10274
10485
  const priorStart = new Date(start.getTime() - periodMs);
@@ -10370,7 +10581,6 @@ function registerReportCommand(program2) {
10370
10581
  const blockLabel = blocked > 0 ? import_chalk9.default.red(`\u{1F6D1} ${num(blocked)} blocked`) : import_chalk9.default.dim("\u{1F6D1} 0 blocked");
10371
10582
  const dlpLabel = dlpHits > 0 ? import_chalk9.default.yellow(`\u{1F6A8} ${dlpHits} DLP hits`) : import_chalk9.default.dim("\u{1F6A8} 0 DLP hits");
10372
10583
  const loopLabel = loopHits > 0 ? import_chalk9.default.yellow(`\u{1F504} ${loopHits} loops`) : import_chalk9.default.dim("\u{1F504} 0 loops");
10373
- const costLabel = costUSD > 0 ? import_chalk9.default.magenta(`\u{1F4B0} ${fmtCost(costUSD)}`) : import_chalk9.default.dim("\u{1F4B0} \u2013");
10374
10584
  const currentRate = total > 0 ? blocked / total : 0;
10375
10585
  const trendLabel = (() => {
10376
10586
  if (priorBlockRate === null) return import_chalk9.default.dim(`${pct(blocked, total)} block rate`);
@@ -10383,7 +10593,7 @@ function registerReportCommand(program2) {
10383
10593
  const ratioLabel = reads > 0 ? import_chalk9.default.dim(`edit/read ${(edits / reads).toFixed(1)}`) : import_chalk9.default.dim("edit/read \u2013");
10384
10594
  const testLabel = testPasses + testFails > 0 ? import_chalk9.default.dim("tests ") + import_chalk9.default.green(`${testPasses}\u2713`) + (testFails > 0 ? " " + import_chalk9.default.red(`${testFails}\u2717`) : "") : import_chalk9.default.dim("tests \u2013");
10385
10595
  console.log(
10386
- " " + import_chalk9.default.green(`\u2705 ${num(allowed)} allowed`) + " " + blockLabel + " " + dlpLabel + " " + loopLabel + " " + trendLabel + " " + costLabel
10596
+ " " + import_chalk9.default.green(`\u2705 ${num(allowed)} allowed`) + " " + blockLabel + " " + dlpLabel + " " + loopLabel + " " + trendLabel
10387
10597
  );
10388
10598
  console.log(" " + ratioLabel + " " + testLabel);
10389
10599
  console.log("");
@@ -10468,6 +10678,30 @@ function registerReportCommand(program2) {
10468
10678
  );
10469
10679
  }
10470
10680
  }
10681
+ if (costUSD > 0) {
10682
+ const periodDays = Math.max(1, Math.ceil((end.getTime() - start.getTime()) / 864e5));
10683
+ const avgPerDay = costUSD / periodDays;
10684
+ const cacheHitPct = costInputTokens + costCacheRead > 0 ? Math.round(costCacheRead / (costInputTokens + costCacheRead) * 100) : 0;
10685
+ const costHeaderRight = [
10686
+ import_chalk9.default.yellow(fmtCost(costUSD)),
10687
+ import_chalk9.default.dim(`avg ${fmtCost(avgPerDay)}/day`),
10688
+ cacheHitPct > 0 ? import_chalk9.default.dim(`${cacheHitPct}% cache hit`) : null
10689
+ ].filter(Boolean).join(import_chalk9.default.dim(" \xB7 "));
10690
+ console.log("");
10691
+ console.log(" " + import_chalk9.default.bold("Cost") + " " + costHeaderRight);
10692
+ console.log(" " + import_chalk9.default.dim("\u2500".repeat(Math.min(50, W - 4))));
10693
+ const modelList = [...costByModel.entries()].sort((a, b) => b[1] - a[1]);
10694
+ const maxModelCost = Math.max(...modelList.map(([, v]) => v), 1e-9);
10695
+ const MODEL_LABEL = 22;
10696
+ const MODEL_BAR = Math.max(6, Math.min(20, W - MODEL_LABEL - 12));
10697
+ for (const [model, cost] of modelList) {
10698
+ const label = model.length > MODEL_LABEL - 1 ? model.slice(0, MODEL_LABEL - 2) + "\u2026" : model;
10699
+ const b = colorBar(cost, maxModelCost, MODEL_BAR);
10700
+ console.log(
10701
+ " " + import_chalk9.default.white(label.padEnd(MODEL_LABEL)) + b + " " + import_chalk9.default.yellow(fmtCost(cost))
10702
+ );
10703
+ }
10704
+ }
10471
10705
  console.log("");
10472
10706
  console.log(
10473
10707
  " " + import_chalk9.default.dim("node9 audit --deny") + import_chalk9.default.dim(" \xB7 ") + import_chalk9.default.dim("node9 report --period today|7d|30d|month --no-tests")
@@ -10542,14 +10776,14 @@ function registerDaemonCommand(program2) {
10542
10776
 
10543
10777
  // src/cli/commands/status.ts
10544
10778
  var import_chalk11 = __toESM(require("chalk"));
10545
- var import_fs24 = __toESM(require("fs"));
10546
- var import_path26 = __toESM(require("path"));
10547
- var import_os20 = __toESM(require("os"));
10779
+ var import_fs25 = __toESM(require("fs"));
10780
+ var import_path27 = __toESM(require("path"));
10781
+ var import_os21 = __toESM(require("os"));
10548
10782
  init_core();
10549
10783
  init_daemon();
10550
10784
  function readJson2(filePath) {
10551
10785
  try {
10552
- if (import_fs24.default.existsSync(filePath)) return JSON.parse(import_fs24.default.readFileSync(filePath, "utf-8"));
10786
+ if (import_fs25.default.existsSync(filePath)) return JSON.parse(import_fs25.default.readFileSync(filePath, "utf-8"));
10553
10787
  } catch {
10554
10788
  }
10555
10789
  return null;
@@ -10614,28 +10848,28 @@ function registerStatusCommand(program2) {
10614
10848
  console.log("");
10615
10849
  const modeLabel = settings.mode === "audit" ? import_chalk11.default.blue("audit") : settings.mode === "strict" ? import_chalk11.default.red("strict") : import_chalk11.default.white("standard");
10616
10850
  console.log(` Mode: ${modeLabel}`);
10617
- const projectConfig = import_path26.default.join(process.cwd(), "node9.config.json");
10618
- const globalConfig = import_path26.default.join(import_os20.default.homedir(), ".node9", "config.json");
10851
+ const projectConfig = import_path27.default.join(process.cwd(), "node9.config.json");
10852
+ const globalConfig = import_path27.default.join(import_os21.default.homedir(), ".node9", "config.json");
10619
10853
  console.log(
10620
- ` Local: ${import_fs24.default.existsSync(projectConfig) ? import_chalk11.default.green("Active (node9.config.json)") : import_chalk11.default.gray("Not present")}`
10854
+ ` Local: ${import_fs25.default.existsSync(projectConfig) ? import_chalk11.default.green("Active (node9.config.json)") : import_chalk11.default.gray("Not present")}`
10621
10855
  );
10622
10856
  console.log(
10623
- ` Global: ${import_fs24.default.existsSync(globalConfig) ? import_chalk11.default.green("Active (~/.node9/config.json)") : import_chalk11.default.gray("Not present")}`
10857
+ ` Global: ${import_fs25.default.existsSync(globalConfig) ? import_chalk11.default.green("Active (~/.node9/config.json)") : import_chalk11.default.gray("Not present")}`
10624
10858
  );
10625
10859
  if (mergedConfig.policy.sandboxPaths.length > 0) {
10626
10860
  console.log(
10627
10861
  ` Sandbox: ${import_chalk11.default.green(`${mergedConfig.policy.sandboxPaths.length} safe zones active`)}`
10628
10862
  );
10629
10863
  }
10630
- const homeDir2 = import_os20.default.homedir();
10864
+ const homeDir2 = import_os21.default.homedir();
10631
10865
  const claudeSettings = readJson2(
10632
- import_path26.default.join(homeDir2, ".claude", "settings.json")
10866
+ import_path27.default.join(homeDir2, ".claude", "settings.json")
10633
10867
  );
10634
- const claudeConfig = readJson2(import_path26.default.join(homeDir2, ".claude.json"));
10868
+ const claudeConfig = readJson2(import_path27.default.join(homeDir2, ".claude.json"));
10635
10869
  const geminiSettings = readJson2(
10636
- import_path26.default.join(homeDir2, ".gemini", "settings.json")
10870
+ import_path27.default.join(homeDir2, ".gemini", "settings.json")
10637
10871
  );
10638
- const cursorConfig = readJson2(import_path26.default.join(homeDir2, ".cursor", "mcp.json"));
10872
+ const cursorConfig = readJson2(import_path27.default.join(homeDir2, ".cursor", "mcp.json"));
10639
10873
  const agentFound = claudeSettings || claudeConfig || geminiSettings || cursorConfig;
10640
10874
  if (agentFound) {
10641
10875
  console.log("");
@@ -10694,9 +10928,9 @@ function registerStatusCommand(program2) {
10694
10928
 
10695
10929
  // src/cli/commands/init.ts
10696
10930
  var import_chalk12 = __toESM(require("chalk"));
10697
- var import_fs25 = __toESM(require("fs"));
10698
- var import_path27 = __toESM(require("path"));
10699
- var import_os21 = __toESM(require("os"));
10931
+ var import_fs26 = __toESM(require("fs"));
10932
+ var import_path28 = __toESM(require("path"));
10933
+ var import_os22 = __toESM(require("os"));
10700
10934
  var import_https2 = __toESM(require("https"));
10701
10935
  init_core();
10702
10936
  init_shields();
@@ -10756,15 +10990,15 @@ function registerInitCommand(program2) {
10756
10990
  }
10757
10991
  console.log("");
10758
10992
  }
10759
- const configPath = import_path27.default.join(import_os21.default.homedir(), ".node9", "config.json");
10760
- if (import_fs25.default.existsSync(configPath) && !options.force) {
10993
+ const configPath = import_path28.default.join(import_os22.default.homedir(), ".node9", "config.json");
10994
+ if (import_fs26.default.existsSync(configPath) && !options.force) {
10761
10995
  try {
10762
- const existing = JSON.parse(import_fs25.default.readFileSync(configPath, "utf-8"));
10996
+ const existing = JSON.parse(import_fs26.default.readFileSync(configPath, "utf-8"));
10763
10997
  const settings = existing.settings ?? {};
10764
10998
  if (settings.mode !== chosenMode) {
10765
10999
  settings.mode = chosenMode;
10766
11000
  existing.settings = settings;
10767
- import_fs25.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
11001
+ import_fs26.default.writeFileSync(configPath, JSON.stringify(existing, null, 2) + "\n");
10768
11002
  console.log(import_chalk12.default.green(`\u2705 Mode updated: ${chosenMode}`));
10769
11003
  } else {
10770
11004
  console.log(import_chalk12.default.blue(`\u2139\uFE0F Config already exists: ${configPath}`));
@@ -10777,9 +11011,9 @@ function registerInitCommand(program2) {
10777
11011
  ...DEFAULT_CONFIG,
10778
11012
  settings: { ...DEFAULT_CONFIG.settings, mode: chosenMode }
10779
11013
  };
10780
- const dir = import_path27.default.dirname(configPath);
10781
- if (!import_fs25.default.existsSync(dir)) import_fs25.default.mkdirSync(dir, { recursive: true });
10782
- import_fs25.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
11014
+ const dir = import_path28.default.dirname(configPath);
11015
+ if (!import_fs26.default.existsSync(dir)) import_fs26.default.mkdirSync(dir, { recursive: true });
11016
+ import_fs26.default.writeFileSync(configPath, JSON.stringify(configToSave, null, 2) + "\n");
10783
11017
  console.log(import_chalk12.default.green(`\u2705 Config created: ${configPath}`));
10784
11018
  console.log(import_chalk12.default.gray(` Mode: ${chosenMode}`));
10785
11019
  }
@@ -10833,7 +11067,7 @@ function registerInitCommand(program2) {
10833
11067
  }
10834
11068
 
10835
11069
  // src/cli/commands/undo.ts
10836
- var import_path28 = __toESM(require("path"));
11070
+ var import_path29 = __toESM(require("path"));
10837
11071
  var import_chalk14 = __toESM(require("chalk"));
10838
11072
 
10839
11073
  // src/tui/undo-navigator.ts
@@ -10992,7 +11226,7 @@ function findMatchingCwd(startDir, history) {
10992
11226
  let dir = startDir;
10993
11227
  while (true) {
10994
11228
  if (cwds.has(dir)) return dir;
10995
- const parent = import_path28.default.dirname(dir);
11229
+ const parent = import_path29.default.dirname(dir);
10996
11230
  if (parent === dir) return null;
10997
11231
  dir = parent;
10998
11232
  }
@@ -11188,12 +11422,12 @@ init_orchestrator();
11188
11422
  init_provenance();
11189
11423
 
11190
11424
  // src/mcp-pin.ts
11191
- var import_fs26 = __toESM(require("fs"));
11192
- var import_path29 = __toESM(require("path"));
11193
- var import_os22 = __toESM(require("os"));
11425
+ var import_fs27 = __toESM(require("fs"));
11426
+ var import_path30 = __toESM(require("path"));
11427
+ var import_os23 = __toESM(require("os"));
11194
11428
  var import_crypto9 = __toESM(require("crypto"));
11195
11429
  function getPinsFilePath() {
11196
- return import_path29.default.join(import_os22.default.homedir(), ".node9", "mcp-pins.json");
11430
+ return import_path30.default.join(import_os23.default.homedir(), ".node9", "mcp-pins.json");
11197
11431
  }
11198
11432
  function hashToolDefinitions(tools) {
11199
11433
  const sorted = [...tools].sort((a, b) => {
@@ -11210,7 +11444,7 @@ function getServerKey(upstreamCommand) {
11210
11444
  function readMcpPinsSafe() {
11211
11445
  const filePath = getPinsFilePath();
11212
11446
  try {
11213
- const raw = import_fs26.default.readFileSync(filePath, "utf-8");
11447
+ const raw = import_fs27.default.readFileSync(filePath, "utf-8");
11214
11448
  if (!raw.trim()) {
11215
11449
  return { ok: false, reason: "corrupt", detail: "empty file" };
11216
11450
  }
@@ -11234,10 +11468,10 @@ function readMcpPins() {
11234
11468
  }
11235
11469
  function writeMcpPins(data) {
11236
11470
  const filePath = getPinsFilePath();
11237
- import_fs26.default.mkdirSync(import_path29.default.dirname(filePath), { recursive: true });
11471
+ import_fs27.default.mkdirSync(import_path30.default.dirname(filePath), { recursive: true });
11238
11472
  const tmp = `${filePath}.${import_crypto9.default.randomBytes(6).toString("hex")}.tmp`;
11239
- import_fs26.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
11240
- import_fs26.default.renameSync(tmp, filePath);
11473
+ import_fs27.default.writeFileSync(tmp, JSON.stringify(data, null, 2), { mode: 384 });
11474
+ import_fs27.default.renameSync(tmp, filePath);
11241
11475
  }
11242
11476
  function checkPin(serverKey, currentHash) {
11243
11477
  const result = readMcpPinsSafe();
@@ -11609,9 +11843,9 @@ function registerMcpGatewayCommand(program2) {
11609
11843
 
11610
11844
  // src/mcp-server/index.ts
11611
11845
  var import_readline4 = __toESM(require("readline"));
11612
- var import_fs27 = __toESM(require("fs"));
11613
- var import_os23 = __toESM(require("os"));
11614
- var import_path30 = __toESM(require("path"));
11846
+ var import_fs28 = __toESM(require("fs"));
11847
+ var import_os24 = __toESM(require("os"));
11848
+ var import_path31 = __toESM(require("path"));
11615
11849
  init_core();
11616
11850
  init_daemon();
11617
11851
  init_shields();
@@ -11786,13 +12020,13 @@ function handleStatus() {
11786
12020
  lines.push(`Active shields: ${activeShields.length > 0 ? activeShields.join(", ") : "none"}`);
11787
12021
  lines.push(`Smart rules: ${config.policy.smartRules.length} loaded`);
11788
12022
  lines.push(`DLP: ${config.policy.dlp?.enabled !== false ? "enabled" : "disabled"}`);
11789
- const projectConfig = import_path30.default.join(process.cwd(), "node9.config.json");
11790
- const globalConfig = import_path30.default.join(import_os23.default.homedir(), ".node9", "config.json");
12023
+ const projectConfig = import_path31.default.join(process.cwd(), "node9.config.json");
12024
+ const globalConfig = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
11791
12025
  lines.push(
11792
- `Project config (node9.config.json): ${import_fs27.default.existsSync(projectConfig) ? "present" : "not found"}`
12026
+ `Project config (node9.config.json): ${import_fs28.default.existsSync(projectConfig) ? "present" : "not found"}`
11793
12027
  );
11794
12028
  lines.push(
11795
- `Global config (~/.node9/config.json): ${import_fs27.default.existsSync(globalConfig) ? "present" : "not found"}`
12029
+ `Global config (~/.node9/config.json): ${import_fs28.default.existsSync(globalConfig) ? "present" : "not found"}`
11796
12030
  );
11797
12031
  return lines.join("\n");
11798
12032
  }
@@ -11866,21 +12100,21 @@ function handleShieldDisable(args) {
11866
12100
  writeActiveShields(active.filter((s) => s !== name));
11867
12101
  return `Shield "${name}" disabled.`;
11868
12102
  }
11869
- var GLOBAL_CONFIG_PATH2 = import_path30.default.join(import_os23.default.homedir(), ".node9", "config.json");
12103
+ var GLOBAL_CONFIG_PATH2 = import_path31.default.join(import_os24.default.homedir(), ".node9", "config.json");
11870
12104
  var APPROVER_CHANNELS = ["native", "browser", "cloud", "terminal"];
11871
12105
  function readGlobalConfigRaw() {
11872
12106
  try {
11873
- if (import_fs27.default.existsSync(GLOBAL_CONFIG_PATH2)) {
11874
- return JSON.parse(import_fs27.default.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
12107
+ if (import_fs28.default.existsSync(GLOBAL_CONFIG_PATH2)) {
12108
+ return JSON.parse(import_fs28.default.readFileSync(GLOBAL_CONFIG_PATH2, "utf-8"));
11875
12109
  }
11876
12110
  } catch {
11877
12111
  }
11878
12112
  return {};
11879
12113
  }
11880
12114
  function writeGlobalConfigRaw(data) {
11881
- const dir = import_path30.default.dirname(GLOBAL_CONFIG_PATH2);
11882
- if (!import_fs27.default.existsSync(dir)) import_fs27.default.mkdirSync(dir, { recursive: true });
11883
- import_fs27.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
12115
+ const dir = import_path31.default.dirname(GLOBAL_CONFIG_PATH2);
12116
+ if (!import_fs28.default.existsSync(dir)) import_fs28.default.mkdirSync(dir, { recursive: true });
12117
+ import_fs28.default.writeFileSync(GLOBAL_CONFIG_PATH2, JSON.stringify(data, null, 2) + "\n");
11884
12118
  }
11885
12119
  function handleApproverList() {
11886
12120
  const config = getConfig();
@@ -11923,9 +12157,9 @@ function handleApproverSet(args) {
11923
12157
  }
11924
12158
  function handleAuditGet(args) {
11925
12159
  const limit = Math.min(typeof args.limit === "number" ? args.limit : 20, 100);
11926
- const auditPath = import_path30.default.join(import_os23.default.homedir(), ".node9", "audit.log");
11927
- if (!import_fs27.default.existsSync(auditPath)) return "No audit log found.";
11928
- const lines = import_fs27.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
12160
+ const auditPath = import_path31.default.join(import_os24.default.homedir(), ".node9", "audit.log");
12161
+ if (!import_fs28.default.existsSync(auditPath)) return "No audit log found.";
12162
+ const lines = import_fs28.default.readFileSync(auditPath, "utf-8").trim().split("\n").filter(Boolean);
11929
12163
  const recent = lines.slice(-limit);
11930
12164
  const entries = recent.map((line) => {
11931
12165
  try {
@@ -12245,20 +12479,20 @@ function registerMcpPinCommand(program2) {
12245
12479
 
12246
12480
  // src/cli.ts
12247
12481
  var { version } = JSON.parse(
12248
- import_fs30.default.readFileSync(import_path33.default.join(__dirname, "../package.json"), "utf-8")
12482
+ import_fs31.default.readFileSync(import_path34.default.join(__dirname, "../package.json"), "utf-8")
12249
12483
  );
12250
12484
  var program = new import_commander.Command();
12251
12485
  program.name("node9").description("The Sudo Command for AI Agents").version(version);
12252
12486
  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) => {
12253
12487
  const DEFAULT_API_URL = "https://api.node9.ai/api/v1/intercept";
12254
- const credPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "credentials.json");
12255
- if (!import_fs30.default.existsSync(import_path33.default.dirname(credPath)))
12256
- import_fs30.default.mkdirSync(import_path33.default.dirname(credPath), { recursive: true });
12488
+ const credPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "credentials.json");
12489
+ if (!import_fs31.default.existsSync(import_path34.default.dirname(credPath)))
12490
+ import_fs31.default.mkdirSync(import_path34.default.dirname(credPath), { recursive: true });
12257
12491
  const profileName = options.profile || "default";
12258
12492
  let existingCreds = {};
12259
12493
  try {
12260
- if (import_fs30.default.existsSync(credPath)) {
12261
- const raw = JSON.parse(import_fs30.default.readFileSync(credPath, "utf-8"));
12494
+ if (import_fs31.default.existsSync(credPath)) {
12495
+ const raw = JSON.parse(import_fs31.default.readFileSync(credPath, "utf-8"));
12262
12496
  if (raw.apiKey) {
12263
12497
  existingCreds = {
12264
12498
  default: { apiKey: raw.apiKey, apiUrl: raw.apiUrl || DEFAULT_API_URL }
@@ -12270,13 +12504,13 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
12270
12504
  } catch {
12271
12505
  }
12272
12506
  existingCreds[profileName] = { apiKey, apiUrl: DEFAULT_API_URL };
12273
- import_fs30.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
12507
+ import_fs31.default.writeFileSync(credPath, JSON.stringify(existingCreds, null, 2), { mode: 384 });
12274
12508
  if (profileName === "default") {
12275
- const configPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "config.json");
12509
+ const configPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "config.json");
12276
12510
  let config = {};
12277
12511
  try {
12278
- if (import_fs30.default.existsSync(configPath))
12279
- config = JSON.parse(import_fs30.default.readFileSync(configPath, "utf-8"));
12512
+ if (import_fs31.default.existsSync(configPath))
12513
+ config = JSON.parse(import_fs31.default.readFileSync(configPath, "utf-8"));
12280
12514
  } catch {
12281
12515
  }
12282
12516
  if (!config.settings || typeof config.settings !== "object") config.settings = {};
@@ -12291,9 +12525,9 @@ program.command("login").argument("<apiKey>").option("--local", "Save key for au
12291
12525
  approvers.cloud = false;
12292
12526
  }
12293
12527
  s.approvers = approvers;
12294
- if (!import_fs30.default.existsSync(import_path33.default.dirname(configPath)))
12295
- import_fs30.default.mkdirSync(import_path33.default.dirname(configPath), { recursive: true });
12296
- import_fs30.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
12528
+ if (!import_fs31.default.existsSync(import_path34.default.dirname(configPath)))
12529
+ import_fs31.default.mkdirSync(import_path34.default.dirname(configPath), { recursive: true });
12530
+ import_fs31.default.writeFileSync(configPath, JSON.stringify(config, null, 2), { mode: 384 });
12297
12531
  }
12298
12532
  if (options.profile && profileName !== "default") {
12299
12533
  console.log(import_chalk20.default.green(`\u2705 Profile "${profileName}" saved`));
@@ -12387,15 +12621,15 @@ program.command("uninstall").description("Remove all Node9 hooks and optionally
12387
12621
  }
12388
12622
  }
12389
12623
  if (options.purge) {
12390
- const node9Dir = import_path33.default.join(import_os26.default.homedir(), ".node9");
12391
- if (import_fs30.default.existsSync(node9Dir)) {
12624
+ const node9Dir = import_path34.default.join(import_os27.default.homedir(), ".node9");
12625
+ if (import_fs31.default.existsSync(node9Dir)) {
12392
12626
  const confirmed = await (0, import_prompts2.confirm)({
12393
12627
  message: `Permanently delete ${node9Dir} (config, audit log, credentials)?`,
12394
12628
  default: false
12395
12629
  });
12396
12630
  if (confirmed) {
12397
- import_fs30.default.rmSync(node9Dir, { recursive: true });
12398
- if (import_fs30.default.existsSync(node9Dir)) {
12631
+ import_fs31.default.rmSync(node9Dir, { recursive: true });
12632
+ if (import_fs31.default.existsSync(node9Dir)) {
12399
12633
  console.error(
12400
12634
  import_chalk20.default.red("\n \u26A0\uFE0F ~/.node9/ could not be fully deleted \u2014 remove it manually.")
12401
12635
  );
@@ -12536,14 +12770,14 @@ Claude Code spawns this command every ~300ms and writes a JSON payload to stdin.
12536
12770
  Run "node9 addto claude" to register it as the statusLine.`
12537
12771
  ).argument("[subcommand]", 'Optional: "debug on" / "debug off" to toggle stdin logging').argument("[state]", 'on|off \u2014 used with "debug" subcommand').action(async (subcommand, state) => {
12538
12772
  if (subcommand === "debug") {
12539
- const flagFile = import_path33.default.join(import_os26.default.homedir(), ".node9", "hud-debug");
12773
+ const flagFile = import_path34.default.join(import_os27.default.homedir(), ".node9", "hud-debug");
12540
12774
  if (state === "on") {
12541
- import_fs30.default.mkdirSync(import_path33.default.dirname(flagFile), { recursive: true });
12542
- import_fs30.default.writeFileSync(flagFile, "");
12775
+ import_fs31.default.mkdirSync(import_path34.default.dirname(flagFile), { recursive: true });
12776
+ import_fs31.default.writeFileSync(flagFile, "");
12543
12777
  console.log("HUD debug logging enabled \u2192 ~/.node9/hud-debug.log");
12544
12778
  console.log("Tail it with: tail -f ~/.node9/hud-debug.log");
12545
12779
  } else if (state === "off") {
12546
- if (import_fs30.default.existsSync(flagFile)) import_fs30.default.unlinkSync(flagFile);
12780
+ if (import_fs31.default.existsSync(flagFile)) import_fs31.default.unlinkSync(flagFile);
12547
12781
  console.log("HUD debug logging disabled.");
12548
12782
  } else {
12549
12783
  console.error("Usage: node9 hud debug on|off");
@@ -12650,9 +12884,9 @@ if (process.argv[2] !== "daemon") {
12650
12884
  const isCheckHook = process.argv[2] === "check";
12651
12885
  if (isCheckHook) {
12652
12886
  if (process.env.NODE9_DEBUG === "1" || getConfig().settings.enableHookLogDebug) {
12653
- const logPath = import_path33.default.join(import_os26.default.homedir(), ".node9", "hook-debug.log");
12887
+ const logPath = import_path34.default.join(import_os27.default.homedir(), ".node9", "hook-debug.log");
12654
12888
  const msg = reason instanceof Error ? reason.message : String(reason);
12655
- import_fs30.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
12889
+ import_fs31.default.appendFileSync(logPath, `[${(/* @__PURE__ */ new Date()).toISOString()}] UNHANDLED: ${msg}
12656
12890
  `);
12657
12891
  }
12658
12892
  process.exit(0);