@great-arrow/cli 0.1.46 → 0.1.47

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 +249 -138
  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.47";
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 = {};
@@ -9849,11 +9894,13 @@ function toCredential(env4, apiUrl, clientId, now) {
9849
9894
  apiUrl: trimSlash2(apiUrl)
9850
9895
  };
9851
9896
  }
9852
- async function postToken(apiUrl, form, fetchImpl) {
9897
+ var TOKEN_POST_TIMEOUT_MS = 8e3;
9898
+ async function postToken(apiUrl, form, fetchImpl, timeoutMs = TOKEN_POST_TIMEOUT_MS) {
9853
9899
  const res = await fetchImpl(`${trimSlash2(apiUrl)}/api/oauth/token`, {
9854
9900
  method: "POST",
9855
9901
  headers: { "content-type": "application/x-www-form-urlencoded" },
9856
- body: new URLSearchParams(form).toString()
9902
+ body: new URLSearchParams(form).toString(),
9903
+ signal: AbortSignal.timeout(timeoutMs)
9857
9904
  });
9858
9905
  const json = await res.json().catch(() => ({}));
9859
9906
  return { ok: res.ok, status: res.status, json };
@@ -9894,7 +9941,8 @@ async function revokeToken(opts) {
9894
9941
  const res = await doFetch(`${trimSlash2(opts.apiUrl)}/api/oauth/revoke`, {
9895
9942
  method: "POST",
9896
9943
  headers: { "content-type": "application/x-www-form-urlencoded" },
9897
- body: new URLSearchParams(form).toString()
9944
+ body: new URLSearchParams(form).toString(),
9945
+ signal: AbortSignal.timeout(TOKEN_POST_TIMEOUT_MS)
9898
9946
  });
9899
9947
  return res.ok;
9900
9948
  } catch {
@@ -9910,7 +9958,8 @@ async function refreshTokens(opts) {
9910
9958
  client_id: opts.clientId,
9911
9959
  refresh_token: opts.refreshToken
9912
9960
  },
9913
- opts.fetchImpl ?? fetch
9961
+ opts.fetchImpl ?? fetch,
9962
+ opts.timeoutMs
9914
9963
  );
9915
9964
  if (!ok) {
9916
9965
  const desc = String(json.error_description ?? json.error ?? "unknown");
@@ -10284,6 +10333,7 @@ var loginCommand = new Command("login").description("Authenticate the CLI with y
10284
10333
  process.exitCode = 1;
10285
10334
  return;
10286
10335
  }
10336
+ writeCliLocator();
10287
10337
  config.cli_client_id = result.clientId;
10288
10338
  config.cli_redirect_uri = result.redirectUri;
10289
10339
  config.api_url = DEFAULT_API_URL;
@@ -10303,52 +10353,22 @@ var loginCommand = new Command("login").description("Authenticate the CLI with y
10303
10353
  );
10304
10354
  var login_default = loginCommand;
10305
10355
 
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
10356
  // src/lib/auth.ts
10337
10357
  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";
10358
+ import { homedir as homedir4 } from "node:os";
10359
+ import { join as join4 } from "node:path";
10340
10360
  var REFRESH_SKEW_MS = 12e4;
10341
10361
  var inflightRefresh = null;
10342
10362
  var REFRESH_LOCK_STALE_MS = 15e3;
10343
10363
  var REFRESH_LOCK_WAIT_MS = 5e3;
10344
10364
  var REFRESH_LOCK_POLL_MS = 100;
10345
10365
  function refreshLockPath() {
10346
- return join3(homedir3(), ".great-arrow", "refresh.lock");
10366
+ return join4(homedir4(), ".great-arrow", "refresh.lock");
10347
10367
  }
10348
10368
  async function acquireRefreshLock() {
10349
10369
  const path10 = refreshLockPath();
10350
10370
  try {
10351
- await mkdir3(join3(homedir3(), ".great-arrow"), { recursive: true });
10371
+ await mkdir3(join4(homedir4(), ".great-arrow"), { recursive: true });
10352
10372
  } catch (err) {
10353
10373
  debugSwallow("refresh-lock.mkdir", err);
10354
10374
  }
@@ -10382,7 +10402,7 @@ async function releaseRefreshLock() {
10382
10402
  debugSwallow("refresh-lock.release", err);
10383
10403
  }
10384
10404
  }
10385
- async function rotatedByAnotherProcess(ours) {
10405
+ async function storedPairOtherThan(ours) {
10386
10406
  let stored = null;
10387
10407
  try {
10388
10408
  stored = await loadStoredCredential();
@@ -10390,8 +10410,11 @@ async function rotatedByAnotherProcess(ours) {
10390
10410
  debugSwallow("refresh.reread", err);
10391
10411
  }
10392
10412
  if (stored?.kind !== "oauth") return null;
10393
- const theirs = stored.credential;
10394
- if (theirs.refreshToken === ours.refreshToken) return null;
10413
+ return stored.credential.refreshToken === ours.refreshToken ? null : stored.credential;
10414
+ }
10415
+ async function rotatedByAnotherProcess(ours) {
10416
+ const theirs = await storedPairOtherThan(ours);
10417
+ if (!theirs) return null;
10395
10418
  if (Date.now() >= theirs.accessExpiresAt - REFRESH_SKEW_MS) return null;
10396
10419
  return theirs;
10397
10420
  }
@@ -10439,6 +10462,27 @@ async function resolveAuth() {
10439
10462
  "Run 'gad login' to authenticate, or set the GAD_TOKEN environment variable."
10440
10463
  );
10441
10464
  }
10465
+ var NotOAuthCredential = class extends Error {
10466
+ code = "not_oauth";
10467
+ };
10468
+ async function getFreshOAuthAccessToken() {
10469
+ const stored = await loadStoredCredential();
10470
+ if (!stored) {
10471
+ throw authError(
10472
+ "Session expired. Not signed in.",
10473
+ "Run 'gad login' to authenticate."
10474
+ );
10475
+ }
10476
+ if (stored.kind !== "oauth") {
10477
+ throw new NotOAuthCredential("stored credential is not OAuth");
10478
+ }
10479
+ const fresh = await ensureFreshAccessToken(stored.credential);
10480
+ return {
10481
+ accessToken: fresh.accessToken,
10482
+ accessExpiresAt: fresh.accessExpiresAt,
10483
+ apiUrl: fresh.apiUrl
10484
+ };
10485
+ }
10442
10486
  async function ensureFreshAccessToken(credential) {
10443
10487
  if (Date.now() < credential.accessExpiresAt - REFRESH_SKEW_MS) {
10444
10488
  return credential;
@@ -10468,6 +10512,8 @@ async function doRefresh(credential) {
10468
10512
  return refreshed;
10469
10513
  } catch (err) {
10470
10514
  if (err instanceof RefreshRejected) {
10515
+ const theirs = await storedPairOtherThan(credential);
10516
+ if (theirs) return theirs;
10471
10517
  await clearStoredCredential();
10472
10518
  throw sessionExpired();
10473
10519
  }
@@ -10480,6 +10526,70 @@ function sessionExpired() {
10480
10526
  return authError("Session expired.", "Run 'gad login' to re-authenticate.");
10481
10527
  }
10482
10528
 
10529
+ // src/commands/auth.ts
10530
+ var EXIT_SESSION_EXPIRED = 2;
10531
+ var EXIT_NOT_OAUTH = 3;
10532
+ function authCommand() {
10533
+ const cmd = new Command("auth").description("Authentication helpers");
10534
+ 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) => {
10535
+ try {
10536
+ const t = await getFreshOAuthAccessToken();
10537
+ writeCliLocator();
10538
+ process.stdout.write(
10539
+ options.json ? `${JSON.stringify(t)}
10540
+ ` : `${t.accessToken}
10541
+ `
10542
+ );
10543
+ } catch (error) {
10544
+ if (error instanceof NotOAuthCredential) {
10545
+ process.stderr.write(`${error.message}
10546
+ `);
10547
+ process.exitCode = EXIT_NOT_OAUTH;
10548
+ return;
10549
+ }
10550
+ if (error instanceof CliError && error.code === 1) {
10551
+ const hint = error.hint ? ` ${error.hint}` : "";
10552
+ process.stderr.write(`${error.message}${hint}
10553
+ `);
10554
+ process.exitCode = EXIT_SESSION_EXPIRED;
10555
+ return;
10556
+ }
10557
+ throw error;
10558
+ }
10559
+ });
10560
+ return cmd;
10561
+ }
10562
+
10563
+ // src/commands/logout.ts
10564
+ var logoutCommand = new Command("logout").description("Remove stored authentication credentials").action(async (_options, cmd) => {
10565
+ const parent = cmd.parent;
10566
+ const json = parent?.opts().json ?? false;
10567
+ const quiet = parent?.opts().quiet ?? false;
10568
+ const noColor = parent?.opts().noColor ?? false;
10569
+ const fmt = createFormatter({
10570
+ json,
10571
+ quiet,
10572
+ isTTY: isTTY(),
10573
+ noColor
10574
+ });
10575
+ const stored = await loadStoredCredential();
10576
+ if (stored?.kind === "oauth") {
10577
+ await revokeToken({
10578
+ apiUrl: stored.credential.apiUrl,
10579
+ clientId: stored.credential.clientId,
10580
+ token: stored.credential.refreshToken,
10581
+ tokenTypeHint: "refresh_token"
10582
+ });
10583
+ }
10584
+ await clearStoredCredential();
10585
+ const config = await loadConfig();
10586
+ if (config.token) {
10587
+ config.token = "";
10588
+ await saveConfig(config);
10589
+ }
10590
+ fmt.success("Logged out successfully.");
10591
+ });
10592
+
10483
10593
  // src/lib/client.ts
10484
10594
  import { execSync } from "node:child_process";
10485
10595
 
@@ -12302,8 +12412,8 @@ var work_default = workCommand;
12302
12412
 
12303
12413
  // src/commands/worktree.ts
12304
12414
  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";
12415
+ import { existsSync, readFileSync as readFileSync2, statSync, writeFileSync as writeFileSync2 } from "node:fs";
12416
+ import { basename, dirname as dirname3, isAbsolute, join as join5, resolve } from "node:path";
12307
12417
  var realExec = (cmd, args, opts) => {
12308
12418
  const out = execFileSync3(cmd, args, {
12309
12419
  cwd: opts?.cwd,
@@ -12325,9 +12435,9 @@ function parseDefaultBranch(symbolicRef) {
12325
12435
  return match ? match[1] : null;
12326
12436
  }
12327
12437
  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";
12438
+ if (existsSync(join5(repoRoot, "pnpm-lock.yaml"))) return "pnpm";
12439
+ if (existsSync(join5(repoRoot, "bun.lockb"))) return "bun";
12440
+ if (existsSync(join5(repoRoot, "yarn.lock"))) return "yarn";
12331
12441
  return "npm";
12332
12442
  }
12333
12443
  function defaultWorktreeDir(repoRoot, branch) {
@@ -12335,7 +12445,7 @@ function defaultWorktreeDir(repoRoot, branch) {
12335
12445
  /[^A-Za-z0-9._-]+/g,
12336
12446
  "-"
12337
12447
  );
12338
- return join4(dirname3(repoRoot), `${basename(repoRoot)}-${slug}`);
12448
+ return join5(dirname3(repoRoot), `${basename(repoRoot)}-${slug}`);
12339
12449
  }
12340
12450
  var AGENT_PROCESS_RE = /(^|\/)(claude|kiro|cursor|codex)/i;
12341
12451
  function psField(pid, field) {
@@ -12358,7 +12468,7 @@ function findAgentPid(startPid) {
12358
12468
  return null;
12359
12469
  }
12360
12470
  function ownGitDir(worktreeDir) {
12361
- const dotGit = join4(worktreeDir, ".git");
12471
+ const dotGit = join5(worktreeDir, ".git");
12362
12472
  if (!existsSync(dotGit)) return null;
12363
12473
  if (statSync(dotGit).isDirectory()) return dotGit;
12364
12474
  const match = readFileSync2(dotGit, "utf8").trim().match(/^gitdir:\s*(.+)$/m);
@@ -12372,8 +12482,8 @@ function stampWorktreeOwner(worktreeDir) {
12372
12482
  if (!gitDir) return null;
12373
12483
  const ownerPid = findAgentPid(process.ppid);
12374
12484
  if (!ownerPid) return null;
12375
- writeFileSync(
12376
- join4(gitDir, "agent-owner.json"),
12485
+ writeFileSync2(
12486
+ join5(gitDir, "agent-owner.json"),
12377
12487
  JSON.stringify(
12378
12488
  {
12379
12489
  ownerPid,
@@ -12467,7 +12577,7 @@ function createTaskWorktree(opts) {
12467
12577
  const stampedPid = opts.stamp === false ? null : stampWorktreeOwner(dir);
12468
12578
  const packageManager = detectPackageManager(repoRoot);
12469
12579
  let installed = false;
12470
- if (opts.install !== false && existsSync(join4(repoRoot, "package.json"))) {
12580
+ if (opts.install !== false && existsSync(join5(repoRoot, "package.json"))) {
12471
12581
  exec2(packageManager, ["install"], { cwd: dir, inherit: true });
12472
12582
  installed = true;
12473
12583
  }
@@ -12530,13 +12640,13 @@ var worktree_default = worktreeCommand;
12530
12640
  // src/commands/install-hooks.ts
12531
12641
  import { execFileSync as execFileSync4 } from "node:child_process";
12532
12642
  import {
12533
- chmodSync,
12643
+ chmodSync as chmodSync2,
12534
12644
  existsSync as existsSync2,
12535
- mkdirSync,
12645
+ mkdirSync as mkdirSync2,
12536
12646
  readFileSync as readFileSync3,
12537
- writeFileSync as writeFileSync2
12647
+ writeFileSync as writeFileSync3
12538
12648
  } from "node:fs";
12539
- import { isAbsolute as isAbsolute2, join as join5, resolve as resolve2 } from "node:path";
12649
+ import { isAbsolute as isAbsolute2, join as join6, resolve as resolve2 } from "node:path";
12540
12650
  var GUARD_MARKER = "# gad-agent-guard v1";
12541
12651
  var SCHEMA_AUDIT_MARKER = "# gad-schema-audit v1";
12542
12652
  var GUARD_FILE = "gad-commit-guard.mjs";
@@ -12719,28 +12829,28 @@ function installCommitGuard(opts) {
12719
12829
  );
12720
12830
  }
12721
12831
  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");
12832
+ const hooksDir = join6(commonDir, "hooks");
12833
+ mkdirSync2(hooksDir, { recursive: true });
12834
+ const guardPath = join6(hooksDir, GUARD_FILE);
12835
+ writeFileSync3(guardPath, buildGuardScript());
12836
+ chmodSync2(guardPath, 493);
12837
+ const huskyHook = join6(repoRoot, ".husky", "pre-commit");
12838
+ const hookPath = existsSync2(huskyHook) ? huskyHook : join6(hooksDir, "pre-commit");
12729
12839
  const existing = existsSync2(hookPath) ? readFileSync3(hookPath, "utf8") : null;
12730
12840
  const plan = planHookInstall(existing);
12731
12841
  if (plan.action !== "noop") {
12732
- writeFileSync2(hookPath, plan.content);
12842
+ writeFileSync3(hookPath, plan.content);
12733
12843
  }
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");
12844
+ chmodSync2(hookPath, 493);
12845
+ const huskyDir = join6(repoRoot, ".husky");
12846
+ const huskyPrePush = join6(huskyDir, "pre-push");
12847
+ const schemaAuditHookPath = existsSync2(huskyPrePush) || existsSync2(huskyDir) ? huskyPrePush : join6(hooksDir, "pre-push");
12738
12848
  const existingSchemaAuditHook = existsSync2(schemaAuditHookPath) ? readFileSync3(schemaAuditHookPath, "utf8") : null;
12739
12849
  const schemaAuditPlan = planSchemaAuditHookInstall(existingSchemaAuditHook);
12740
12850
  if (schemaAuditPlan.action !== "noop") {
12741
- writeFileSync2(schemaAuditHookPath, schemaAuditPlan.content);
12851
+ writeFileSync3(schemaAuditHookPath, schemaAuditPlan.content);
12742
12852
  }
12743
- chmodSync(schemaAuditHookPath, 493);
12853
+ chmodSync2(schemaAuditHookPath, 493);
12744
12854
  return {
12745
12855
  guardPath,
12746
12856
  hookPath,
@@ -12858,8 +12968,8 @@ function guardrailsCommand() {
12858
12968
  // src/commands/doctor.ts
12859
12969
  import { existsSync as existsSync3 } from "node:fs";
12860
12970
  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";
12971
+ import { homedir as homedir5 } from "node:os";
12972
+ import { dirname as dirname4, join as join7 } from "node:path";
12863
12973
 
12864
12974
  // src/lib/codex-config.ts
12865
12975
  import { execFileSync as execFileSync5 } from "node:child_process";
@@ -14390,7 +14500,7 @@ async function checkMcp(auth) {
14390
14500
  }
14391
14501
  }
14392
14502
  function defaultCodexConfigPath() {
14393
- return join6(homedir4(), ".codex", "config.toml");
14503
+ return join7(homedir5(), ".codex", "config.toml");
14394
14504
  }
14395
14505
  function codexIssuesToChecks(issues) {
14396
14506
  return issues.map((issue) => {
@@ -14528,12 +14638,12 @@ function claudeCommand() {
14528
14638
  isTTY: isTTY(),
14529
14639
  noColor: g.noColor
14530
14640
  });
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");
14641
+ 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");
14537
14647
  const checks = [];
14538
14648
  const repairs = [];
14539
14649
  const backups = [];
@@ -14741,8 +14851,8 @@ function claudeCommand() {
14741
14851
  file: "/Library/Application Support/ClaudeCode/managed-settings.json"
14742
14852
  },
14743
14853
  { scope: "user", file: settingsPath },
14744
- { scope: "project", file: join6(cwd, ".claude", "settings.json") },
14745
- { scope: "local", file: join6(cwd, ".claude", "settings.local.json") }
14854
+ { scope: "project", file: join7(cwd, ".claude", "settings.json") },
14855
+ { scope: "local", file: join7(cwd, ".claude", "settings.local.json") }
14746
14856
  ];
14747
14857
  const configured = [];
14748
14858
  for (const { scope, file } of scopes) {
@@ -14811,12 +14921,12 @@ function claudeCommand() {
14811
14921
  });
14812
14922
  }
14813
14923
  function defaultJsonClientConfigs() {
14814
- const home = homedir4();
14924
+ const home = homedir5();
14815
14925
  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") }
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") }
14820
14930
  ];
14821
14931
  }
14822
14932
  function clientsCommand() {
@@ -15462,12 +15572,12 @@ async function pathExists(path10) {
15462
15572
  }
15463
15573
  }
15464
15574
  async function installCodexConfig(apiUrl, token, repoUrl) {
15465
- const { homedir: homedir6 } = await import("node:os");
15466
- const { join: join8 } = await import("node:path");
15575
+ const { homedir: homedir7 } = await import("node:os");
15576
+ const { join: join9 } = await import("node:path");
15467
15577
  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");
15578
+ const codexDir = join9(homedir7(), ".codex");
15579
+ const configPath = join9(codexDir, "config.toml");
15580
+ const envPath = join9(codexDir, ".env");
15471
15581
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
15472
15582
  await mkdir6(codexDir, { recursive: true });
15473
15583
  const existingConfig = await pathExists(configPath) ? await readFile5(configPath, "utf8") : "";
@@ -15766,11 +15876,11 @@ var installCommand = new Command("install").description("Generate MCP configurat
15766
15876
  process.stdout.write(configJson2 + "\n");
15767
15877
  return;
15768
15878
  }
15769
- const { homedir: homedir6 } = await import("node:os");
15770
- const { join: join9 } = await import("node:path");
15879
+ const { homedir: homedir7 } = await import("node:os");
15880
+ const { join: join10 } = await import("node:path");
15771
15881
  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");
15882
+ const geminiDir = join10(homedir7(), ".gemini");
15883
+ const mcpPath = join10(geminiDir, "settings.json");
15774
15884
  await mkdir6(geminiDir, { recursive: true });
15775
15885
  let existingConfig = {};
15776
15886
  try {
@@ -15792,14 +15902,14 @@ var installCommand = new Command("install").description("Generate MCP configurat
15792
15902
  "utf-8"
15793
15903
  );
15794
15904
  fmt.success(`Wrote gemini MCP config to ${mcpPath}`);
15795
- const agyMcpDir = join9(
15905
+ const agyMcpDir = join10(
15796
15906
  geminiDir,
15797
15907
  "antigravity-cli",
15798
15908
  "mcp",
15799
15909
  MCP_SERVER_NAME
15800
15910
  );
15801
15911
  await mkdir6(agyMcpDir, { recursive: true });
15802
- const instructionsPath = join9(agyMcpDir, "instructions.md");
15912
+ const instructionsPath = join10(agyMcpDir, "instructions.md");
15803
15913
  const instructionsContent = `You are connected to greatarrow.ai - the user's private workspace memory + integrations brain.
15804
15914
 
15805
15915
  CRITICAL DIRECTIVE (MANDATORY STANDARD):
@@ -15831,8 +15941,8 @@ Always assume full autonomy to leverage these tools to their maximum extent.`;
15831
15941
  return;
15832
15942
  }
15833
15943
  const { writeFile: writeFile5 } = await import("node:fs/promises");
15834
- const { join: join8 } = await import("node:path");
15835
- const outputPath = join8(process.cwd(), ".mcp.json");
15944
+ const { join: join9 } = await import("node:path");
15945
+ const outputPath = join9(process.cwd(), ".mcp.json");
15836
15946
  await writeFile5(outputPath, configJson + "\n", { mode: 384 });
15837
15947
  fmt.success(`Wrote ${clientName} MCP config to .mcp.json`);
15838
15948
  if (await mcpJsonAtRiskOfCommit(process.cwd())) {
@@ -15863,13 +15973,13 @@ async function readKeychainOnly() {
15863
15973
  }
15864
15974
  }
15865
15975
  async function resolveProxyScript(override) {
15866
- const { homedir: homedir6 } = await import("node:os");
15867
- const { join: join8 } = await import("node:path");
15976
+ const { homedir: homedir7 } = await import("node:os");
15977
+ const { join: join9 } = await import("node:path");
15868
15978
  const candidates = [
15869
15979
  override,
15870
15980
  process.env.GAD_PROXY_SCRIPT,
15871
- join8(homedir6(), ".great-arrow", "mcp-server.mjs"),
15872
- join8(process.cwd(), "scripts", "mcp-server.mjs")
15981
+ join9(homedir7(), ".great-arrow", "mcp-server.mjs"),
15982
+ join9(process.cwd(), "scripts", "mcp-server.mjs")
15873
15983
  ].filter((c) => Boolean(c));
15874
15984
  for (const candidate of candidates) {
15875
15985
  if (await pathExists(candidate)) return candidate;
@@ -15893,13 +16003,13 @@ async function runProxyDiagnostics(scriptPath) {
15893
16003
  }
15894
16004
  }
15895
16005
  async function findLegacyStores(cwd) {
15896
- const { join: join8 } = await import("node:path");
16006
+ const { join: join9 } = await import("node:path");
15897
16007
  const { readdir } = await import("node:fs/promises");
15898
16008
  const dirs = /* @__PURE__ */ new Set([cwd]);
15899
16009
  try {
15900
- const worktreeRoot = join8(cwd, ".claude", "worktrees");
16010
+ const worktreeRoot = join9(cwd, ".claude", "worktrees");
15901
16011
  for (const entry of await readdir(worktreeRoot, { withFileTypes: true })) {
15902
- if (entry.isDirectory()) dirs.add(join8(worktreeRoot, entry.name));
16012
+ if (entry.isDirectory()) dirs.add(join9(worktreeRoot, entry.name));
15903
16013
  }
15904
16014
  } catch (err) {
15905
16015
  debugSwallow("mcp.findLegacyStores(worktrees)", err);
@@ -15921,18 +16031,18 @@ async function findLegacyStores(cwd) {
15921
16031
  }
15922
16032
  const stores = [];
15923
16033
  for (const dir of dirs) {
15924
- const path10 = join8(dir, ".mcp", OAUTH_CRED_FILENAME);
16034
+ const path10 = join9(dir, ".mcp", OAUTH_CRED_FILENAME);
15925
16035
  const raw = await readIfExists(path10);
15926
16036
  if (raw !== null) stores.push({ path: path10, raw });
15927
16037
  }
15928
16038
  return stores;
15929
16039
  }
15930
16040
  async function scanConnections() {
15931
- const { homedir: homedir6 } = await import("node:os");
15932
- const { join: join8 } = await import("node:path");
15933
- const home = homedir6();
16041
+ const { homedir: homedir7 } = await import("node:os");
16042
+ const { join: join9 } = await import("node:path");
16043
+ const home = homedir7();
15934
16044
  const hookSettings = await readIfExists(
15935
- join8(home, ".claude", "settings.json")
16045
+ join9(home, ".claude", "settings.json")
15936
16046
  );
15937
16047
  const hooksInstalled = hookSettings ? hasGreatArrowHooks(hookSettings) : false;
15938
16048
  const entries = [];
@@ -15965,16 +16075,16 @@ async function probeEndpoint() {
15965
16075
  }
15966
16076
  }
15967
16077
  async function applyMigration(plan) {
15968
- const { homedir: homedir6 } = await import("node:os");
15969
- const { join: join8 } = await import("node:path");
16078
+ const { homedir: homedir7 } = await import("node:os");
16079
+ const { join: join9 } = await import("node:path");
15970
16080
  const { mkdir: mkdir6, rename, writeFile: writeFile5 } = await import("node:fs/promises");
15971
16081
  const applied = [];
15972
- const dir = join8(homedir6(), ".great-arrow");
16082
+ const dir = join9(homedir7(), ".great-arrow");
15973
16083
  await mkdir6(dir, { recursive: true, mode: 448 });
15974
16084
  if (plan.seedFrom) {
15975
16085
  const raw = await readIfExists(plan.seedFrom);
15976
16086
  if (raw !== null) {
15977
- const target = join8(dir, OAUTH_CRED_FILENAME);
16087
+ const target = join9(dir, OAUTH_CRED_FILENAME);
15978
16088
  const tmp = `${target}.tmp`;
15979
16089
  await writeFile5(tmp, raw, { mode: 384 });
15980
16090
  await rename(tmp, target);
@@ -15984,7 +16094,7 @@ async function applyMigration(plan) {
15984
16094
  if (plan.syncKeychainToFile) {
15985
16095
  const raw = await readKeychainOnly();
15986
16096
  if (raw) {
15987
- const target = join8(dir, "credential");
16097
+ const target = join9(dir, "credential");
15988
16098
  const tmp = `${target}.tmp`;
15989
16099
  await writeFile5(tmp, raw, { mode: 384 });
15990
16100
  await rename(tmp, target);
@@ -16010,19 +16120,19 @@ var doctorSubcommand = new Command("doctor").description(
16010
16120
  noColor: false
16011
16121
  });
16012
16122
  const c = createColor(jsonFlag);
16013
- const { homedir: homedir6 } = await import("node:os");
16014
- const { join: join8 } = await import("node:path");
16015
- const home = homedir6();
16123
+ const { homedir: homedir7 } = await import("node:os");
16124
+ const { join: join9 } = await import("node:path");
16125
+ const home = homedir7();
16016
16126
  const proxyPath = await resolveProxyScript(options.proxy);
16017
16127
  const diagnostics = proxyPath ? await runProxyDiagnostics(proxyPath) : null;
16018
16128
  const entries = await scanConnections();
16019
16129
  const duplicates = detectDuplicates(entries);
16020
16130
  const keychain = await readKeychainOnly();
16021
16131
  const credentialFile = await readIfExists(
16022
- join8(home, ".great-arrow", "credential")
16132
+ join9(home, ".great-arrow", "credential")
16023
16133
  );
16024
16134
  const userStore = await readIfExists(
16025
- join8(home, ".great-arrow", OAUTH_CRED_FILENAME)
16135
+ join9(home, ".great-arrow", OAUTH_CRED_FILENAME)
16026
16136
  );
16027
16137
  const legacyStores = await findLegacyStores(process.cwd());
16028
16138
  const findings = [
@@ -16447,8 +16557,8 @@ function askCommand() {
16447
16557
 
16448
16558
  // src/lib/glance-footer.ts
16449
16559
  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";
16560
+ import { homedir as homedir6 } from "node:os";
16561
+ import { join as join8, dirname as dirname5 } from "node:path";
16452
16562
 
16453
16563
  // src/lib/seen.ts
16454
16564
  var SEEN_TIMEOUT_MS = 1e3;
@@ -16835,7 +16945,7 @@ function shouldRequestTip(input) {
16835
16945
  return now - state.lastTipAt >= TIP_THROTTLE_INTERVAL_MS;
16836
16946
  }
16837
16947
  function getGlanceStatePath() {
16838
- return join7(homedir5(), ".gad", "glance-state.json");
16948
+ return join8(homedir6(), ".gad", "glance-state.json");
16839
16949
  }
16840
16950
  async function loadGlanceState() {
16841
16951
  try {
@@ -19082,9 +19192,9 @@ node_default(Temp.purgeSyncAll);
19082
19192
  var temp_default = Temp;
19083
19193
 
19084
19194
  // node_modules/.pnpm/atomically@2.1.1/node_modules/atomically/dist/index.js
19085
- function writeFileSync3(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
19195
+ function writeFileSync4(filePath, data, options = DEFAULT_WRITE_OPTIONS) {
19086
19196
  if (isString(options))
19087
- return writeFileSync3(filePath, data, { encoding: options });
19197
+ return writeFileSync4(filePath, data, { encoding: options });
19088
19198
  const timeout2 = options.timeout ?? DEFAULT_TIMEOUT_SYNC;
19089
19199
  const retryOptions = { timeout: timeout2 };
19090
19200
  let tempDisposer = null;
@@ -19411,7 +19521,7 @@ var Configstore = class {
19411
19521
  }
19412
19522
  if (error.name === "SyntaxError") {
19413
19523
  if (this._clearInvalidConfig) {
19414
- writeFileSync3(this._path, "", writeFileOptions);
19524
+ writeFileSync4(this._path, "", writeFileOptions);
19415
19525
  return {};
19416
19526
  }
19417
19527
  throw error;
@@ -19423,7 +19533,7 @@ var Configstore = class {
19423
19533
  set all(value) {
19424
19534
  try {
19425
19535
  import_graceful_fs.default.mkdirSync(path5.dirname(this._path), mkdirOptions);
19426
- writeFileSync3(this._path, JSON.stringify(value, void 0, " "), writeFileOptions);
19536
+ writeFileSync4(this._path, JSON.stringify(value, void 0, " "), writeFileOptions);
19427
19537
  } catch (error) {
19428
19538
  handlePermissionError(error);
19429
19539
  }
@@ -22130,6 +22240,7 @@ void captureCliEvent({
22130
22240
  });
22131
22241
  program2.addCommand(login_default);
22132
22242
  program2.addCommand(logoutCommand);
22243
+ program2.addCommand(authCommand());
22133
22244
  program2.addCommand(whoamiCommand());
22134
22245
  program2.addCommand(memoryCommand);
22135
22246
  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.47",
4
4
  "description": "GreatArrow.ai CLI tool",
5
5
  "type": "module",
6
6
  "bin": {