@great-arrow/cli 0.1.46 → 0.1.48

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +7 -0
  2. package/dist/index.js +319 -149
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -38,6 +38,13 @@ gad whoami
38
38
 
39
39
  # Revoke the session server-side and remove stored credentials
40
40
  gad logout
41
+
42
+ # For tools, not humans: print a fresh access token through the CLI's locked
43
+ # refresh path (~/.great-arrow/refresh.lock), so other programs never refresh
44
+ # this credential themselves. --json prints {accessToken, accessExpiresAt,
45
+ # apiUrl}. Exit 2: session expired or not signed in. Exit 3: the stored
46
+ # credential is a personal token (nothing to refresh).
47
+ gad auth token [--json]
41
48
  ```
42
49
 
43
50
  Credentials live in your OS keychain (Keychain Access / libsecret / Credential
package/dist/index.js CHANGED
@@ -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 join8 = __require("path").join;
1967
+ var join9 = __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
- join8(etc, name, "config"),
1993
- join8(etc, name + "rc")
1992
+ join9(etc, name, "config"),
1993
+ join9(etc, name + "rc")
1994
1994
  ].forEach(addConfigFile);
1995
1995
  if (home)
1996
1996
  [
1997
- join8(home, ".config", name, "config"),
1998
- join8(home, ".config", name),
1999
- join8(home, "." + name, "config"),
2000
- join8(home, "." + name + "rc")
1997
+ join9(home, ".config", name, "config"),
1998
+ join9(home, ".config", name),
1999
+ join9(home, "." + name, "config"),
2000
+ join9(home, "." + name + "rc")
2001
2001
  ].forEach(addConfigFile);
2002
2002
  addConfigFile(cc.find("." + name + "rc"));
2003
2003
  if (env4.config) addConfigFile(env4.config);
@@ -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.46";
9402
+ var CLI_VERSION = "0.1.48";
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";
@@ -9538,10 +9538,55 @@ async function clearStoredCredential() {
9538
9538
  await deleteToken();
9539
9539
  }
9540
9540
 
9541
+ // src/lib/cli-locator.ts
9542
+ import {
9543
+ chmodSync,
9544
+ mkdirSync,
9545
+ realpathSync,
9546
+ renameSync,
9547
+ rmSync,
9548
+ writeFileSync
9549
+ } from "node:fs";
9550
+ import { homedir as homedir2 } from "node:os";
9551
+ import { join as join2 } from "node:path";
9552
+ function writeCliLocator(home = homedir2(), entry = process.argv[1]) {
9553
+ if (!entry) return;
9554
+ const dir = join2(home, ".great-arrow");
9555
+ const target = join2(dir, "cli-bin.json");
9556
+ const tmp = `${target}.${process.pid}.tmp`;
9557
+ try {
9558
+ let resolved = entry;
9559
+ try {
9560
+ resolved = realpathSync(entry);
9561
+ } catch (err) {
9562
+ debugSwallow("cli-locator.realpath", err);
9563
+ }
9564
+ mkdirSync(dir, { recursive: true });
9565
+ writeFileSync(
9566
+ tmp,
9567
+ JSON.stringify({
9568
+ node: process.execPath,
9569
+ entry: resolved,
9570
+ version: CLI_VERSION
9571
+ }),
9572
+ { mode: 384 }
9573
+ );
9574
+ chmodSync(tmp, 384);
9575
+ renameSync(tmp, target);
9576
+ } catch (err) {
9577
+ debugSwallow("cli-locator.write", err);
9578
+ try {
9579
+ rmSync(tmp, { force: true });
9580
+ } catch (cleanupErr) {
9581
+ debugSwallow("cli-locator.cleanup", cleanupErr);
9582
+ }
9583
+ }
9584
+ }
9585
+
9541
9586
  // src/lib/config.ts
9542
9587
  import { readFile as readFile2, writeFile as writeFile2, mkdir as mkdir2 } from "node:fs/promises";
9543
- import { homedir as homedir2 } from "node:os";
9544
- import { join as join2, dirname as dirname2 } from "node:path";
9588
+ import { homedir as homedir3 } from "node:os";
9589
+ import { join as join3, dirname as dirname2 } from "node:path";
9545
9590
  var DEFAULTS = {
9546
9591
  api_url: "https://www.greatarrow.ai",
9547
9592
  default_workspace: "",
@@ -9552,7 +9597,7 @@ var DEFAULTS = {
9552
9597
  telemetry_enabled: false
9553
9598
  };
9554
9599
  function getConfigPath() {
9555
- return join2(homedir2(), ".gadrc");
9600
+ return join3(homedir3(), ".gadrc");
9556
9601
  }
9557
9602
  function parseConfig(content) {
9558
9603
  const config = {};
@@ -9820,6 +9865,10 @@ function trimSlash(url) {
9820
9865
  }
9821
9866
 
9822
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
+ };
9823
9872
  var RefreshRejected = class extends Error {
9824
9873
  constructor(message) {
9825
9874
  super(message);
@@ -9849,11 +9898,13 @@ function toCredential(env4, apiUrl, clientId, now) {
9849
9898
  apiUrl: trimSlash2(apiUrl)
9850
9899
  };
9851
9900
  }
9852
- async function postToken(apiUrl, form, fetchImpl) {
9901
+ var TOKEN_POST_TIMEOUT_MS = 8e3;
9902
+ async function postToken(apiUrl, form, fetchImpl, timeoutMs = TOKEN_POST_TIMEOUT_MS) {
9853
9903
  const res = await fetchImpl(`${trimSlash2(apiUrl)}/api/oauth/token`, {
9854
9904
  method: "POST",
9855
- headers: { "content-type": "application/x-www-form-urlencoded" },
9856
- body: new URLSearchParams(form).toString()
9905
+ headers: TOKEN_HEADERS,
9906
+ body: new URLSearchParams(form).toString(),
9907
+ signal: AbortSignal.timeout(timeoutMs)
9857
9908
  });
9858
9909
  const json = await res.json().catch(() => ({}));
9859
9910
  return { ok: res.ok, status: res.status, json };
@@ -9893,8 +9944,9 @@ async function revokeToken(opts) {
9893
9944
  try {
9894
9945
  const res = await doFetch(`${trimSlash2(opts.apiUrl)}/api/oauth/revoke`, {
9895
9946
  method: "POST",
9896
- headers: { "content-type": "application/x-www-form-urlencoded" },
9897
- body: new URLSearchParams(form).toString()
9947
+ headers: TOKEN_HEADERS,
9948
+ body: new URLSearchParams(form).toString(),
9949
+ signal: AbortSignal.timeout(TOKEN_POST_TIMEOUT_MS)
9898
9950
  });
9899
9951
  return res.ok;
9900
9952
  } catch {
@@ -9910,7 +9962,8 @@ async function refreshTokens(opts) {
9910
9962
  client_id: opts.clientId,
9911
9963
  refresh_token: opts.refreshToken
9912
9964
  },
9913
- opts.fetchImpl ?? fetch
9965
+ opts.fetchImpl ?? fetch,
9966
+ opts.timeoutMs
9914
9967
  );
9915
9968
  if (!ok) {
9916
9969
  const desc = String(json.error_description ?? json.error ?? "unknown");
@@ -10284,6 +10337,7 @@ var loginCommand = new Command("login").description("Authenticate the CLI with y
10284
10337
  process.exitCode = 1;
10285
10338
  return;
10286
10339
  }
10340
+ writeCliLocator();
10287
10341
  config.cli_client_id = result.clientId;
10288
10342
  config.cli_redirect_uri = result.redirectUri;
10289
10343
  config.api_url = DEFAULT_API_URL;
@@ -10303,52 +10357,22 @@ var loginCommand = new Command("login").description("Authenticate the CLI with y
10303
10357
  );
10304
10358
  var login_default = loginCommand;
10305
10359
 
10306
- // src/commands/logout.ts
10307
- var logoutCommand = new Command("logout").description("Remove stored authentication credentials").action(async (_options, cmd) => {
10308
- const parent = cmd.parent;
10309
- const json = parent?.opts().json ?? false;
10310
- const quiet = parent?.opts().quiet ?? false;
10311
- const noColor = parent?.opts().noColor ?? false;
10312
- const fmt = createFormatter({
10313
- json,
10314
- quiet,
10315
- isTTY: isTTY(),
10316
- noColor
10317
- });
10318
- const stored = await loadStoredCredential();
10319
- if (stored?.kind === "oauth") {
10320
- await revokeToken({
10321
- apiUrl: stored.credential.apiUrl,
10322
- clientId: stored.credential.clientId,
10323
- token: stored.credential.refreshToken,
10324
- tokenTypeHint: "refresh_token"
10325
- });
10326
- }
10327
- await clearStoredCredential();
10328
- const config = await loadConfig();
10329
- if (config.token) {
10330
- config.token = "";
10331
- await saveConfig(config);
10332
- }
10333
- fmt.success("Logged out successfully.");
10334
- });
10335
-
10336
10360
  // src/lib/auth.ts
10337
10361
  import { mkdir as mkdir3, open, rm, stat } from "node:fs/promises";
10338
- import { homedir as homedir3 } from "node:os";
10339
- import { join as join3 } from "node:path";
10362
+ import { homedir as homedir4 } from "node:os";
10363
+ import { join as join4 } from "node:path";
10340
10364
  var REFRESH_SKEW_MS = 12e4;
10341
10365
  var inflightRefresh = null;
10342
10366
  var REFRESH_LOCK_STALE_MS = 15e3;
10343
10367
  var REFRESH_LOCK_WAIT_MS = 5e3;
10344
10368
  var REFRESH_LOCK_POLL_MS = 100;
10345
10369
  function refreshLockPath() {
10346
- return join3(homedir3(), ".great-arrow", "refresh.lock");
10370
+ return join4(homedir4(), ".great-arrow", "refresh.lock");
10347
10371
  }
10348
10372
  async function acquireRefreshLock() {
10349
10373
  const path10 = refreshLockPath();
10350
10374
  try {
10351
- await mkdir3(join3(homedir3(), ".great-arrow"), { recursive: true });
10375
+ await mkdir3(join4(homedir4(), ".great-arrow"), { recursive: true });
10352
10376
  } catch (err) {
10353
10377
  debugSwallow("refresh-lock.mkdir", err);
10354
10378
  }
@@ -10382,7 +10406,7 @@ async function releaseRefreshLock() {
10382
10406
  debugSwallow("refresh-lock.release", err);
10383
10407
  }
10384
10408
  }
10385
- async function rotatedByAnotherProcess(ours) {
10409
+ async function storedPairOtherThan(ours) {
10386
10410
  let stored = null;
10387
10411
  try {
10388
10412
  stored = await loadStoredCredential();
@@ -10390,8 +10414,11 @@ async function rotatedByAnotherProcess(ours) {
10390
10414
  debugSwallow("refresh.reread", err);
10391
10415
  }
10392
10416
  if (stored?.kind !== "oauth") return null;
10393
- const theirs = stored.credential;
10394
- if (theirs.refreshToken === ours.refreshToken) return null;
10417
+ return stored.credential.refreshToken === ours.refreshToken ? null : stored.credential;
10418
+ }
10419
+ async function rotatedByAnotherProcess(ours) {
10420
+ const theirs = await storedPairOtherThan(ours);
10421
+ if (!theirs) return null;
10395
10422
  if (Date.now() >= theirs.accessExpiresAt - REFRESH_SKEW_MS) return null;
10396
10423
  return theirs;
10397
10424
  }
@@ -10434,11 +10461,40 @@ async function resolveAuth() {
10434
10461
  tokenType: "personal"
10435
10462
  });
10436
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
+ }
10437
10472
  throw authError(
10438
10473
  "No authentication token found.",
10439
10474
  "Run 'gad login' to authenticate, or set the GAD_TOKEN environment variable."
10440
10475
  );
10441
10476
  }
10477
+ var NotOAuthCredential = class extends Error {
10478
+ code = "not_oauth";
10479
+ };
10480
+ async function getFreshOAuthAccessToken() {
10481
+ const stored = await loadStoredCredential();
10482
+ if (!stored) {
10483
+ throw authError(
10484
+ "Session expired. Not signed in.",
10485
+ "Run 'gad login' to authenticate."
10486
+ );
10487
+ }
10488
+ if (stored.kind !== "oauth") {
10489
+ throw new NotOAuthCredential("stored credential is not OAuth");
10490
+ }
10491
+ const fresh = await ensureFreshAccessToken(stored.credential);
10492
+ return {
10493
+ accessToken: fresh.accessToken,
10494
+ accessExpiresAt: fresh.accessExpiresAt,
10495
+ apiUrl: fresh.apiUrl
10496
+ };
10497
+ }
10442
10498
  async function ensureFreshAccessToken(credential) {
10443
10499
  if (Date.now() < credential.accessExpiresAt - REFRESH_SKEW_MS) {
10444
10500
  return credential;
@@ -10468,6 +10524,8 @@ async function doRefresh(credential) {
10468
10524
  return refreshed;
10469
10525
  } catch (err) {
10470
10526
  if (err instanceof RefreshRejected) {
10527
+ const theirs = await storedPairOtherThan(credential);
10528
+ if (theirs) return theirs;
10471
10529
  await clearStoredCredential();
10472
10530
  throw sessionExpired();
10473
10531
  }
@@ -10480,6 +10538,70 @@ function sessionExpired() {
10480
10538
  return authError("Session expired.", "Run 'gad login' to re-authenticate.");
10481
10539
  }
10482
10540
 
10541
+ // src/commands/auth.ts
10542
+ var EXIT_SESSION_EXPIRED = 2;
10543
+ var EXIT_NOT_OAUTH = 3;
10544
+ function authCommand() {
10545
+ const cmd = new Command("auth").description("Authentication helpers");
10546
+ cmd.command("token").description("Print a fresh OAuth access token, refreshing it if needed").option("--json", "Output {accessToken, accessExpiresAt, apiUrl} as JSON").action(async (options) => {
10547
+ try {
10548
+ const t = await getFreshOAuthAccessToken();
10549
+ writeCliLocator();
10550
+ process.stdout.write(
10551
+ options.json ? `${JSON.stringify(t)}
10552
+ ` : `${t.accessToken}
10553
+ `
10554
+ );
10555
+ } catch (error) {
10556
+ if (error instanceof NotOAuthCredential) {
10557
+ process.stderr.write(`${error.message}
10558
+ `);
10559
+ process.exitCode = EXIT_NOT_OAUTH;
10560
+ return;
10561
+ }
10562
+ if (error instanceof CliError && error.code === 1) {
10563
+ const hint = error.hint ? ` ${error.hint}` : "";
10564
+ process.stderr.write(`${error.message}${hint}
10565
+ `);
10566
+ process.exitCode = EXIT_SESSION_EXPIRED;
10567
+ return;
10568
+ }
10569
+ throw error;
10570
+ }
10571
+ });
10572
+ return cmd;
10573
+ }
10574
+
10575
+ // src/commands/logout.ts
10576
+ var logoutCommand = new Command("logout").description("Remove stored authentication credentials").action(async (_options, cmd) => {
10577
+ const parent = cmd.parent;
10578
+ const json = parent?.opts().json ?? false;
10579
+ const quiet = parent?.opts().quiet ?? false;
10580
+ const noColor = parent?.opts().noColor ?? false;
10581
+ const fmt = createFormatter({
10582
+ json,
10583
+ quiet,
10584
+ isTTY: isTTY(),
10585
+ noColor
10586
+ });
10587
+ const stored = await loadStoredCredential();
10588
+ if (stored?.kind === "oauth") {
10589
+ await revokeToken({
10590
+ apiUrl: stored.credential.apiUrl,
10591
+ clientId: stored.credential.clientId,
10592
+ token: stored.credential.refreshToken,
10593
+ tokenTypeHint: "refresh_token"
10594
+ });
10595
+ }
10596
+ await clearStoredCredential();
10597
+ const config = await loadConfig();
10598
+ if (config.token) {
10599
+ config.token = "";
10600
+ await saveConfig(config);
10601
+ }
10602
+ fmt.success("Logged out successfully.");
10603
+ });
10604
+
10483
10605
  // src/lib/client.ts
10484
10606
  import { execSync } from "node:child_process";
10485
10607
 
@@ -10641,6 +10763,46 @@ function isRetryable(error) {
10641
10763
  }
10642
10764
  return false;
10643
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
+ }
10644
10806
  function isRetryableStatus(status) {
10645
10807
  return status >= 500 && status < 600;
10646
10808
  }
@@ -10696,7 +10858,7 @@ var GadClient = class {
10696
10858
  /**
10697
10859
  * Execute a fetch request with timeout and retry logic.
10698
10860
  */
10699
- async fetchWithRetry(url, init, timeoutMs) {
10861
+ async fetchWithRetry(url, init, timeoutMs, policy = "transient") {
10700
10862
  const effectiveTimeout = timeoutMs ?? this.timeout;
10701
10863
  let lastError;
10702
10864
  for (let attempt = 0; attempt < MAX_RETRIES; attempt++) {
@@ -10708,7 +10870,7 @@ var GadClient = class {
10708
10870
  signal: controller.signal
10709
10871
  });
10710
10872
  clearTimeout(timer);
10711
- if (isRetryableStatus(response.status)) {
10873
+ if (policy === "transient" && isRetryableStatus(response.status)) {
10712
10874
  lastError = response;
10713
10875
  if (attempt < MAX_RETRIES - 1) {
10714
10876
  await sleep(retryDelay(attempt));
@@ -10720,7 +10882,8 @@ var GadClient = class {
10720
10882
  } catch (error) {
10721
10883
  clearTimeout(timer);
10722
10884
  lastError = error;
10723
- if (isRetryable(error) && attempt < MAX_RETRIES - 1) {
10885
+ const retryable = policy === "transient" ? isRetryable(error) : isConnectionPhaseFailure(error);
10886
+ if (retryable && attempt < MAX_RETRIES - 1) {
10724
10887
  await sleep(retryDelay(attempt));
10725
10888
  continue;
10726
10889
  }
@@ -10789,7 +10952,8 @@ var GadClient = class {
10789
10952
  headers: this.buildHeaders(),
10790
10953
  body
10791
10954
  },
10792
- timeoutMs
10955
+ timeoutMs,
10956
+ RETRY_SAFE_TOOLS.has(toolName) ? "transient" : "connection-only"
10793
10957
  );
10794
10958
  } catch (error) {
10795
10959
  if (error instanceof CliError) throw error;
@@ -10881,11 +11045,16 @@ var GadClient = class {
10881
11045
  const url = `${this.apiUrl}${path10}`;
10882
11046
  let response;
10883
11047
  try {
10884
- response = await this.fetchWithRetry(url, {
10885
- method: "POST",
10886
- headers: this.buildHeaders(),
10887
- body: JSON.stringify(body)
10888
- });
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
+ );
10889
11058
  } catch (error) {
10890
11059
  if (error instanceof CliError) throw error;
10891
11060
  void captureCliEvent({
@@ -12302,8 +12471,8 @@ var work_default = workCommand;
12302
12471
 
12303
12472
  // src/commands/worktree.ts
12304
12473
  import { execFileSync as execFileSync3 } from "node:child_process";
12305
- import { existsSync, readFileSync as readFileSync2, statSync, writeFileSync } from "node:fs";
12306
- import { basename, dirname as dirname3, isAbsolute, join as join4, resolve } from "node:path";
12474
+ import { existsSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
12475
+ import { basename, dirname as dirname3, isAbsolute, join as join5, resolve } from "node:path";
12307
12476
  var realExec = (cmd, args, opts) => {
12308
12477
  const out = execFileSync3(cmd, args, {
12309
12478
  cwd: opts?.cwd,
@@ -12325,9 +12494,9 @@ function parseDefaultBranch(symbolicRef) {
12325
12494
  return match ? match[1] : null;
12326
12495
  }
12327
12496
  function detectPackageManager(repoRoot) {
12328
- if (existsSync(join4(repoRoot, "pnpm-lock.yaml"))) return "pnpm";
12329
- if (existsSync(join4(repoRoot, "bun.lockb"))) return "bun";
12330
- if (existsSync(join4(repoRoot, "yarn.lock"))) return "yarn";
12497
+ if (existsSync(join5(repoRoot, "pnpm-lock.yaml"))) return "pnpm";
12498
+ if (existsSync(join5(repoRoot, "bun.lockb"))) return "bun";
12499
+ if (existsSync(join5(repoRoot, "yarn.lock"))) return "yarn";
12331
12500
  return "npm";
12332
12501
  }
12333
12502
  function defaultWorktreeDir(repoRoot, branch) {
@@ -12335,7 +12504,7 @@ function defaultWorktreeDir(repoRoot, branch) {
12335
12504
  /[^A-Za-z0-9._-]+/g,
12336
12505
  "-"
12337
12506
  );
12338
- return join4(dirname3(repoRoot), `${basename(repoRoot)}-${slug}`);
12507
+ return join5(dirname3(repoRoot), `${basename(repoRoot)}-${slug}`);
12339
12508
  }
12340
12509
  var AGENT_PROCESS_RE = /(^|\/)(claude|kiro|cursor|codex)/i;
12341
12510
  function psField(pid, field) {
@@ -12358,7 +12527,7 @@ function findAgentPid(startPid) {
12358
12527
  return null;
12359
12528
  }
12360
12529
  function ownGitDir(worktreeDir) {
12361
- const dotGit = join4(worktreeDir, ".git");
12530
+ const dotGit = join5(worktreeDir, ".git");
12362
12531
  if (!existsSync(dotGit)) return null;
12363
12532
  if (statSync(dotGit).isDirectory()) return dotGit;
12364
12533
  const match = readFileSync2(dotGit, "utf8").trim().match(/^gitdir:\s*(.+)$/m);
@@ -12372,8 +12541,8 @@ function stampWorktreeOwner(worktreeDir) {
12372
12541
  if (!gitDir) return null;
12373
12542
  const ownerPid = findAgentPid(process.ppid);
12374
12543
  if (!ownerPid) return null;
12375
- writeFileSync(
12376
- join4(gitDir, "agent-owner.json"),
12544
+ writeFileSync2(
12545
+ join5(gitDir, "agent-owner.json"),
12377
12546
  JSON.stringify(
12378
12547
  {
12379
12548
  ownerPid,
@@ -12467,7 +12636,7 @@ function createTaskWorktree(opts) {
12467
12636
  const stampedPid = opts.stamp === false ? null : stampWorktreeOwner(dir);
12468
12637
  const packageManager = detectPackageManager(repoRoot);
12469
12638
  let installed = false;
12470
- if (opts.install !== false && existsSync(join4(repoRoot, "package.json"))) {
12639
+ if (opts.install !== false && existsSync(join5(repoRoot, "package.json"))) {
12471
12640
  exec2(packageManager, ["install"], { cwd: dir, inherit: true });
12472
12641
  installed = true;
12473
12642
  }
@@ -12530,13 +12699,13 @@ var worktree_default = worktreeCommand;
12530
12699
  // src/commands/install-hooks.ts
12531
12700
  import { execFileSync as execFileSync4 } from "node:child_process";
12532
12701
  import {
12533
- chmodSync,
12702
+ chmodSync as chmodSync2,
12534
12703
  existsSync as existsSync2,
12535
- mkdirSync,
12704
+ mkdirSync as mkdirSync2,
12536
12705
  readFileSync as readFileSync3,
12537
- writeFileSync as writeFileSync2
12706
+ writeFileSync as writeFileSync3
12538
12707
  } from "node:fs";
12539
- import { isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "node:path";
12708
+ import { isAbsolute as isAbsolute2, join as join6, resolve as resolve2 } from "node:path";
12540
12709
  var GUARD_MARKER = "# gad-agent-guard v1";
12541
12710
  var SCHEMA_AUDIT_MARKER = "# gad-schema-audit v1";
12542
12711
  var GUARD_FILE = "gad-commit-guard.mjs";
@@ -12719,28 +12888,28 @@ function installCommitGuard(opts) {
12719
12888
  );
12720
12889
  }
12721
12890
  if (!isAbsolute2(commonDir)) commonDir = resolve2(repoRoot, commonDir);
12722
- const hooksDir = join5(commonDir, "hooks");
12723
- mkdirSync(hooksDir, { recursive: true });
12724
- const guardPath = join5(hooksDir, GUARD_FILE);
12725
- writeFileSync2(guardPath, buildGuardScript());
12726
- chmodSync(guardPath, 493);
12727
- const huskyHook = join5(repoRoot, ".husky", "pre-commit");
12728
- const hookPath = existsSync2(huskyHook) ? huskyHook : join5(hooksDir, "pre-commit");
12891
+ const hooksDir = join6(commonDir, "hooks");
12892
+ mkdirSync2(hooksDir, { recursive: true });
12893
+ const guardPath = join6(hooksDir, GUARD_FILE);
12894
+ writeFileSync3(guardPath, buildGuardScript());
12895
+ chmodSync2(guardPath, 493);
12896
+ const huskyHook = join6(repoRoot, ".husky", "pre-commit");
12897
+ const hookPath = existsSync2(huskyHook) ? huskyHook : join6(hooksDir, "pre-commit");
12729
12898
  const existing = existsSync2(hookPath) ? readFileSync3(hookPath, "utf8") : null;
12730
12899
  const plan = planHookInstall(existing);
12731
12900
  if (plan.action !== "noop") {
12732
- writeFileSync2(hookPath, plan.content);
12901
+ writeFileSync3(hookPath, plan.content);
12733
12902
  }
12734
- chmodSync(hookPath, 493);
12735
- const huskyDir = join5(repoRoot, ".husky");
12736
- const huskyPrePush = join5(huskyDir, "pre-push");
12737
- const schemaAuditHookPath = existsSync2(huskyPrePush) || existsSync2(huskyDir) ? huskyPrePush : join5(hooksDir, "pre-push");
12903
+ chmodSync2(hookPath, 493);
12904
+ const huskyDir = join6(repoRoot, ".husky");
12905
+ const huskyPrePush = join6(huskyDir, "pre-push");
12906
+ const schemaAuditHookPath = existsSync2(huskyPrePush) || existsSync2(huskyDir) ? huskyPrePush : join6(hooksDir, "pre-push");
12738
12907
  const existingSchemaAuditHook = existsSync2(schemaAuditHookPath) ? readFileSync3(schemaAuditHookPath, "utf8") : null;
12739
12908
  const schemaAuditPlan = planSchemaAuditHookInstall(existingSchemaAuditHook);
12740
12909
  if (schemaAuditPlan.action !== "noop") {
12741
- writeFileSync2(schemaAuditHookPath, schemaAuditPlan.content);
12910
+ writeFileSync3(schemaAuditHookPath, schemaAuditPlan.content);
12742
12911
  }
12743
- chmodSync(schemaAuditHookPath, 493);
12912
+ chmodSync2(schemaAuditHookPath, 493);
12744
12913
  return {
12745
12914
  guardPath,
12746
12915
  hookPath,
@@ -12858,8 +13027,8 @@ function guardrailsCommand() {
12858
13027
  // src/commands/doctor.ts
12859
13028
  import { existsSync as existsSync3 } from "node:fs";
12860
13029
  import { copyFile, mkdir as mkdir4, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
12861
- import { homedir as homedir4 } from "node:os";
12862
- import { dirname as dirname4, join as join6 } from "node:path";
13030
+ import { homedir as homedir5 } from "node:os";
13031
+ import { dirname as dirname4, join as join7 } from "node:path";
12863
13032
 
12864
13033
  // src/lib/codex-config.ts
12865
13034
  import { execFileSync as execFileSync5 } from "node:child_process";
@@ -14390,7 +14559,7 @@ async function checkMcp(auth) {
14390
14559
  }
14391
14560
  }
14392
14561
  function defaultCodexConfigPath() {
14393
- return join6(homedir4(), ".codex", "config.toml");
14562
+ return join7(homedir5(), ".codex", "config.toml");
14394
14563
  }
14395
14564
  function codexIssuesToChecks(issues) {
14396
14565
  return issues.map((issue) => {
@@ -14528,12 +14697,12 @@ function claudeCommand() {
14528
14697
  isTTY: isTTY(),
14529
14698
  noColor: g.noColor
14530
14699
  });
14531
- const home = homedir4();
14532
- const gaDir = join6(home, ".great-arrow");
14533
- const userConfigPath = join6(home, ".claude.json");
14534
- const settingsPath = join6(home, ".claude", "settings.json");
14535
- const hookConfigPath = join6(gaDir, "hook-config.json");
14536
- const glanceHookPath = join6(gaDir, "claude-glance-hook.mjs");
14700
+ const home = homedir5();
14701
+ const gaDir = join7(home, ".great-arrow");
14702
+ const userConfigPath = join7(home, ".claude.json");
14703
+ const settingsPath = join7(home, ".claude", "settings.json");
14704
+ const hookConfigPath = join7(gaDir, "hook-config.json");
14705
+ const glanceHookPath = join7(gaDir, "claude-glance-hook.mjs");
14537
14706
  const checks = [];
14538
14707
  const repairs = [];
14539
14708
  const backups = [];
@@ -14741,8 +14910,8 @@ function claudeCommand() {
14741
14910
  file: "/Library/Application Support/ClaudeCode/managed-settings.json"
14742
14911
  },
14743
14912
  { scope: "user", file: settingsPath },
14744
- { scope: "project", file: join6(cwd, ".claude", "settings.json") },
14745
- { scope: "local", file: join6(cwd, ".claude", "settings.local.json") }
14913
+ { scope: "project", file: join7(cwd, ".claude", "settings.json") },
14914
+ { scope: "local", file: join7(cwd, ".claude", "settings.local.json") }
14746
14915
  ];
14747
14916
  const configured = [];
14748
14917
  for (const { scope, file } of scopes) {
@@ -14811,12 +14980,12 @@ function claudeCommand() {
14811
14980
  });
14812
14981
  }
14813
14982
  function defaultJsonClientConfigs() {
14814
- const home = homedir4();
14983
+ const home = homedir5();
14815
14984
  return [
14816
- { name: "Claude Code", path: join6(home, ".claude.json") },
14817
- { name: "Cursor", path: join6(home, ".cursor", "mcp.json") },
14818
- { name: "Kiro", path: join6(home, ".kiro", "settings", "mcp.json") },
14819
- { name: "Gemini CLI", path: join6(home, ".gemini", "settings.json") }
14985
+ { name: "Claude Code", path: join7(home, ".claude.json") },
14986
+ { name: "Cursor", path: join7(home, ".cursor", "mcp.json") },
14987
+ { name: "Kiro", path: join7(home, ".kiro", "settings", "mcp.json") },
14988
+ { name: "Gemini CLI", path: join7(home, ".gemini", "settings.json") }
14820
14989
  ];
14821
14990
  }
14822
14991
  function clientsCommand() {
@@ -15462,12 +15631,12 @@ async function pathExists(path10) {
15462
15631
  }
15463
15632
  }
15464
15633
  async function installCodexConfig(apiUrl, token, repoUrl) {
15465
- const { homedir: homedir6 } = await import("node:os");
15466
- const { join: join8 } = await import("node:path");
15634
+ const { homedir: homedir7 } = await import("node:os");
15635
+ const { join: join9 } = await import("node:path");
15467
15636
  const { chmod, copyFile: copyFile2, mkdir: mkdir6, readFile: readFile5, writeFile: writeFile5 } = await import("node:fs/promises");
15468
- const codexDir = join8(homedir6(), ".codex");
15469
- const configPath = join8(codexDir, "config.toml");
15470
- const envPath = join8(codexDir, ".env");
15637
+ const codexDir = join9(homedir7(), ".codex");
15638
+ const configPath = join9(codexDir, "config.toml");
15639
+ const envPath = join9(codexDir, ".env");
15471
15640
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15472
15641
  await mkdir6(codexDir, { recursive: true });
15473
15642
  const existingConfig = await pathExists(configPath) ? await readFile5(configPath, "utf8") : "";
@@ -15766,11 +15935,11 @@ var installCommand = new Command("install").description("Generate MCP configurat
15766
15935
  process.stdout.write(configJson2 + "\n");
15767
15936
  return;
15768
15937
  }
15769
- const { homedir: homedir6 } = await import("node:os");
15770
- const { join: join9 } = await import("node:path");
15938
+ const { homedir: homedir7 } = await import("node:os");
15939
+ const { join: join10 } = await import("node:path");
15771
15940
  const { mkdir: mkdir6, readFile: readFile5, writeFile: writeFile6 } = await import("node:fs/promises");
15772
- const geminiDir = join9(homedir6(), ".gemini");
15773
- const mcpPath = join9(geminiDir, "settings.json");
15941
+ const geminiDir = join10(homedir7(), ".gemini");
15942
+ const mcpPath = join10(geminiDir, "settings.json");
15774
15943
  await mkdir6(geminiDir, { recursive: true });
15775
15944
  let existingConfig = {};
15776
15945
  try {
@@ -15792,14 +15961,14 @@ var installCommand = new Command("install").description("Generate MCP configurat
15792
15961
  "utf-8"
15793
15962
  );
15794
15963
  fmt.success(`Wrote gemini MCP config to ${mcpPath}`);
15795
- const agyMcpDir = join9(
15964
+ const agyMcpDir = join10(
15796
15965
  geminiDir,
15797
15966
  "antigravity-cli",
15798
15967
  "mcp",
15799
15968
  MCP_SERVER_NAME
15800
15969
  );
15801
15970
  await mkdir6(agyMcpDir, { recursive: true });
15802
- const instructionsPath = join9(agyMcpDir, "instructions.md");
15971
+ const instructionsPath = join10(agyMcpDir, "instructions.md");
15803
15972
  const instructionsContent = `You are connected to greatarrow.ai - the user's private workspace memory + integrations brain.
15804
15973
 
15805
15974
  CRITICAL DIRECTIVE (MANDATORY STANDARD):
@@ -15831,8 +16000,8 @@ Always assume full autonomy to leverage these tools to their maximum extent.`;
15831
16000
  return;
15832
16001
  }
15833
16002
  const { writeFile: writeFile5 } = await import("node:fs/promises");
15834
- const { join: join8 } = await import("node:path");
15835
- const outputPath = join8(process.cwd(), ".mcp.json");
16003
+ const { join: join9 } = await import("node:path");
16004
+ const outputPath = join9(process.cwd(), ".mcp.json");
15836
16005
  await writeFile5(outputPath, configJson + "\n", { mode: 384 });
15837
16006
  fmt.success(`Wrote ${clientName} MCP config to .mcp.json`);
15838
16007
  if (await mcpJsonAtRiskOfCommit(process.cwd())) {
@@ -15863,13 +16032,13 @@ async function readKeychainOnly() {
15863
16032
  }
15864
16033
  }
15865
16034
  async function resolveProxyScript(override) {
15866
- const { homedir: homedir6 } = await import("node:os");
15867
- const { join: join8 } = await import("node:path");
16035
+ const { homedir: homedir7 } = await import("node:os");
16036
+ const { join: join9 } = await import("node:path");
15868
16037
  const candidates = [
15869
16038
  override,
15870
16039
  process.env.GAD_PROXY_SCRIPT,
15871
- join8(homedir6(), ".great-arrow", "mcp-server.mjs"),
15872
- join8(process.cwd(), "scripts", "mcp-server.mjs")
16040
+ join9(homedir7(), ".great-arrow", "mcp-server.mjs"),
16041
+ join9(process.cwd(), "scripts", "mcp-server.mjs")
15873
16042
  ].filter((c) => Boolean(c));
15874
16043
  for (const candidate of candidates) {
15875
16044
  if (await pathExists(candidate)) return candidate;
@@ -15893,13 +16062,13 @@ async function runProxyDiagnostics(scriptPath) {
15893
16062
  }
15894
16063
  }
15895
16064
  async function findLegacyStores(cwd) {
15896
- const { join: join8 } = await import("node:path");
16065
+ const { join: join9 } = await import("node:path");
15897
16066
  const { readdir } = await import("node:fs/promises");
15898
16067
  const dirs = /* @__PURE__ */ new Set([cwd]);
15899
16068
  try {
15900
- const worktreeRoot = join8(cwd, ".claude", "worktrees");
16069
+ const worktreeRoot = join9(cwd, ".claude", "worktrees");
15901
16070
  for (const entry of await readdir(worktreeRoot, { withFileTypes: true })) {
15902
- if (entry.isDirectory()) dirs.add(join8(worktreeRoot, entry.name));
16071
+ if (entry.isDirectory()) dirs.add(join9(worktreeRoot, entry.name));
15903
16072
  }
15904
16073
  } catch (err) {
15905
16074
  debugSwallow("mcp.findLegacyStores(worktrees)", err);
@@ -15921,18 +16090,18 @@ async function findLegacyStores(cwd) {
15921
16090
  }
15922
16091
  const stores = [];
15923
16092
  for (const dir of dirs) {
15924
- const path10 = join8(dir, ".mcp", OAUTH_CRED_FILENAME);
16093
+ const path10 = join9(dir, ".mcp", OAUTH_CRED_FILENAME);
15925
16094
  const raw = await readIfExists(path10);
15926
16095
  if (raw !== null) stores.push({ path: path10, raw });
15927
16096
  }
15928
16097
  return stores;
15929
16098
  }
15930
16099
  async function scanConnections() {
15931
- const { homedir: homedir6 } = await import("node:os");
15932
- const { join: join8 } = await import("node:path");
15933
- const home = homedir6();
16100
+ const { homedir: homedir7 } = await import("node:os");
16101
+ const { join: join9 } = await import("node:path");
16102
+ const home = homedir7();
15934
16103
  const hookSettings = await readIfExists(
15935
- join8(home, ".claude", "settings.json")
16104
+ join9(home, ".claude", "settings.json")
15936
16105
  );
15937
16106
  const hooksInstalled = hookSettings ? hasGreatArrowHooks(hookSettings) : false;
15938
16107
  const entries = [];
@@ -15965,16 +16134,16 @@ async function probeEndpoint() {
15965
16134
  }
15966
16135
  }
15967
16136
  async function applyMigration(plan) {
15968
- const { homedir: homedir6 } = await import("node:os");
15969
- const { join: join8 } = await import("node:path");
16137
+ const { homedir: homedir7 } = await import("node:os");
16138
+ const { join: join9 } = await import("node:path");
15970
16139
  const { mkdir: mkdir6, rename, writeFile: writeFile5 } = await import("node:fs/promises");
15971
16140
  const applied = [];
15972
- const dir = join8(homedir6(), ".great-arrow");
16141
+ const dir = join9(homedir7(), ".great-arrow");
15973
16142
  await mkdir6(dir, { recursive: true, mode: 448 });
15974
16143
  if (plan.seedFrom) {
15975
16144
  const raw = await readIfExists(plan.seedFrom);
15976
16145
  if (raw !== null) {
15977
- const target = join8(dir, OAUTH_CRED_FILENAME);
16146
+ const target = join9(dir, OAUTH_CRED_FILENAME);
15978
16147
  const tmp = `${target}.tmp`;
15979
16148
  await writeFile5(tmp, raw, { mode: 384 });
15980
16149
  await rename(tmp, target);
@@ -15984,7 +16153,7 @@ async function applyMigration(plan) {
15984
16153
  if (plan.syncKeychainToFile) {
15985
16154
  const raw = await readKeychainOnly();
15986
16155
  if (raw) {
15987
- const target = join8(dir, "credential");
16156
+ const target = join9(dir, "credential");
15988
16157
  const tmp = `${target}.tmp`;
15989
16158
  await writeFile5(tmp, raw, { mode: 384 });
15990
16159
  await rename(tmp, target);
@@ -16010,19 +16179,19 @@ var doctorSubcommand = new Command("doctor").description(
16010
16179
  noColor: false
16011
16180
  });
16012
16181
  const c = createColor(jsonFlag);
16013
- const { homedir: homedir6 } = await import("node:os");
16014
- const { join: join8 } = await import("node:path");
16015
- const home = homedir6();
16182
+ const { homedir: homedir7 } = await import("node:os");
16183
+ const { join: join9 } = await import("node:path");
16184
+ const home = homedir7();
16016
16185
  const proxyPath = await resolveProxyScript(options.proxy);
16017
16186
  const diagnostics = proxyPath ? await runProxyDiagnostics(proxyPath) : null;
16018
16187
  const entries = await scanConnections();
16019
16188
  const duplicates = detectDuplicates(entries);
16020
16189
  const keychain = await readKeychainOnly();
16021
16190
  const credentialFile = await readIfExists(
16022
- join8(home, ".great-arrow", "credential")
16191
+ join9(home, ".great-arrow", "credential")
16023
16192
  );
16024
16193
  const userStore = await readIfExists(
16025
- join8(home, ".great-arrow", OAUTH_CRED_FILENAME)
16194
+ join9(home, ".great-arrow", OAUTH_CRED_FILENAME)
16026
16195
  );
16027
16196
  const legacyStores = await findLegacyStores(process.cwd());
16028
16197
  const findings = [
@@ -16447,8 +16616,8 @@ function askCommand() {
16447
16616
 
16448
16617
  // src/lib/glance-footer.ts
16449
16618
  import { readFile as readFile4, writeFile as writeFile4, mkdir as mkdir5 } from "node:fs/promises";
16450
- import { homedir as homedir5 } from "node:os";
16451
- import { join as join7, dirname as dirname5 } from "node:path";
16619
+ import { homedir as homedir6 } from "node:os";
16620
+ import { join as join8, dirname as dirname5 } from "node:path";
16452
16621
 
16453
16622
  // src/lib/seen.ts
16454
16623
  var SEEN_TIMEOUT_MS = 1e3;
@@ -16835,7 +17004,7 @@ function shouldRequestTip(input) {
16835
17004
  return now - state.lastTipAt >= TIP_THROTTLE_INTERVAL_MS;
16836
17005
  }
16837
17006
  function getGlanceStatePath() {
16838
- return join7(homedir5(), ".gad", "glance-state.json");
17007
+ return join8(homedir6(), ".gad", "glance-state.json");
16839
17008
  }
16840
17009
  async function loadGlanceState() {
16841
17010
  try {
@@ -19082,9 +19251,9 @@ node_default(Temp.purgeSyncAll);
19082
19251
  var temp_default = Temp;
19083
19252
 
19084
19253
  // node_modules/.pnpm/atomically@2.1.1/node_modules/atomically/dist/index.js
19085
- function writeFileSync3(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
19254
+ function writeFileSync4(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
19086
19255
  if (isString(options))
19087
- return writeFileSync3(filePath, data, { encoding: options });
19256
+ return writeFileSync4(filePath, data, { encoding: options });
19088
19257
  const timeout2 = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
19089
19258
  const retryOptions = { timeout: timeout2 };
19090
19259
  let tempDisposer = null;
@@ -19411,7 +19580,7 @@ var Configstore = class {
19411
19580
  }
19412
19581
  if (error.name === "SyntaxError") {
19413
19582
  if (this._clearInvalidConfig) {
19414
- writeFileSync3(this._path, "", writeFileOptions);
19583
+ writeFileSync4(this._path, "", writeFileOptions);
19415
19584
  return {};
19416
19585
  }
19417
19586
  throw error;
@@ -19423,7 +19592,7 @@ var Configstore = class {
19423
19592
  set all(value) {
19424
19593
  try {
19425
19594
  import_graceful_fs.default.mkdirSync(path5.dirname(this._path), mkdirOptions);
19426
- writeFileSync3(this._path, JSON.stringify(value, void 0, " "), writeFileOptions);
19595
+ writeFileSync4(this._path, JSON.stringify(value, void 0, " "), writeFileOptions);
19427
19596
  } catch (error) {
19428
19597
  handlePermissionError(error);
19429
19598
  }
@@ -22130,6 +22299,7 @@ void captureCliEvent({
22130
22299
  });
22131
22300
  program2.addCommand(login_default);
22132
22301
  program2.addCommand(logoutCommand);
22302
+ program2.addCommand(authCommand());
22133
22303
  program2.addCommand(whoamiCommand());
22134
22304
  program2.addCommand(memoryCommand);
22135
22305
  program2.addCommand(workspace_default);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@great-arrow/cli",
3
- "version": "0.1.46",
3
+ "version": "0.1.48",
4
4
  "description": "GreatArrow.ai CLI tool",
5
5
  "type": "module",
6
6
  "bin": {