@great-arrow/cli 0.1.47 → 0.1.49

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/index.js CHANGED
@@ -104,7 +104,7 @@ var require_polyfills = __commonJS({
104
104
  }
105
105
  if (platform === "win32") {
106
106
  fs6.rename = typeof fs6.rename !== "function" ? fs6.rename : (function(fs$rename) {
107
- function rename(from, to, cb) {
107
+ function rename2(from, to, cb) {
108
108
  var start = Date.now();
109
109
  var backoff = 0;
110
110
  fs$rename(from, to, function CB(er) {
@@ -124,8 +124,8 @@ var require_polyfills = __commonJS({
124
124
  if (cb) cb(er);
125
125
  });
126
126
  }
127
- if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename);
128
- return rename;
127
+ if (Object.setPrototypeOf) Object.setPrototypeOf(rename2, fs$rename);
128
+ return rename2;
129
129
  })(fs6.rename);
130
130
  }
131
131
  fs6.read = typeof fs6.read !== "function" ? fs6.read : (function(fs$read) {
@@ -529,8 +529,8 @@ var require_graceful_fs = __commonJS({
529
529
  fs7.createReadStream = createReadStream;
530
530
  fs7.createWriteStream = createWriteStream;
531
531
  var fs$readFile = fs7.readFile;
532
- fs7.readFile = readFile5;
533
- function readFile5(path10, options, cb) {
532
+ fs7.readFile = readFile7;
533
+ function readFile7(path10, options, cb) {
534
534
  if (typeof options === "function")
535
535
  cb = options, options = null;
536
536
  return go$readFile(path10, options, cb);
@@ -546,8 +546,8 @@ var require_graceful_fs = __commonJS({
546
546
  }
547
547
  }
548
548
  var fs$writeFile = fs7.writeFile;
549
- fs7.writeFile = writeFile5;
550
- function writeFile5(path10, data, options, cb) {
549
+ fs7.writeFile = writeFile6;
550
+ function writeFile6(path10, data, options, cb) {
551
551
  if (typeof options === "function")
552
552
  cb = options, options = null;
553
553
  return go$writeFile(path10, data, options, cb);
@@ -582,8 +582,8 @@ var require_graceful_fs = __commonJS({
582
582
  }
583
583
  var fs$copyFile = fs7.copyFile;
584
584
  if (fs$copyFile)
585
- fs7.copyFile = copyFile2;
586
- function copyFile2(src, dest, flags, cb) {
585
+ fs7.copyFile = copyFile3;
586
+ function copyFile3(src, dest, flags, cb) {
587
587
  if (typeof flags === "function") {
588
588
  cb = flags;
589
589
  flags = 0;
@@ -1964,7 +1964,7 @@ var require_minimist = __commonJS({
1964
1964
  var require_rc = __commonJS({
1965
1965
  "node_modules/.pnpm/rc@1.2.8/node_modules/rc/index.js"(exports, module) {
1966
1966
  var cc = require_utils();
1967
- var join9 = __require("path").join;
1967
+ var join10 = __require("path").join;
1968
1968
  var deepExtend = require_deep_extend();
1969
1969
  var etc = "/etc";
1970
1970
  var win = process.platform === "win32";
@@ -1989,15 +1989,15 @@ var require_rc = __commonJS({
1989
1989
  }
1990
1990
  if (!win)
1991
1991
  [
1992
- join9(etc, name, "config"),
1993
- join9(etc, name + "rc")
1992
+ join10(etc, name, "config"),
1993
+ join10(etc, name + "rc")
1994
1994
  ].forEach(addConfigFile);
1995
1995
  if (home)
1996
1996
  [
1997
- join9(home, ".config", name, "config"),
1998
- join9(home, ".config", name),
1999
- join9(home, "." + name, "config"),
2000
- join9(home, "." + name + "rc")
1997
+ join10(home, ".config", name, "config"),
1998
+ join10(home, ".config", name),
1999
+ join10(home, "." + name, "config"),
2000
+ join10(home, "." + name + "rc")
2001
2001
  ].forEach(addConfigFile);
2002
2002
  addConfigFile(cc.find("." + name + "rc"));
2003
2003
  if (env4.config) addConfigFile(env4.config);
@@ -2078,7 +2078,7 @@ var require_polyfills2 = __commonJS({
2078
2078
  }
2079
2079
  if (platform === "win32") {
2080
2080
  fs6.rename = typeof fs6.rename !== "function" ? fs6.rename : (function(fs$rename) {
2081
- function rename(from, to, cb) {
2081
+ function rename2(from, to, cb) {
2082
2082
  var start = Date.now();
2083
2083
  var backoff = 0;
2084
2084
  fs$rename(from, to, function CB(er) {
@@ -2098,8 +2098,8 @@ var require_polyfills2 = __commonJS({
2098
2098
  if (cb) cb(er);
2099
2099
  });
2100
2100
  }
2101
- if (Object.setPrototypeOf) Object.setPrototypeOf(rename, fs$rename);
2102
- return rename;
2101
+ if (Object.setPrototypeOf) Object.setPrototypeOf(rename2, fs$rename);
2102
+ return rename2;
2103
2103
  })(fs6.rename);
2104
2104
  }
2105
2105
  fs6.read = typeof fs6.read !== "function" ? fs6.read : (function(fs$read) {
@@ -2503,8 +2503,8 @@ var require_graceful_fs2 = __commonJS({
2503
2503
  fs7.createReadStream = createReadStream;
2504
2504
  fs7.createWriteStream = createWriteStream;
2505
2505
  var fs$readFile = fs7.readFile;
2506
- fs7.readFile = readFile5;
2507
- function readFile5(path10, options, cb) {
2506
+ fs7.readFile = readFile7;
2507
+ function readFile7(path10, options, cb) {
2508
2508
  if (typeof options === "function")
2509
2509
  cb = options, options = null;
2510
2510
  return go$readFile(path10, options, cb);
@@ -2520,8 +2520,8 @@ var require_graceful_fs2 = __commonJS({
2520
2520
  }
2521
2521
  }
2522
2522
  var fs$writeFile = fs7.writeFile;
2523
- fs7.writeFile = writeFile5;
2524
- function writeFile5(path10, data, options, cb) {
2523
+ fs7.writeFile = writeFile6;
2524
+ function writeFile6(path10, data, options, cb) {
2525
2525
  if (typeof options === "function")
2526
2526
  cb = options, options = null;
2527
2527
  return go$writeFile(path10, data, options, cb);
@@ -2556,8 +2556,8 @@ var require_graceful_fs2 = __commonJS({
2556
2556
  }
2557
2557
  var fs$copyFile = fs7.copyFile;
2558
2558
  if (fs$copyFile)
2559
- fs7.copyFile = copyFile2;
2560
- function copyFile2(src, dest, flags, cb) {
2559
+ fs7.copyFile = copyFile3;
2560
+ function copyFile3(src, dest, flags, cb) {
2561
2561
  if (typeof flags === "function") {
2562
2562
  cb = flags;
2563
2563
  flags = 0;
@@ -3335,11 +3335,11 @@ var require_util = __commonJS({
3335
3335
  if (files.includes("node_modules") || files.includes("package.json") || files.includes("package.json5") || files.includes("package.yaml") || files.includes("pnpm-workspace.yaml")) {
3336
3336
  return name2;
3337
3337
  }
3338
- const dirname6 = path10.dirname(name2);
3339
- if (dirname6 === name2) {
3338
+ const dirname7 = path10.dirname(name2);
3339
+ if (dirname7 === name2) {
3340
3340
  return original;
3341
3341
  }
3342
- return find(dirname6, original);
3342
+ return find(dirname7, original);
3343
3343
  } catch (error) {
3344
3344
  if (name2 === original) {
3345
3345
  if (error.code === "ENOENT") {
@@ -9399,7 +9399,7 @@ function isNodeError(value) {
9399
9399
 
9400
9400
  // src/lib/version.ts
9401
9401
  var CLI_PACKAGE_NAME = "@great-arrow/cli";
9402
- var CLI_VERSION = "0.1.47";
9402
+ var CLI_VERSION = "0.1.49";
9403
9403
  var GAD_INSTALL_VERSION = "2026.08.08";
9404
9404
  var GAD_INSTALL_MIN_VERSION = "2026.06.22";
9405
9405
  var GAD_INSTALL_HEADER = "X-GAD-Install-Version";
@@ -9865,6 +9865,10 @@ function trimSlash(url) {
9865
9865
  }
9866
9866
 
9867
9867
  // src/lib/oauth/token-exchange.ts
9868
+ var TOKEN_HEADERS = {
9869
+ "content-type": "application/x-www-form-urlencoded",
9870
+ "user-agent": `gad-cli/${CLI_VERSION}`
9871
+ };
9868
9872
  var RefreshRejected = class extends Error {
9869
9873
  constructor(message) {
9870
9874
  super(message);
@@ -9898,7 +9902,7 @@ var TOKEN_POST_TIMEOUT_MS = 8e3;
9898
9902
  async function postToken(apiUrl, form, fetchImpl, timeoutMs = TOKEN_POST_TIMEOUT_MS) {
9899
9903
  const res = await fetchImpl(`${trimSlash2(apiUrl)}/api/oauth/token`, {
9900
9904
  method: "POST",
9901
- headers: { "content-type": "application/x-www-form-urlencoded" },
9905
+ headers: TOKEN_HEADERS,
9902
9906
  body: new URLSearchParams(form).toString(),
9903
9907
  signal: AbortSignal.timeout(timeoutMs)
9904
9908
  });
@@ -9940,7 +9944,7 @@ async function revokeToken(opts) {
9940
9944
  try {
9941
9945
  const res = await doFetch(`${trimSlash2(opts.apiUrl)}/api/oauth/revoke`, {
9942
9946
  method: "POST",
9943
- headers: { "content-type": "application/x-www-form-urlencoded" },
9947
+ headers: TOKEN_HEADERS,
9944
9948
  body: new URLSearchParams(form).toString(),
9945
9949
  signal: AbortSignal.timeout(TOKEN_POST_TIMEOUT_MS)
9946
9950
  });
@@ -10457,6 +10461,14 @@ async function resolveAuth() {
10457
10461
  tokenType: "personal"
10458
10462
  });
10459
10463
  }
10464
+ const codexEnvToken = process.env.GREAT_ARROW_TOKEN;
10465
+ if (codexEnvToken) {
10466
+ return withWorkspace({
10467
+ token: codexEnvToken,
10468
+ apiUrl: configApiUrl,
10469
+ tokenType: "personal"
10470
+ });
10471
+ }
10460
10472
  throw authError(
10461
10473
  "No authentication token found.",
10462
10474
  "Run 'gad login' to authenticate, or set the GAD_TOKEN environment variable."
@@ -10751,6 +10763,46 @@ function isRetryable(error) {
10751
10763
  }
10752
10764
  return false;
10753
10765
  }
10766
+ var RETRY_SAFE_TOOLS = /* @__PURE__ */ new Set([
10767
+ "workspace_introspect",
10768
+ "mcp_capabilities",
10769
+ "tools_discover",
10770
+ "memory_search",
10771
+ "memory_list",
10772
+ "memory_get",
10773
+ "memory_tags",
10774
+ "todo_list",
10775
+ "todo_get",
10776
+ "workspace_list",
10777
+ "workspace_settings_get",
10778
+ "surfaces_list",
10779
+ "agent_list",
10780
+ "engineering_guardrails_list",
10781
+ "model_local_get",
10782
+ "glance",
10783
+ "did_you_know"
10784
+ ]);
10785
+ var CONNECTION_PHASE_CODES = /* @__PURE__ */ new Set([
10786
+ "ECONNREFUSED",
10787
+ "ENOTFOUND",
10788
+ "EAI_AGAIN",
10789
+ "ENETUNREACH",
10790
+ "EHOSTUNREACH",
10791
+ "UND_ERR_CONNECT_TIMEOUT"
10792
+ ]);
10793
+ function hasConnectionPhaseCode(value) {
10794
+ if (!value || typeof value !== "object") return false;
10795
+ const errors = value.errors;
10796
+ if (Array.isArray(errors) && errors.length > 0) {
10797
+ return errors.every(hasConnectionPhaseCode);
10798
+ }
10799
+ const code = value.code;
10800
+ return typeof code === "string" && CONNECTION_PHASE_CODES.has(code);
10801
+ }
10802
+ function isConnectionPhaseFailure(error) {
10803
+ if (!(error instanceof TypeError)) return false;
10804
+ return hasConnectionPhaseCode(error.cause);
10805
+ }
10754
10806
  function isRetryableStatus(status) {
10755
10807
  return status >= 500 && status < 600;
10756
10808
  }
@@ -10806,7 +10858,7 @@ var GadClient = class {
10806
10858
  /**
10807
10859
  * Execute a fetch request with timeout and retry logic.
10808
10860
  */
10809
- async fetchWithRetry(url, init, timeoutMs) {
10861
+ async fetchWithRetry(url, init, timeoutMs, policy = "transient") {
10810
10862
  const effectiveTimeout = timeoutMs ?? this.timeout;
10811
10863
  let lastError;
10812
10864
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
@@ -10818,7 +10870,7 @@ var GadClient = class {
10818
10870
  signal: controller.signal
10819
10871
  });
10820
10872
  clearTimeout(timer);
10821
- if (isRetryableStatus(response.status)) {
10873
+ if (policy === "transient" && isRetryableStatus(response.status)) {
10822
10874
  lastError = response;
10823
10875
  if (attempt < MAX_RETRIES - 1) {
10824
10876
  await sleep(retryDelay(attempt));
@@ -10830,7 +10882,8 @@ var GadClient = class {
10830
10882
  } catch (error) {
10831
10883
  clearTimeout(timer);
10832
10884
  lastError = error;
10833
- if (isRetryable(error) && attempt < MAX_RETRIES - 1) {
10885
+ const retryable = policy === "transient" ? isRetryable(error) : isConnectionPhaseFailure(error);
10886
+ if (retryable && attempt < MAX_RETRIES - 1) {
10834
10887
  await sleep(retryDelay(attempt));
10835
10888
  continue;
10836
10889
  }
@@ -10899,7 +10952,8 @@ var GadClient = class {
10899
10952
  headers: this.buildHeaders(),
10900
10953
  body
10901
10954
  },
10902
- timeoutMs
10955
+ timeoutMs,
10956
+ RETRY_SAFE_TOOLS.has(toolName) ? "transient" : "connection-only"
10903
10957
  );
10904
10958
  } catch (error) {
10905
10959
  if (error instanceof CliError) throw error;
@@ -10991,11 +11045,16 @@ var GadClient = class {
10991
11045
  const url = `${this.apiUrl}${path10}`;
10992
11046
  let response;
10993
11047
  try {
10994
- response = await this.fetchWithRetry(url, {
10995
- method: "POST",
10996
- headers: this.buildHeaders(),
10997
- body: JSON.stringify(body)
10998
- });
11048
+ response = await this.fetchWithRetry(
11049
+ url,
11050
+ {
11051
+ method: "POST",
11052
+ headers: this.buildHeaders(),
11053
+ body: JSON.stringify(body)
11054
+ },
11055
+ void 0,
11056
+ "connection-only"
11057
+ );
10999
11058
  } catch (error) {
11000
11059
  if (error instanceof CliError) throw error;
11001
11060
  void captureCliEvent({
@@ -12966,10 +13025,10 @@ function guardrailsCommand() {
12966
13025
  }
12967
13026
 
12968
13027
  // src/commands/doctor.ts
12969
- import { existsSync as existsSync3 } from "node:fs";
12970
- import { copyFile, mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
13028
+ import { existsSync as existsSync5 } from "node:fs";
13029
+ import { copyFile as copyFile2, mkdir as mkdir5, readFile as readFile5, writeFile as writeFile4 } from "node:fs/promises";
12971
13030
  import { homedir as homedir5 } from "node:os";
12972
- import { dirname as dirname4, join as join7 } from "node:path";
13031
+ import { dirname as dirname5, join as join8 } from "node:path";
12973
13032
 
12974
13033
  // src/lib/codex-config.ts
12975
13034
  import { execFileSync as execFileSync5 } from "node:child_process";
@@ -13756,6 +13815,285 @@ function inspectConfiguredHooks(hooks, deps) {
13756
13815
  return issues;
13757
13816
  }
13758
13817
 
13818
+ // src/lib/suggest-hook.ts
13819
+ import { existsSync as existsSync3 } from "node:fs";
13820
+ import {
13821
+ chmod,
13822
+ copyFile,
13823
+ mkdir as mkdir4,
13824
+ readFile as readFile3,
13825
+ rename,
13826
+ unlink as unlink2,
13827
+ writeFile as writeFile3
13828
+ } from "node:fs/promises";
13829
+ import { dirname as dirname4, join as join7 } from "node:path";
13830
+ import { fileURLToPath } from "node:url";
13831
+ var SUGGEST_HOOK_FILE = "gad-suggest-tools.mjs";
13832
+ var SUGGEST_HOOK_EVENT = "UserPromptSubmit";
13833
+ var SUGGEST_HOOK_DISCLOSURE = "Tool suggestions (optional, off by default): when on, every prompt you type in Claude Code is sent \u2014 its text only, up to 2,000 characters, never your session id, transcript or working directory \u2014 to greatarrow.ai over your own Great Arrow token, so it can name up to three Great Arrow tools that may help. The prompt is ranked and dropped: it is not stored and not logged. The hook gives up after 1.5 seconds and never blocks a prompt. Turn it off any time with `gad mcp suggest-tools off`.";
13834
+ function asObject2(value) {
13835
+ return value && typeof value === "object" && !Array.isArray(value) ? value : null;
13836
+ }
13837
+ function isOurs(hook) {
13838
+ return !!hook && typeof hook.command === "string" && hook.command.includes(SUGGEST_HOOK_FILE);
13839
+ }
13840
+ function parseSettings(content) {
13841
+ const parsed = content.trim() ? JSON.parse(content) : {};
13842
+ const cfg = asObject2(parsed);
13843
+ if (!cfg) throw new Error("settings.json is not a JSON object");
13844
+ return cfg;
13845
+ }
13846
+ function promptGroups(cfg) {
13847
+ const groups = asObject2(cfg.hooks)?.[SUGGEST_HOOK_EVENT];
13848
+ return Array.isArray(groups) ? groups : [];
13849
+ }
13850
+ function isSuggestHookEnabled(settingsContent) {
13851
+ try {
13852
+ return promptGroups(parseSettings(settingsContent)).some(
13853
+ (g) => Array.isArray(g?.hooks) && g.hooks.some(isOurs)
13854
+ );
13855
+ } catch {
13856
+ return false;
13857
+ }
13858
+ }
13859
+ function suggestHookCommand(scriptPath) {
13860
+ return `node "${scriptPath}"`;
13861
+ }
13862
+ function planEnableSuggestHook(settingsContent, command) {
13863
+ if (isSuggestHookEnabled(settingsContent)) {
13864
+ return { content: settingsContent, changed: false };
13865
+ }
13866
+ const cfg = parseSettings(settingsContent);
13867
+ const hooks = asObject2(cfg.hooks) ?? {};
13868
+ cfg.hooks = hooks;
13869
+ const groups = promptGroups(cfg);
13870
+ groups.push({ hooks: [{ type: "command", command, timeout: 5 }] });
13871
+ hooks[SUGGEST_HOOK_EVENT] = groups;
13872
+ return { content: JSON.stringify(cfg, null, 2) + "\n", changed: true };
13873
+ }
13874
+ function planDisableSuggestHook(settingsContent) {
13875
+ if (!isSuggestHookEnabled(settingsContent)) {
13876
+ return { content: settingsContent, changed: false };
13877
+ }
13878
+ const cfg = parseSettings(settingsContent);
13879
+ const hooks = asObject2(cfg.hooks);
13880
+ const kept = promptGroups(cfg).map(
13881
+ (g) => Array.isArray(g?.hooks) ? { ...g, hooks: g.hooks.filter((h) => !isOurs(h)) } : g
13882
+ ).filter((g) => !Array.isArray(g?.hooks) || g.hooks.length > 0);
13883
+ if (kept.length > 0) hooks[SUGGEST_HOOK_EVENT] = kept;
13884
+ else delete hooks[SUGGEST_HOOK_EVENT];
13885
+ if (Object.keys(hooks).length === 0) delete cfg.hooks;
13886
+ return { content: JSON.stringify(cfg, null, 2) + "\n", changed: true };
13887
+ }
13888
+ function suggestHookPaths(home) {
13889
+ return {
13890
+ settingsPath: join7(home, ".claude", "settings.json"),
13891
+ scriptPath: join7(home, ".great-arrow", SUGGEST_HOOK_FILE),
13892
+ hookConfigPath: join7(home, ".great-arrow", "hook-config.json")
13893
+ };
13894
+ }
13895
+ function packagedSuggestHookPath() {
13896
+ const here = dirname4(fileURLToPath(import.meta.url));
13897
+ for (const candidate of [
13898
+ join7(here, "hooks", SUGGEST_HOOK_FILE),
13899
+ join7(here, "..", "..", "dist", "hooks", SUGGEST_HOOK_FILE)
13900
+ ]) {
13901
+ if (existsSync3(candidate)) return candidate;
13902
+ }
13903
+ return null;
13904
+ }
13905
+ async function readOrEmpty(path10) {
13906
+ try {
13907
+ return await readFile3(path10, "utf8");
13908
+ } catch {
13909
+ return "";
13910
+ }
13911
+ }
13912
+ async function atomicWrite(path10, content, mode) {
13913
+ await mkdir4(dirname4(path10), { recursive: true });
13914
+ const tmp = `${path10}.tmp-${process.pid}`;
13915
+ await writeFile3(tmp, content, mode === void 0 ? "utf8" : { mode });
13916
+ await rename(tmp, path10);
13917
+ }
13918
+ function hasHookCredential(content) {
13919
+ try {
13920
+ const cfg = asObject2(JSON.parse(content));
13921
+ return typeof cfg?.token === "string" && cfg.token.trim().length > 0;
13922
+ } catch {
13923
+ return false;
13924
+ }
13925
+ }
13926
+ async function suggestHookStatus(home) {
13927
+ const p = suggestHookPaths(home);
13928
+ return {
13929
+ enabled: isSuggestHookEnabled(await readOrEmpty(p.settingsPath)),
13930
+ scriptPresent: existsSync3(p.scriptPath),
13931
+ credentialPresent: hasHookCredential(await readOrEmpty(p.hookConfigPath))
13932
+ };
13933
+ }
13934
+ async function enableSuggestHook(opts) {
13935
+ const p = suggestHookPaths(opts.home);
13936
+ const settings = await readOrEmpty(p.settingsPath);
13937
+ const plan = planEnableSuggestHook(
13938
+ settings,
13939
+ suggestHookCommand(p.scriptPath)
13940
+ );
13941
+ await mkdir4(dirname4(p.scriptPath), { recursive: true, mode: 448 });
13942
+ await copyFile(opts.packagedScript, p.scriptPath);
13943
+ await chmod(p.scriptPath, 493);
13944
+ if (plan.changed) await atomicWrite(p.settingsPath, plan.content);
13945
+ return { changed: plan.changed };
13946
+ }
13947
+ async function disableSuggestHook(opts) {
13948
+ const p = suggestHookPaths(opts.home);
13949
+ const plan = planDisableSuggestHook(await readOrEmpty(p.settingsPath));
13950
+ if (plan.changed) await atomicWrite(p.settingsPath, plan.content);
13951
+ if (existsSync3(p.scriptPath)) await unlink2(p.scriptPath);
13952
+ return { changed: plan.changed };
13953
+ }
13954
+ async function runSuggestHookOptIn(opts) {
13955
+ if (opts.choice === false) {
13956
+ await opts.disable();
13957
+ return "disabled";
13958
+ }
13959
+ if (opts.choice === void 0 && !opts.interactive) return "unchanged";
13960
+ opts.print(SUGGEST_HOOK_DISCLOSURE);
13961
+ if (opts.choice === void 0) {
13962
+ const yes = await opts.confirm("Turn on tool suggestions?");
13963
+ if (!yes) return "unchanged";
13964
+ }
13965
+ await opts.enable();
13966
+ return "enabled";
13967
+ }
13968
+
13969
+ // src/lib/hook-credential.ts
13970
+ import { existsSync as existsSync4 } from "node:fs";
13971
+ import { readFile as readFile4 } from "node:fs/promises";
13972
+ function issuesToChecks(issues) {
13973
+ return issues.map((issue) => {
13974
+ const check = {
13975
+ name: `Hook credential: ${issue.code}`,
13976
+ status: issue.level,
13977
+ message: issue.message
13978
+ };
13979
+ if (issue.hint) check.hint = issue.hint;
13980
+ return check;
13981
+ });
13982
+ }
13983
+ function mergeHookConfig(existing, url, token) {
13984
+ let base = {};
13985
+ if (existing) {
13986
+ try {
13987
+ const parsed = JSON.parse(existing);
13988
+ if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
13989
+ base = parsed;
13990
+ }
13991
+ } catch {
13992
+ base = {};
13993
+ }
13994
+ }
13995
+ return JSON.stringify({ ...base, url, token }, null, 2);
13996
+ }
13997
+ async function ensureHookCredential(opts) {
13998
+ const fetchImpl = opts.fetchImpl ?? fetch;
13999
+ const { hookConfigPath, apiUrl, auth } = opts;
14000
+ const checks = [];
14001
+ const repairs = [];
14002
+ let url = null;
14003
+ let token = null;
14004
+ let existing = null;
14005
+ if (existsSync4(hookConfigPath)) {
14006
+ existing = await readFile4(hookConfigPath, "utf8");
14007
+ const inspection = inspectHookConfig(existing);
14008
+ url = inspection.url;
14009
+ token = inspection.token;
14010
+ checks.push(...issuesToChecks(inspection.issues));
14011
+ } else {
14012
+ checks.push({
14013
+ name: "Hook credential: not found",
14014
+ status: "warn",
14015
+ message: `${hookConfigPath} does not exist \u2014 hooks cannot authenticate`,
14016
+ hint: "Run with --enforce to write it from your gad login credential."
14017
+ });
14018
+ }
14019
+ const probe = async (t, base) => {
14020
+ const res = await fetchImpl(`${base}/api/mcp/ping`, {
14021
+ headers: { Authorization: `Bearer ${t}` },
14022
+ signal: AbortSignal.timeout(1e4)
14023
+ });
14024
+ return res.status;
14025
+ };
14026
+ let tokenOk = false;
14027
+ if (token) {
14028
+ try {
14029
+ const status = await probe(token, url ?? apiUrl);
14030
+ tokenOk = status >= 200 && status < 300;
14031
+ if (status === 401 || status === 403) {
14032
+ checks.push({
14033
+ name: "Hook credential: token rejected",
14034
+ status: "fail",
14035
+ message: `The hook-config.json token was rejected (HTTP ${status}) \u2014 transcript capture and the session glance are silently failing`,
14036
+ hint: "Run with --enforce to rewrite it from your gad login credential."
14037
+ });
14038
+ } else if (!tokenOk) {
14039
+ checks.push({
14040
+ name: "Hook credential: probe inconclusive",
14041
+ status: "warn",
14042
+ message: `Ping returned HTTP ${status} \u2014 could not confirm the hook token`
14043
+ });
14044
+ } else {
14045
+ checks.push({
14046
+ name: "Hook credential: token valid",
14047
+ status: "pass",
14048
+ message: "hook-config.json token authenticates"
14049
+ });
14050
+ }
14051
+ } catch (e) {
14052
+ checks.push({
14053
+ name: "Hook credential: probe failed",
14054
+ status: "warn",
14055
+ message: `Could not reach ${url ?? apiUrl}: ${String(
14056
+ e instanceof Error ? e.message : e
14057
+ )}`
14058
+ });
14059
+ }
14060
+ }
14061
+ let verified = tokenOk;
14062
+ if (opts.repair && !tokenOk) {
14063
+ if (auth?.token) {
14064
+ await opts.write(
14065
+ hookConfigPath,
14066
+ mergeHookConfig(existing, apiUrl, auth.token)
14067
+ );
14068
+ repairs.push("hook-config.json: rewrote from gad credential");
14069
+ try {
14070
+ const status = await probe(auth.token, apiUrl);
14071
+ verified = status >= 200 && status < 300;
14072
+ checks.push({
14073
+ name: "Hook credential: after repair",
14074
+ status: verified ? "pass" : "fail",
14075
+ message: verified ? "Rewritten token authenticates" : `Rewritten token still failing (HTTP ${status})`
14076
+ });
14077
+ } catch (err) {
14078
+ checks.push({
14079
+ name: "Hook credential: after repair",
14080
+ status: "warn",
14081
+ message: `Rewrote the token but could not re-probe it (${err instanceof Error ? err.message : String(err)})`,
14082
+ hint: "Re-run `gad doctor` once the network is reachable to confirm."
14083
+ });
14084
+ }
14085
+ } else {
14086
+ checks.push({
14087
+ name: "Hook credential: no repair source",
14088
+ status: "fail",
14089
+ message: "No gad login credential available to rewrite hook-config.json",
14090
+ hint: "Run `gad login` first, then re-run with --enforce."
14091
+ });
14092
+ }
14093
+ }
14094
+ return { checks, repairs, verified };
14095
+ }
14096
+
13759
14097
  // src/lib/mcp-doctor.ts
13760
14098
  import { createHash as createHash2 } from "node:crypto";
13761
14099
 
@@ -13927,7 +14265,7 @@ function knownConfigLocations(home, cwd, platform = process.platform, appData) {
13927
14265
  ];
13928
14266
  }
13929
14267
  var GREAT_ARROW_NAME = /great[-_ ]?arrow|ai[-_]memory/i;
13930
- function asObject2(value) {
14268
+ function asObject3(value) {
13931
14269
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
13932
14270
  }
13933
14271
  function isGreatArrowServer(name, entry) {
@@ -13948,13 +14286,13 @@ function classifySurface(entry, clientId, hooksInstalled) {
13948
14286
  }
13949
14287
  function serverMapsFromJson(parsed, serversKey) {
13950
14288
  const maps = [];
13951
- const top = asObject2(parsed[serversKey]);
14289
+ const top = asObject3(parsed[serversKey]);
13952
14290
  if (top) maps.push({ servers: top });
13953
- const projects = asObject2(parsed.projects);
14291
+ const projects = asObject3(parsed.projects);
13954
14292
  if (projects) {
13955
14293
  for (const [dir, value] of Object.entries(projects)) {
13956
- const section = asObject2(value);
13957
- const servers = section ? asObject2(section[serversKey]) : null;
14294
+ const section = asObject3(value);
14295
+ const servers = section ? asObject3(section[serversKey]) : null;
13958
14296
  if (servers && Object.keys(servers).length > 0) {
13959
14297
  maps.push({ servers, projectDir: dir });
13960
14298
  }
@@ -13992,7 +14330,7 @@ function findGreatArrowEntries(location, content, options = {}) {
13992
14330
  if (location.format === "toml") return findCodexEntries(location, content);
13993
14331
  let parsed = null;
13994
14332
  try {
13995
- parsed = asObject2(JSON.parse(content));
14333
+ parsed = asObject3(JSON.parse(content));
13996
14334
  } catch {
13997
14335
  return [];
13998
14336
  }
@@ -14004,7 +14342,7 @@ function findGreatArrowEntries(location, content, options = {}) {
14004
14342
  serversKey
14005
14343
  )) {
14006
14344
  for (const [name, value] of Object.entries(servers)) {
14007
- const entry = asObject2(value);
14345
+ const entry = asObject3(value);
14008
14346
  if (!entry) continue;
14009
14347
  if (!isGreatArrowServer(name, entry)) continue;
14010
14348
  entries.push({
@@ -14027,7 +14365,7 @@ function findGreatArrowEntries(location, content, options = {}) {
14027
14365
  function hasGreatArrowHooks(settingsContent) {
14028
14366
  let parsed = null;
14029
14367
  try {
14030
- parsed = asObject2(JSON.parse(settingsContent));
14368
+ parsed = asObject3(JSON.parse(settingsContent));
14031
14369
  } catch {
14032
14370
  return false;
14033
14371
  }
@@ -14101,7 +14439,7 @@ function describeStoredCredential(raw) {
14101
14439
  if (!trimmed) return null;
14102
14440
  if (trimmed.startsWith("{")) {
14103
14441
  try {
14104
- const parsed = asObject2(JSON.parse(trimmed));
14442
+ const parsed = asObject3(JSON.parse(trimmed));
14105
14443
  const refreshToken = parsed?.refreshToken;
14106
14444
  if (typeof refreshToken === "string" && refreshToken.length > 0) {
14107
14445
  return {
@@ -14500,7 +14838,7 @@ async function checkMcp(auth) {
14500
14838
  }
14501
14839
  }
14502
14840
  function defaultCodexConfigPath() {
14503
- return join7(homedir5(), ".codex", "config.toml");
14841
+ return join8(homedir5(), ".codex", "config.toml");
14504
14842
  }
14505
14843
  function codexIssuesToChecks(issues) {
14506
14844
  return issues.map((issue) => {
@@ -14538,16 +14876,16 @@ function codexCommand() {
14538
14876
  });
14539
14877
  const configPath = options.config ?? defaultCodexConfigPath();
14540
14878
  const expectedRepoUrl = options.expectedRepo ?? detectGitOrigin2();
14541
- let content = existsSync3(configPath) ? await readFile3(configPath, "utf8") : "";
14879
+ let content = existsSync5(configPath) ? await readFile5(configPath, "utf8") : "";
14542
14880
  let backupPath = null;
14543
14881
  let removedServers = [];
14544
14882
  if (options.enforce) {
14545
14883
  const apiUrl = extractCodexGreatArrowApiUrl(content) ?? (await resolveAuth()).apiUrl;
14546
14884
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
14547
- await mkdir4(dirname4(configPath), { recursive: true });
14885
+ await mkdir5(dirname5(configPath), { recursive: true });
14548
14886
  if (content.trim()) {
14549
14887
  backupPath = `${configPath}.bak.${timestamp}`;
14550
- await copyFile(configPath, backupPath);
14888
+ await copyFile2(configPath, backupPath);
14551
14889
  }
14552
14890
  content = upsertCodexServerBlock(content, apiUrl, expectedRepoUrl);
14553
14891
  const preRepair = inspectCodexConfig(content, expectedRepoUrl);
@@ -14557,7 +14895,7 @@ function codexCommand() {
14557
14895
  );
14558
14896
  content = repaired.content;
14559
14897
  removedServers = repaired.removedServers;
14560
- await writeFile3(configPath, content, "utf8");
14898
+ await writeFile4(configPath, content, "utf8");
14561
14899
  }
14562
14900
  const inspection = inspectCodexConfig(content, expectedRepoUrl);
14563
14901
  const checks = codexIssuesToChecks(inspection.issues);
@@ -14639,11 +14977,11 @@ function claudeCommand() {
14639
14977
  noColor: g.noColor
14640
14978
  });
14641
14979
  const home = homedir5();
14642
- const gaDir = join7(home, ".great-arrow");
14643
- const userConfigPath = join7(home, ".claude.json");
14644
- const settingsPath = join7(home, ".claude", "settings.json");
14645
- const hookConfigPath = join7(gaDir, "hook-config.json");
14646
- const glanceHookPath = join7(gaDir, "claude-glance-hook.mjs");
14980
+ const gaDir = join8(home, ".great-arrow");
14981
+ const userConfigPath = join8(home, ".claude.json");
14982
+ const settingsPath = join8(home, ".claude", "settings.json");
14983
+ const hookConfigPath = join8(gaDir, "hook-config.json");
14984
+ const glanceHookPath = join8(gaDir, "claude-glance-hook.mjs");
14647
14985
  const checks = [];
14648
14986
  const repairs = [];
14649
14987
  const backups = [];
@@ -14660,13 +14998,13 @@ function claudeCommand() {
14660
14998
  }
14661
14999
  };
14662
15000
  const backupAndWrite = async (path10, content) => {
14663
- if (existsSync3(path10)) {
15001
+ if (existsSync5(path10)) {
14664
15002
  const backupPath = `${path10}.bak.${timestamp}`;
14665
- await copyFile(path10, backupPath);
15003
+ await copyFile2(path10, backupPath);
14666
15004
  backups.push(backupPath);
14667
15005
  }
14668
- await mkdir4(dirname4(path10), { recursive: true });
14669
- await writeFile3(path10, content, "utf8");
15006
+ await mkdir5(dirname5(path10), { recursive: true });
15007
+ await writeFile4(path10, content, "utf8");
14670
15008
  };
14671
15009
  let auth = null;
14672
15010
  try {
@@ -14675,8 +15013,8 @@ function claudeCommand() {
14675
15013
  auth = null;
14676
15014
  }
14677
15015
  const apiUrl = auth?.apiUrl ?? "https://www.greatarrow.ai";
14678
- if (existsSync3(userConfigPath)) {
14679
- let content = await readFile3(userConfigPath, "utf8");
15016
+ if (existsSync5(userConfigPath)) {
15017
+ let content = await readFile5(userConfigPath, "utf8");
14680
15018
  if (options.enforce) {
14681
15019
  const repaired = repairClaudeUserConfig(content);
14682
15020
  if (repaired.changed.length > 0) {
@@ -14696,7 +15034,7 @@ function claudeCommand() {
14696
15034
  });
14697
15035
  }
14698
15036
  {
14699
- let content = existsSync3(settingsPath) ? await readFile3(settingsPath, "utf8") : "";
15037
+ let content = existsSync5(settingsPath) ? await readFile5(settingsPath, "utf8") : "";
14700
15038
  if (options.enforce) {
14701
15039
  const glanceCommand = `node "${glanceHookPath}"`;
14702
15040
  const repaired = repairClaudeHookSettings(content, glanceCommand);
@@ -14705,15 +15043,15 @@ function claudeCommand() {
14705
15043
  content = repaired.content;
14706
15044
  repairs.push(...repaired.changed.map((c) => `settings.json: ${c}`));
14707
15045
  }
14708
- if (!existsSync3(glanceHookPath)) {
15046
+ if (!existsSync5(glanceHookPath)) {
14709
15047
  try {
14710
15048
  const res = await fetch(
14711
15049
  `${apiUrl}/api/install/claude-glance-hook`,
14712
15050
  { signal: AbortSignal.timeout(1e4) }
14713
15051
  );
14714
15052
  if (res.ok) {
14715
- await mkdir4(gaDir, { recursive: true });
14716
- await writeFile3(glanceHookPath, await res.text(), {
15053
+ await mkdir5(gaDir, { recursive: true });
15054
+ await writeFile4(glanceHookPath, await res.text(), {
14717
15055
  mode: 493
14718
15056
  });
14719
15057
  repairs.push(`downloaded ${glanceHookPath}`);
@@ -14734,7 +15072,7 @@ function claudeCommand() {
14734
15072
  }
14735
15073
  }
14736
15074
  toChecks("Claude hooks", inspectClaudeHookSettings(content).issues);
14737
- if (!options.enforce && !existsSync3(glanceHookPath)) {
15075
+ if (!options.enforce && !existsSync5(glanceHookPath)) {
14738
15076
  checks.push({
14739
15077
  name: "Glance hook script: missing",
14740
15078
  status: "warn",
@@ -14744,96 +15082,31 @@ function claudeCommand() {
14744
15082
  }
14745
15083
  }
14746
15084
  {
14747
- let url = null;
14748
- let token = null;
14749
- if (existsSync3(hookConfigPath)) {
14750
- const inspection = inspectHookConfig(
14751
- await readFile3(hookConfigPath, "utf8")
14752
- );
14753
- url = inspection.url;
14754
- token = inspection.token;
14755
- toChecks("Hook credential", inspection.issues);
14756
- } else {
14757
- checks.push({
14758
- name: "Hook credential: not found",
14759
- status: "warn",
14760
- message: `${hookConfigPath} does not exist \u2014 hooks cannot authenticate`,
14761
- hint: "Run with --enforce to write it from your gad login credential."
14762
- });
14763
- }
14764
- const probe = async (t, base) => {
14765
- const res = await fetch(`${base}/api/mcp/ping`, {
14766
- headers: { Authorization: `Bearer ${t}` },
14767
- signal: AbortSignal.timeout(1e4)
14768
- });
14769
- return res.status;
15085
+ const credential = await ensureHookCredential({
15086
+ hookConfigPath,
15087
+ apiUrl,
15088
+ auth,
15089
+ repair: Boolean(options.enforce),
15090
+ write: backupAndWrite
15091
+ });
15092
+ checks.push(...credential.checks);
15093
+ repairs.push(...credential.repairs);
15094
+ }
15095
+ {
15096
+ const suggest = await suggestHookStatus(home);
15097
+ const check = {
15098
+ name: "Tool suggestions (opt-in)",
15099
+ status: "pass",
15100
+ message: suggest.enabled ? "ON \u2014 each prompt's text is sent to Great Arrow for tool suggestions" : "OFF"
14770
15101
  };
14771
- let tokenOk = false;
14772
- if (token) {
14773
- try {
14774
- const status = await probe(token, url ?? apiUrl);
14775
- tokenOk = status >= 200 && status < 300;
14776
- if (status === 401 || status === 403) {
14777
- checks.push({
14778
- name: "Hook credential: token rejected",
14779
- status: "fail",
14780
- message: `The hook-config.json token was rejected (HTTP ${status}) \u2014 transcript capture and the session glance are silently failing`,
14781
- hint: "Run with --enforce to rewrite it from your gad login credential."
14782
- });
14783
- } else if (!tokenOk) {
14784
- checks.push({
14785
- name: "Hook credential: probe inconclusive",
14786
- status: "warn",
14787
- message: `Ping returned HTTP ${status} \u2014 could not confirm the hook token`
14788
- });
14789
- } else {
14790
- checks.push({
14791
- name: "Hook credential: token valid",
14792
- status: "pass",
14793
- message: "hook-config.json token authenticates"
14794
- });
14795
- }
14796
- } catch (e) {
14797
- checks.push({
14798
- name: "Hook credential: probe failed",
14799
- status: "warn",
14800
- message: `Could not reach ${url ?? apiUrl}: ${String(
14801
- e instanceof Error ? e.message : e
14802
- )}`
14803
- });
14804
- }
14805
- }
14806
- if (options.enforce && !tokenOk) {
14807
- if (auth?.token) {
14808
- await backupAndWrite(
14809
- hookConfigPath,
14810
- JSON.stringify({ url: apiUrl, token: auth.token }, null, 2)
14811
- );
14812
- repairs.push("hook-config.json: rewrote from gad credential");
14813
- try {
14814
- const status = await probe(auth.token, apiUrl);
14815
- checks.push({
14816
- name: "Hook credential: after repair",
14817
- status: status >= 200 && status < 300 ? "pass" : "fail",
14818
- message: status >= 200 && status < 300 ? "Rewritten token authenticates" : `Rewritten token still failing (HTTP ${status})`
14819
- });
14820
- } catch (err) {
14821
- checks.push({
14822
- name: "Hook credential: after repair",
14823
- status: "warn",
14824
- message: `Rewrote the token but could not re-probe it (${err instanceof Error ? err.message : String(err)})`,
14825
- hint: "Re-run `gad doctor` once the network is reachable to confirm."
14826
- });
14827
- }
14828
- } else {
14829
- checks.push({
14830
- name: "Hook credential: no repair source",
14831
- status: "fail",
14832
- message: "No gad login credential available to rewrite hook-config.json",
14833
- hint: "Run `gad login` first, then re-run with --enforce."
14834
- });
14835
- }
15102
+ if (suggest.enabled && !suggest.credentialPresent) {
15103
+ check.status = "warn";
15104
+ check.message = "ON but no hook credential \u2014 the hook sends nothing";
15105
+ check.hint = "Run `gad login`, then `gad mcp suggest-tools on`.";
15106
+ } else if (!suggest.enabled) {
15107
+ check.hint = "Opt in with `gad mcp suggest-tools on`.";
14836
15108
  }
15109
+ checks.push(check);
14837
15110
  }
14838
15111
  {
14839
15112
  let root = command;
@@ -14851,23 +15124,23 @@ function claudeCommand() {
14851
15124
  file: "/Library/Application Support/ClaudeCode/managed-settings.json"
14852
15125
  },
14853
15126
  { scope: "user", file: settingsPath },
14854
- { scope: "project", file: join7(cwd, ".claude", "settings.json") },
14855
- { scope: "local", file: join7(cwd, ".claude", "settings.local.json") }
15127
+ { scope: "project", file: join8(cwd, ".claude", "settings.json") },
15128
+ { scope: "local", file: join8(cwd, ".claude", "settings.local.json") }
14856
15129
  ];
14857
15130
  const configured = [];
14858
15131
  for (const { scope, file } of scopes) {
14859
- if (!existsSync3(file)) continue;
15132
+ if (!existsSync5(file)) continue;
14860
15133
  configured.push(
14861
15134
  ...collectConfiguredHooks(
14862
15135
  scope,
14863
15136
  file,
14864
- await readFile3(file, "utf8")
15137
+ await readFile5(file, "utf8")
14865
15138
  )
14866
15139
  );
14867
15140
  }
14868
15141
  const hookIssues = inspectConfiguredHooks(configured, {
14869
15142
  knownGadVerbs,
14870
- fileExists: existsSync3
15143
+ fileExists: existsSync5
14871
15144
  });
14872
15145
  toChecks("Configured hooks", hookIssues);
14873
15146
  if (configured.length > 0 && hookIssues.length === 0) {
@@ -14923,10 +15196,10 @@ function claudeCommand() {
14923
15196
  function defaultJsonClientConfigs() {
14924
15197
  const home = homedir5();
14925
15198
  return [
14926
- { name: "Claude Code", path: join7(home, ".claude.json") },
14927
- { name: "Cursor", path: join7(home, ".cursor", "mcp.json") },
14928
- { name: "Kiro", path: join7(home, ".kiro", "settings", "mcp.json") },
14929
- { name: "Gemini CLI", path: join7(home, ".gemini", "settings.json") }
15199
+ { name: "Claude Code", path: join8(home, ".claude.json") },
15200
+ { name: "Cursor", path: join8(home, ".cursor", "mcp.json") },
15201
+ { name: "Kiro", path: join8(home, ".kiro", "settings", "mcp.json") },
15202
+ { name: "Gemini CLI", path: join8(home, ".gemini", "settings.json") }
14930
15203
  ];
14931
15204
  }
14932
15205
  function clientsCommand() {
@@ -14952,8 +15225,8 @@ function clientsCommand() {
14952
15225
  const checks = [];
14953
15226
  const results = [];
14954
15227
  for (const { name, path: path10 } of defaultJsonClientConfigs()) {
14955
- if (!existsSync3(path10)) continue;
14956
- let content = await readFile3(path10, "utf8");
15228
+ if (!existsSync5(path10)) continue;
15229
+ let content = await readFile5(path10, "utf8");
14957
15230
  let backupPath = null;
14958
15231
  let removedServers = [];
14959
15232
  if (options.enforce) {
@@ -14961,14 +15234,14 @@ function clientsCommand() {
14961
15234
  if (!pre.parseError && pre.staleRepoServers.length > 0) {
14962
15235
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
14963
15236
  backupPath = `${path10}.bak.${timestamp}`;
14964
- await copyFile(path10, backupPath);
15237
+ await copyFile2(path10, backupPath);
14965
15238
  const repaired = repairJsonMcpConfig(
14966
15239
  content,
14967
15240
  pre.staleRepoServers
14968
15241
  );
14969
15242
  content = repaired.content;
14970
15243
  removedServers = repaired.removedServers;
14971
- await writeFile3(path10, content, "utf8");
15244
+ await writeFile4(path10, content, "utf8");
14972
15245
  }
14973
15246
  }
14974
15247
  const inspection = inspectJsonMcpConfig(content, expectedRepoUrl);
@@ -15463,6 +15736,107 @@ async function runStdioBridge(options) {
15463
15736
  });
15464
15737
  }
15465
15738
 
15739
+ // src/commands/suggest-tools.ts
15740
+ import { homedir as homedir6 } from "node:os";
15741
+ var HOOK_CREDENTIAL_FIX_LINE = "Tool suggestions are on, but the hook's Great Arrow token could not be confirmed \u2014 run `gad doctor claude --enforce` (after `gad login` if needed) to fix it.";
15742
+ async function enableSuggestTools(home, deps = {}) {
15743
+ const packagedScript = deps.packagedScript === void 0 ? packagedSuggestHookPath() : deps.packagedScript;
15744
+ if (!packagedScript) {
15745
+ throw validationError(
15746
+ "The tool-suggestion hook script is missing from this @great-arrow/cli install.",
15747
+ "Reinstall with `npm install -g @great-arrow/cli@latest`."
15748
+ );
15749
+ }
15750
+ await enableSuggestHook({ home, packagedScript });
15751
+ let auth = null;
15752
+ try {
15753
+ auth = await (deps.resolveAuth ?? resolveAuth)();
15754
+ } catch {
15755
+ auth = null;
15756
+ }
15757
+ const credential = await ensureHookCredential({
15758
+ hookConfigPath: suggestHookPaths(home).hookConfigPath,
15759
+ apiUrl: auth?.apiUrl ?? DEFAULT_API_URL,
15760
+ auth,
15761
+ repair: true,
15762
+ write: (path10, content) => atomicWrite(path10, content, 384),
15763
+ ...deps.fetchImpl ? { fetchImpl: deps.fetchImpl } : {}
15764
+ });
15765
+ return { verified: credential.verified };
15766
+ }
15767
+ async function applySuggestToolsChoice(choice, home = homedir6(), deps = {}) {
15768
+ const out = deps.out ?? createFormatter({
15769
+ json: false,
15770
+ quiet: false,
15771
+ isTTY: isTTY(),
15772
+ noColor: false
15773
+ });
15774
+ let verified = true;
15775
+ const outcome = await runSuggestHookOptIn({
15776
+ choice,
15777
+ interactive: deps.interactive ?? Boolean(process.stdin.isTTY && process.stdout.isTTY),
15778
+ print: (line) => out.info(line),
15779
+ confirm: deps.confirm ?? confirm,
15780
+ enable: async () => {
15781
+ verified = (await enableSuggestTools(home, deps)).verified;
15782
+ },
15783
+ disable: () => disableSuggestHook({ home })
15784
+ });
15785
+ const { settingsPath } = suggestHookPaths(home);
15786
+ if (outcome === "enabled") {
15787
+ out.success(`Tool suggestions ON (registered in ${settingsPath}).`);
15788
+ if (!verified) out.warn(HOOK_CREDENTIAL_FIX_LINE);
15789
+ } else if (outcome === "disabled") {
15790
+ out.success("Tool suggestions OFF (our hook entry removed).");
15791
+ }
15792
+ return outcome;
15793
+ }
15794
+ function suggestToolsCommand() {
15795
+ return new Command("suggest-tools").description(
15796
+ "Opt-in Claude Code prompt hook that names Great Arrow tools which may help (sends each prompt's text)"
15797
+ ).argument("[action]", "on | off | status", "status").option("--yes", "Accept the disclosure without an interactive prompt").option("--json", "Output status as JSON").action(
15798
+ async (action, options) => {
15799
+ const home = homedir6();
15800
+ if (action === "status") {
15801
+ const status = await suggestHookStatus(home);
15802
+ if (options.json) {
15803
+ process.stdout.write(JSON.stringify(status) + "\n");
15804
+ return;
15805
+ }
15806
+ process.stdout.write(
15807
+ `Tool suggestions: ${status.enabled ? "ON" : "OFF"}` + (status.enabled && !status.scriptPresent ? " (hook script missing \u2014 run `gad mcp suggest-tools on`)" : "") + (status.enabled && !status.credentialPresent ? " (no hook credential \u2014 run `gad login`, then `gad mcp suggest-tools on`)" : "") + "\n"
15808
+ );
15809
+ return;
15810
+ }
15811
+ if (action === "off") {
15812
+ await applySuggestToolsChoice(false, home);
15813
+ return;
15814
+ }
15815
+ if (action === "on") {
15816
+ const outcome = await applySuggestToolsChoice(
15817
+ options.yes ? true : void 0,
15818
+ home
15819
+ );
15820
+ if (outcome === "unchanged") {
15821
+ if (!(process.stdin.isTTY && process.stdout.isTTY)) {
15822
+ process.stdout.write(`${SUGGEST_HOOK_DISCLOSURE}
15823
+
15824
+ `);
15825
+ }
15826
+ process.stdout.write(
15827
+ "Tool suggestions left OFF. To turn on without a prompt: `gad mcp suggest-tools on --yes`.\n"
15828
+ );
15829
+ }
15830
+ return;
15831
+ }
15832
+ throw validationError(
15833
+ `Unknown action "${action}".`,
15834
+ "Use one of: on, off, status."
15835
+ );
15836
+ }
15837
+ );
15838
+ }
15839
+
15466
15840
  // src/commands/mcp.ts
15467
15841
  var SUPPORTED_CLIENTS = [
15468
15842
  "claude",
@@ -15572,33 +15946,33 @@ async function pathExists(path10) {
15572
15946
  }
15573
15947
  }
15574
15948
  async function installCodexConfig(apiUrl, token, repoUrl) {
15575
- const { homedir: homedir7 } = await import("node:os");
15576
- const { join: join9 } = await import("node:path");
15577
- const { chmod, copyFile: copyFile2, mkdir: mkdir6, readFile: readFile5, writeFile: writeFile5 } = await import("node:fs/promises");
15578
- const codexDir = join9(homedir7(), ".codex");
15579
- const configPath = join9(codexDir, "config.toml");
15580
- const envPath = join9(codexDir, ".env");
15949
+ const { homedir: homedir8 } = await import("node:os");
15950
+ const { join: join10 } = await import("node:path");
15951
+ const { chmod: chmod2, copyFile: copyFile3, mkdir: mkdir7, readFile: readFile7, writeFile: writeFile6 } = await import("node:fs/promises");
15952
+ const codexDir = join10(homedir8(), ".codex");
15953
+ const configPath = join10(codexDir, "config.toml");
15954
+ const envPath = join10(codexDir, ".env");
15581
15955
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15582
- await mkdir6(codexDir, { recursive: true });
15583
- const existingConfig = await pathExists(configPath) ? await readFile5(configPath, "utf8") : "";
15956
+ await mkdir7(codexDir, { recursive: true });
15957
+ const existingConfig = await pathExists(configPath) ? await readFile7(configPath, "utf8") : "";
15584
15958
  if (existingConfig.trim()) {
15585
- await copyFile2(configPath, `${configPath}.bak.${timestamp}`);
15959
+ await copyFile3(configPath, `${configPath}.bak.${timestamp}`);
15586
15960
  }
15587
- await writeFile5(
15961
+ await writeFile6(
15588
15962
  configPath,
15589
15963
  upsertCodexServerBlock(existingConfig, apiUrl, repoUrl),
15590
15964
  "utf8"
15591
15965
  );
15592
- const existingEnv = await pathExists(envPath) ? await readFile5(envPath, "utf8") : "";
15966
+ const existingEnv = await pathExists(envPath) ? await readFile7(envPath, "utf8") : "";
15593
15967
  if (existingEnv.trim()) {
15594
- await copyFile2(envPath, `${envPath}.bak.${timestamp}`);
15968
+ await copyFile3(envPath, `${envPath}.bak.${timestamp}`);
15595
15969
  }
15596
- await writeFile5(
15970
+ await writeFile6(
15597
15971
  envPath,
15598
15972
  upsertEnvValue(existingEnv, "GREAT_ARROW_TOKEN", token),
15599
15973
  "utf8"
15600
15974
  );
15601
- await chmod(envPath, 384);
15975
+ await chmod2(envPath, 384);
15602
15976
  return { configPath, envPath };
15603
15977
  }
15604
15978
  var startCommand = new Command("start").description(
@@ -15825,6 +16199,12 @@ var installCommand = new Command("install").description("Generate MCP configurat
15825
16199
  ).option("--stdout", "Print config to stdout instead of writing file").option(
15826
16200
  "--repo-url <url>",
15827
16201
  "Repo URL to pin MCP requests to (defaults to current git origin)"
16202
+ ).option(
16203
+ "--suggest-tools",
16204
+ "claude only: turn on the opt-in prompt hook that sends each prompt's text to Great Arrow for tool suggestions"
16205
+ ).option(
16206
+ "--no-suggest-tools",
16207
+ "claude only: turn the tool-suggestion prompt hook off (removes only its entry)"
15828
16208
  ).action(
15829
16209
  async (clientName, options) => {
15830
16210
  const fmt = createFormatter({
@@ -15876,15 +16256,15 @@ var installCommand = new Command("install").description("Generate MCP configurat
15876
16256
  process.stdout.write(configJson2 + "\n");
15877
16257
  return;
15878
16258
  }
15879
- const { homedir: homedir7 } = await import("node:os");
15880
- const { join: join10 } = await import("node:path");
15881
- const { mkdir: mkdir6, readFile: readFile5, writeFile: writeFile6 } = await import("node:fs/promises");
15882
- const geminiDir = join10(homedir7(), ".gemini");
15883
- const mcpPath = join10(geminiDir, "settings.json");
15884
- await mkdir6(geminiDir, { recursive: true });
16259
+ const { homedir: homedir8 } = await import("node:os");
16260
+ const { join: join11 } = await import("node:path");
16261
+ const { mkdir: mkdir7, readFile: readFile7, writeFile: writeFile7 } = await import("node:fs/promises");
16262
+ const geminiDir = join11(homedir8(), ".gemini");
16263
+ const mcpPath = join11(geminiDir, "settings.json");
16264
+ await mkdir7(geminiDir, { recursive: true });
15885
16265
  let existingConfig = {};
15886
16266
  try {
15887
- const content = await readFile5(mcpPath, "utf-8");
16267
+ const content = await readFile7(mcpPath, "utf-8");
15888
16268
  existingConfig = JSON.parse(content);
15889
16269
  } catch {
15890
16270
  existingConfig = {};
@@ -15896,20 +16276,20 @@ var installCommand = new Command("install").description("Generate MCP configurat
15896
16276
  ...config2.mcpServers
15897
16277
  }
15898
16278
  };
15899
- await writeFile6(
16279
+ await writeFile7(
15900
16280
  mcpPath,
15901
16281
  JSON.stringify(existingConfig, null, 2) + "\n",
15902
16282
  "utf-8"
15903
16283
  );
15904
16284
  fmt.success(`Wrote gemini MCP config to ${mcpPath}`);
15905
- const agyMcpDir = join10(
16285
+ const agyMcpDir = join11(
15906
16286
  geminiDir,
15907
16287
  "antigravity-cli",
15908
16288
  "mcp",
15909
16289
  MCP_SERVER_NAME
15910
16290
  );
15911
- await mkdir6(agyMcpDir, { recursive: true });
15912
- const instructionsPath = join10(agyMcpDir, "instructions.md");
16291
+ await mkdir7(agyMcpDir, { recursive: true });
16292
+ const instructionsPath = join11(agyMcpDir, "instructions.md");
15913
16293
  const instructionsContent = `You are connected to greatarrow.ai - the user's private workspace memory + integrations brain.
15914
16294
 
15915
16295
  CRITICAL DIRECTIVE (MANDATORY STANDARD):
@@ -15925,7 +16305,7 @@ THREE-LAYER LIVE TASK TRACKING:
15925
16305
  - Maintain client-native harness plans, Great Arrow Kanban cards (\`todo_create\`, \`todo_update\`), and resume states.
15926
16306
 
15927
16307
  Always assume full autonomy to leverage these tools to their maximum extent.`;
15928
- await writeFile6(instructionsPath, instructionsContent, "utf-8");
16308
+ await writeFile7(instructionsPath, instructionsContent, "utf-8");
15929
16309
  fmt.success(`Wrote gemini MCP instructions to ${instructionsPath}`);
15930
16310
  return;
15931
16311
  }
@@ -15940,25 +16320,28 @@ Always assume full autonomy to leverage these tools to their maximum extent.`;
15940
16320
  process.stdout.write(configJson + "\n");
15941
16321
  return;
15942
16322
  }
15943
- const { writeFile: writeFile5 } = await import("node:fs/promises");
15944
- const { join: join9 } = await import("node:path");
15945
- const outputPath = join9(process.cwd(), ".mcp.json");
15946
- await writeFile5(outputPath, configJson + "\n", { mode: 384 });
16323
+ const { writeFile: writeFile6 } = await import("node:fs/promises");
16324
+ const { join: join10 } = await import("node:path");
16325
+ const outputPath = join10(process.cwd(), ".mcp.json");
16326
+ await writeFile6(outputPath, configJson + "\n", { mode: 384 });
15947
16327
  fmt.success(`Wrote ${clientName} MCP config to .mcp.json`);
15948
16328
  if (await mcpJsonAtRiskOfCommit(process.cwd())) {
15949
16329
  fmt.warn(
15950
16330
  ".mcp.json contains an API token and is NOT gitignored here \u2014 add '.mcp.json' to .gitignore before committing."
15951
16331
  );
15952
16332
  }
16333
+ if (clientName === "claude") {
16334
+ await applySuggestToolsChoice(options.suggestTools);
16335
+ }
15953
16336
  }
15954
16337
  );
15955
16338
  var KEYCHAIN_SERVICE = "GreatArrowDigital";
15956
16339
  var KEYCHAIN_ACCOUNT = "api-token";
15957
16340
  var OAUTH_CRED_FILENAME = "oauth-credential.json";
15958
16341
  async function readIfExists(path10) {
15959
- const { readFile: readFile5 } = await import("node:fs/promises");
16342
+ const { readFile: readFile7 } = await import("node:fs/promises");
15960
16343
  try {
15961
- return await readFile5(path10, "utf8");
16344
+ return await readFile7(path10, "utf8");
15962
16345
  } catch {
15963
16346
  return null;
15964
16347
  }
@@ -15973,13 +16356,13 @@ async function readKeychainOnly() {
15973
16356
  }
15974
16357
  }
15975
16358
  async function resolveProxyScript(override) {
15976
- const { homedir: homedir7 } = await import("node:os");
15977
- const { join: join9 } = await import("node:path");
16359
+ const { homedir: homedir8 } = await import("node:os");
16360
+ const { join: join10 } = await import("node:path");
15978
16361
  const candidates = [
15979
16362
  override,
15980
16363
  process.env.GAD_PROXY_SCRIPT,
15981
- join9(homedir7(), ".great-arrow", "mcp-server.mjs"),
15982
- join9(process.cwd(), "scripts", "mcp-server.mjs")
16364
+ join10(homedir8(), ".great-arrow", "mcp-server.mjs"),
16365
+ join10(process.cwd(), "scripts", "mcp-server.mjs")
15983
16366
  ].filter((c) => Boolean(c));
15984
16367
  for (const candidate of candidates) {
15985
16368
  if (await pathExists(candidate)) return candidate;
@@ -16003,13 +16386,13 @@ async function runProxyDiagnostics(scriptPath) {
16003
16386
  }
16004
16387
  }
16005
16388
  async function findLegacyStores(cwd) {
16006
- const { join: join9 } = await import("node:path");
16389
+ const { join: join10 } = await import("node:path");
16007
16390
  const { readdir } = await import("node:fs/promises");
16008
16391
  const dirs = /* @__PURE__ */ new Set([cwd]);
16009
16392
  try {
16010
- const worktreeRoot = join9(cwd, ".claude", "worktrees");
16393
+ const worktreeRoot = join10(cwd, ".claude", "worktrees");
16011
16394
  for (const entry of await readdir(worktreeRoot, { withFileTypes: true })) {
16012
- if (entry.isDirectory()) dirs.add(join9(worktreeRoot, entry.name));
16395
+ if (entry.isDirectory()) dirs.add(join10(worktreeRoot, entry.name));
16013
16396
  }
16014
16397
  } catch (err) {
16015
16398
  debugSwallow("mcp.findLegacyStores(worktrees)", err);
@@ -16031,18 +16414,18 @@ async function findLegacyStores(cwd) {
16031
16414
  }
16032
16415
  const stores = [];
16033
16416
  for (const dir of dirs) {
16034
- const path10 = join9(dir, ".mcp", OAUTH_CRED_FILENAME);
16417
+ const path10 = join10(dir, ".mcp", OAUTH_CRED_FILENAME);
16035
16418
  const raw = await readIfExists(path10);
16036
16419
  if (raw !== null) stores.push({ path: path10, raw });
16037
16420
  }
16038
16421
  return stores;
16039
16422
  }
16040
16423
  async function scanConnections() {
16041
- const { homedir: homedir7 } = await import("node:os");
16042
- const { join: join9 } = await import("node:path");
16043
- const home = homedir7();
16424
+ const { homedir: homedir8 } = await import("node:os");
16425
+ const { join: join10 } = await import("node:path");
16426
+ const home = homedir8();
16044
16427
  const hookSettings = await readIfExists(
16045
- join9(home, ".claude", "settings.json")
16428
+ join10(home, ".claude", "settings.json")
16046
16429
  );
16047
16430
  const hooksInstalled = hookSettings ? hasGreatArrowHooks(hookSettings) : false;
16048
16431
  const entries = [];
@@ -16075,29 +16458,29 @@ async function probeEndpoint() {
16075
16458
  }
16076
16459
  }
16077
16460
  async function applyMigration(plan) {
16078
- const { homedir: homedir7 } = await import("node:os");
16079
- const { join: join9 } = await import("node:path");
16080
- const { mkdir: mkdir6, rename, writeFile: writeFile5 } = await import("node:fs/promises");
16461
+ const { homedir: homedir8 } = await import("node:os");
16462
+ const { join: join10 } = await import("node:path");
16463
+ const { mkdir: mkdir7, rename: rename2, writeFile: writeFile6 } = await import("node:fs/promises");
16081
16464
  const applied = [];
16082
- const dir = join9(homedir7(), ".great-arrow");
16083
- await mkdir6(dir, { recursive: true, mode: 448 });
16465
+ const dir = join10(homedir8(), ".great-arrow");
16466
+ await mkdir7(dir, { recursive: true, mode: 448 });
16084
16467
  if (plan.seedFrom) {
16085
16468
  const raw = await readIfExists(plan.seedFrom);
16086
16469
  if (raw !== null) {
16087
- const target = join9(dir, OAUTH_CRED_FILENAME);
16470
+ const target = join10(dir, OAUTH_CRED_FILENAME);
16088
16471
  const tmp = `${target}.tmp`;
16089
- await writeFile5(tmp, raw, { mode: 384 });
16090
- await rename(tmp, target);
16472
+ await writeFile6(tmp, raw, { mode: 384 });
16473
+ await rename2(tmp, target);
16091
16474
  applied.push(`Copied ${plan.seedFrom} \u2192 ${target}`);
16092
16475
  }
16093
16476
  }
16094
16477
  if (plan.syncKeychainToFile) {
16095
16478
  const raw = await readKeychainOnly();
16096
16479
  if (raw) {
16097
- const target = join9(dir, "credential");
16480
+ const target = join10(dir, "credential");
16098
16481
  const tmp = `${target}.tmp`;
16099
- await writeFile5(tmp, raw, { mode: 384 });
16100
- await rename(tmp, target);
16482
+ await writeFile6(tmp, raw, { mode: 384 });
16483
+ await rename2(tmp, target);
16101
16484
  applied.push(`Wrote the keychain credential to ${target}`);
16102
16485
  }
16103
16486
  }
@@ -16120,19 +16503,19 @@ var doctorSubcommand = new Command("doctor").description(
16120
16503
  noColor: false
16121
16504
  });
16122
16505
  const c = createColor(jsonFlag);
16123
- const { homedir: homedir7 } = await import("node:os");
16124
- const { join: join9 } = await import("node:path");
16125
- const home = homedir7();
16506
+ const { homedir: homedir8 } = await import("node:os");
16507
+ const { join: join10 } = await import("node:path");
16508
+ const home = homedir8();
16126
16509
  const proxyPath = await resolveProxyScript(options.proxy);
16127
16510
  const diagnostics = proxyPath ? await runProxyDiagnostics(proxyPath) : null;
16128
16511
  const entries = await scanConnections();
16129
16512
  const duplicates = detectDuplicates(entries);
16130
16513
  const keychain = await readKeychainOnly();
16131
16514
  const credentialFile = await readIfExists(
16132
- join9(home, ".great-arrow", "credential")
16515
+ join10(home, ".great-arrow", "credential")
16133
16516
  );
16134
16517
  const userStore = await readIfExists(
16135
- join9(home, ".great-arrow", OAUTH_CRED_FILENAME)
16518
+ join10(home, ".great-arrow", OAUTH_CRED_FILENAME)
16136
16519
  );
16137
16520
  const legacyStores = await findLegacyStores(process.cwd());
16138
16521
  const findings = [
@@ -16235,7 +16618,7 @@ ${c.dim("Credential migration")}
16235
16618
  }
16236
16619
  }
16237
16620
  );
16238
- var mcpCommand = new Command("mcp").description("Manage and diagnose the MCP server connection").addCommand(startCommand).addCommand(statusCommand).addCommand(logsCommand).addCommand(testCommand).addCommand(doctorSubcommand).addCommand(installCommand);
16621
+ var mcpCommand = new Command("mcp").description("Manage and diagnose the MCP server connection").addCommand(startCommand).addCommand(statusCommand).addCommand(logsCommand).addCommand(testCommand).addCommand(doctorSubcommand).addCommand(installCommand).addCommand(suggestToolsCommand());
16239
16622
  var mcp_default = mcpCommand;
16240
16623
 
16241
16624
  // src/commands/smoke.ts
@@ -16556,9 +16939,9 @@ function askCommand() {
16556
16939
  }
16557
16940
 
16558
16941
  // src/lib/glance-footer.ts
16559
- import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir5 } from "node:fs/promises";
16560
- import { homedir as homedir6 } from "node:os";
16561
- import { join as join8, dirname as dirname5 } from "node:path";
16942
+ import { readFile as readFile6, writeFile as writeFile5, mkdir as mkdir6 } from "node:fs/promises";
16943
+ import { homedir as homedir7 } from "node:os";
16944
+ import { join as join9, dirname as dirname6 } from "node:path";
16562
16945
 
16563
16946
  // src/lib/seen.ts
16564
16947
  var SEEN_TIMEOUT_MS = 1e3;
@@ -16945,11 +17328,11 @@ function shouldRequestTip(input) {
16945
17328
  return now - state.lastTipAt >= TIP_THROTTLE_INTERVAL_MS;
16946
17329
  }
16947
17330
  function getGlanceStatePath() {
16948
- return join8(homedir6(), ".gad", "glance-state.json");
17331
+ return join9(homedir7(), ".gad", "glance-state.json");
16949
17332
  }
16950
17333
  async function loadGlanceState() {
16951
17334
  try {
16952
- const content = await readFile4(getGlanceStatePath(), "utf-8");
17335
+ const content = await readFile6(getGlanceStatePath(), "utf-8");
16953
17336
  const parsed = JSON.parse(content);
16954
17337
  if (typeof parsed === "object" && parsed !== null) {
16955
17338
  return parsed;
@@ -16961,8 +17344,8 @@ async function loadGlanceState() {
16961
17344
  }
16962
17345
  async function saveGlanceState(state) {
16963
17346
  const path10 = getGlanceStatePath();
16964
- await mkdir5(dirname5(path10), { recursive: true });
16965
- await writeFile4(path10, JSON.stringify(state), "utf-8");
17347
+ await mkdir6(dirname6(path10), { recursive: true });
17348
+ await writeFile5(path10, JSON.stringify(state), "utf-8");
16966
17349
  }
16967
17350
  function glanceItemUrl(href, apiUrl) {
16968
17351
  if (!href) return null;
@@ -18844,7 +19227,7 @@ function completionsCommand() {
18844
19227
  // node_modules/.pnpm/update-notifier@7.3.1/node_modules/update-notifier/update-notifier.js
18845
19228
  import process11 from "node:process";
18846
19229
  import { spawn } from "node:child_process";
18847
- import { fileURLToPath as fileURLToPath2 } from "node:url";
19230
+ import { fileURLToPath as fileURLToPath3 } from "node:url";
18848
19231
  import path9 from "node:path";
18849
19232
  import { format } from "node:util";
18850
19233
 
@@ -20995,7 +21378,7 @@ var isNpmOrYarn = isNpm || isYarn;
20995
21378
  // node_modules/.pnpm/is-installed-globally@1.0.0/node_modules/is-installed-globally/index.js
20996
21379
  import fs5 from "node:fs";
20997
21380
  import path8 from "node:path";
20998
- import { fileURLToPath } from "node:url";
21381
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
20999
21382
 
21000
21383
  // node_modules/.pnpm/global-directory@4.0.1/node_modules/global-directory/index.js
21001
21384
  var import_ini = __toESM(require_ini2(), 1);
@@ -21099,7 +21482,7 @@ function isPathInside(childPath, parentPath) {
21099
21482
  }
21100
21483
 
21101
21484
  // node_modules/.pnpm/is-installed-globally@1.0.0/node_modules/is-installed-globally/index.js
21102
- var __dirname2 = path8.dirname(fileURLToPath(import.meta.url));
21485
+ var __dirname2 = path8.dirname(fileURLToPath2(import.meta.url));
21103
21486
  var isInstalledGlobally = (() => {
21104
21487
  try {
21105
21488
  return isPathInside(__dirname2, global_directory_default.yarn.packages) || isPathInside(__dirname2, fs5.realpathSync(global_directory_default.npm.packages));
@@ -22081,7 +22464,7 @@ function pupa(template, data, { ignoreMissing = false, transform = ({ value }) =
22081
22464
  }
22082
22465
 
22083
22466
  // node_modules/.pnpm/update-notifier@7.3.1/node_modules/update-notifier/update-notifier.js
22084
- var __dirname3 = path9.dirname(fileURLToPath2(import.meta.url));
22467
+ var __dirname3 = path9.dirname(fileURLToPath3(import.meta.url));
22085
22468
  var ONE_DAY = 1e3 * 60 * 60 * 24;
22086
22469
  var UpdateNotifier = class {
22087
22470
  // Public