@phnx-labs/agents-cli 1.22.28 → 1.22.30

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 (75) hide show
  1. package/CHANGELOG.md +88 -0
  2. package/README.md +39 -1
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/accounts.d.ts +13 -0
  5. package/dist/commands/accounts.js +32 -0
  6. package/dist/commands/daemon.d.ts +18 -0
  7. package/dist/commands/daemon.js +581 -0
  8. package/dist/commands/exec.js +66 -20
  9. package/dist/commands/routines.js +29 -11
  10. package/dist/commands/secrets.d.ts +17 -0
  11. package/dist/commands/secrets.js +30 -15
  12. package/dist/commands/sessions-browser.js +6 -6
  13. package/dist/commands/sessions-favorite.d.ts +7 -7
  14. package/dist/commands/sessions-favorite.js +30 -30
  15. package/dist/commands/sessions-picker.d.ts +33 -1
  16. package/dist/commands/sessions-picker.js +102 -27
  17. package/dist/commands/sessions.d.ts +12 -1
  18. package/dist/commands/sessions.js +259 -20
  19. package/dist/commands/view.d.ts +11 -0
  20. package/dist/commands/view.js +56 -29
  21. package/dist/index.js +37 -2
  22. package/dist/lib/account-labels.d.ts +24 -0
  23. package/dist/lib/account-labels.js +72 -0
  24. package/dist/lib/agents.d.ts +32 -1
  25. package/dist/lib/agents.js +96 -31
  26. package/dist/lib/daemon-health.d.ts +24 -0
  27. package/dist/lib/daemon-health.js +84 -0
  28. package/dist/lib/daemon-ticks.d.ts +81 -0
  29. package/dist/lib/daemon-ticks.js +190 -0
  30. package/dist/lib/daemon.d.ts +68 -18
  31. package/dist/lib/daemon.js +303 -338
  32. package/dist/lib/device-config.d.ts +10 -0
  33. package/dist/lib/device-config.js +27 -0
  34. package/dist/lib/exec.d.ts +27 -0
  35. package/dist/lib/exec.js +49 -2
  36. package/dist/lib/hosts/dispatch.d.ts +4 -0
  37. package/dist/lib/hosts/dispatch.js +4 -0
  38. package/dist/lib/hosts/remote-cmd.js +1 -0
  39. package/dist/lib/hosts/run-target.d.ts +1 -0
  40. package/dist/lib/hosts/run-target.js +1 -0
  41. package/dist/lib/import.js +7 -6
  42. package/dist/lib/memory-cache.d.ts +19 -0
  43. package/dist/lib/memory-cache.js +31 -0
  44. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  45. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  46. package/dist/lib/menubar/snapshot.d.ts +16 -0
  47. package/dist/lib/menubar/snapshot.js +22 -1
  48. package/dist/lib/migrate.d.ts +1 -1
  49. package/dist/lib/migrate.js +7 -2
  50. package/dist/lib/routine-activation.d.ts +2 -0
  51. package/dist/lib/routine-activation.js +16 -0
  52. package/dist/lib/runner.d.ts +18 -0
  53. package/dist/lib/runner.js +52 -0
  54. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  55. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  56. package/dist/lib/secrets/agent.d.ts +19 -0
  57. package/dist/lib/secrets/agent.js +32 -2
  58. package/dist/lib/secrets/scope.d.ts +3 -3
  59. package/dist/lib/secrets/scope.js +3 -3
  60. package/dist/lib/session/db.d.ts +15 -0
  61. package/dist/lib/session/db.js +90 -15
  62. package/dist/lib/session/discover.js +91 -39
  63. package/dist/lib/session/favorites.d.ts +2 -2
  64. package/dist/lib/session/favorites.js +2 -2
  65. package/dist/lib/session/parse.d.ts +63 -0
  66. package/dist/lib/session/parse.js +165 -20
  67. package/dist/lib/session/session-cache.d.ts +9 -6
  68. package/dist/lib/session/session-cache.js +23 -6
  69. package/dist/lib/shims.js +12 -0
  70. package/dist/lib/startup/command-registry.d.ts +15 -1
  71. package/dist/lib/startup/command-registry.js +49 -0
  72. package/dist/lib/usage-refresh.js +3 -2
  73. package/dist/lib/usage.d.ts +12 -10
  74. package/dist/lib/usage.js +81 -154
  75. package/package.json +4 -1
package/dist/lib/usage.js CHANGED
@@ -681,10 +681,11 @@ export function claudeUsageAccessTokenNoRefresh(oauth) {
681
681
  /** Fetch Claude usage via the Anthropic OAuth usage API. */
682
682
  async function getClaudeUsageInfo(options) {
683
683
  try {
684
- // Opt into the no-ACL access-token cache: this is the every-60s watchdog hot
685
- // path and usage needs only the access token, so it kills the Touch ID storm.
686
- // Daemon refresh also sets fileOnly so we never fall through to the ACL
687
- // keychain item (that path is the Touch ID prompt).
684
+ // accessTokenCache: this is the every-60s watchdog hot path and usage needs
685
+ // only the access token, so it reads ONLY the file-based setup-token and never
686
+ // the interactive login (reading that ACL-bound token and firing it at the
687
+ // usage API is what got it revoked RUSH-1822). No setup-token => null =>
688
+ // "usage pending". fileOnly additionally forbids the ACL keychain path.
688
689
  const oauth = await loadClaudeOauth(options?.home, {
689
690
  accessTokenCache: true,
690
691
  fileOnly: options?.fileOnly === true,
@@ -977,9 +978,10 @@ async function getDroidUsageInfo(options) {
977
978
  }
978
979
  /** Probe Claude's OAuth token against the usage endpoint. Never refreshes — reports `expired` for a near-expiry token; see the comment below (RUSH-1822). */
979
980
  export async function probeClaudeStatus(home, cliVersion) {
980
- // Opt into the no-ACL access-token cache: the daemon warms this probe every ~3
981
- // min per account and it never refreshes (access token only), so caching is safe
982
- // and stops it from adding to the Touch ID storm.
981
+ // accessTokenCache: the daemon warms this probe every ~3 min per account, so it
982
+ // reads ONLY the file-based setup-token and never the interactive login
983
+ // transmitting that ACL-bound token to the usage API from a background loop is
984
+ // what got it revoked (RUSH-1822). No setup-token => token 'missing' below.
983
985
  const oauth = await loadClaudeOauth(home, { accessTokenCache: true });
984
986
  const accessToken = oauth?.accessToken?.trim();
985
987
  if (!accessToken)
@@ -1186,29 +1188,37 @@ async function readLatestCodexRateLimits(filePath) {
1186
1188
  }
1187
1189
  /** Normalize Codex rate-limit windows into the common UsageWindow shape. */
1188
1190
  function normalizeCodexWindows(rateLimits) {
1189
- const windows = [];
1190
- const primary = normalizeCodexWindow(rateLimits.primary, 'session', 'Current session', 'S');
1191
- if (primary)
1192
- windows.push(primary);
1193
- const secondary = normalizeCodexWindow(rateLimits.secondary, 'week', 'Current week', 'W');
1194
- if (secondary)
1195
- windows.push(secondary);
1196
- return windows;
1191
+ return [rateLimits.primary, rateLimits.secondary]
1192
+ .map(normalizeCodexWindow)
1193
+ .filter((window) => window !== null)
1194
+ .sort((a, b) => (a.windowMinutes ?? 0) - (b.windowMinutes ?? 0));
1197
1195
  }
1198
1196
  /** Normalize a single Codex rate-limit window. */
1199
- function normalizeCodexWindow(window, key, label, shortLabel) {
1197
+ function normalizeCodexWindow(window) {
1200
1198
  const usedPercent = normalizePercent(window?.used_percent);
1201
1199
  if (usedPercent === null)
1202
1200
  return null;
1201
+ const windowMinutes = normalizeWindowMinutes(window?.window_minutes);
1202
+ const { key, label, shortLabel } = classifyCodexWindow(windowMinutes);
1203
1203
  return {
1204
1204
  key,
1205
1205
  label,
1206
1206
  shortLabel,
1207
1207
  usedPercent,
1208
1208
  resetsAt: parseDateValue(window?.resets_at),
1209
- windowMinutes: normalizeWindowMinutes(window?.window_minutes),
1209
+ windowMinutes,
1210
1210
  };
1211
1211
  }
1212
+ /** Codex assigns quota windows to primary/secondary by plan, so duration carries their meaning. */
1213
+ function classifyCodexWindow(windowMinutes) {
1214
+ if (windowMinutes !== null && windowMinutes >= 28 * 24 * 60) {
1215
+ return { key: 'month', label: 'Current month', shortLabel: 'M' };
1216
+ }
1217
+ if (windowMinutes !== null && windowMinutes >= 7 * 24 * 60) {
1218
+ return { key: 'week', label: 'Current week', shortLabel: 'W' };
1219
+ }
1220
+ return { key: 'session', label: 'Current session', shortLabel: 'S' };
1221
+ }
1212
1222
  /** Normalize Claude API usage windows into the common UsageWindow shape. */
1213
1223
  function normalizeClaudeWindows(data) {
1214
1224
  const windows = [
@@ -1253,97 +1263,29 @@ function parseClaudeOauthPayload(raw) {
1253
1263
  return null;
1254
1264
  }
1255
1265
  }
1256
- // ── No-ACL cache for Claude's OAuth token (kills the macOS Touch ID storm) ──
1266
+ // ── Stale no-ACL Claude OAuth cache eviction (retired subsystem) ──
1257
1267
  //
1258
- // The source item `Claude Code-credentials-<hash>` is ACL-bound to Claude Code's
1259
- // own process, so every read agents-cli makes (via `/usr/bin/security`) pops
1260
- // Touch ID. The Factory watchdog polls `agents view --json` every 60s per agent,
1261
- // and each poll that crosses the 5-minute usage cache fires a background refresh
1262
- // -> loadClaudeOauth -> keychain read -> a biometric prompt. With many agents and
1263
- // accounts that is a prompt every few minutes, per account.
1264
- //
1265
- // Fix: after one real (prompting) read, cache the ACCESS token in a device-local
1266
- // NO-ACL keychain item — the same `set-no-acl` mechanism secrets/session-store.ts
1267
- // uses for unlocked bundles, whose reads never prompt — and serve every later read
1268
- // from it until the token's own `expiresAt`. The ACL read then happens at most once
1269
- // per token lifetime (~a few times a day), shared across every agent process.
1270
- //
1271
- // Security posture: only the short-lived access token is cached, never the refresh
1272
- // token, and the entry dies at the token's expiry (capped below). This mirrors the
1273
- // no-ACL posture session-store.ts already accepts for held bundles.
1268
+ // Earlier versions cached Claude's OAuth ACCESS token in a device-local no-ACL
1269
+ // keychain item so a read-only usage/probe read wouldn't pop the macOS Touch ID
1270
+ // prompt that the ACL-bound source item (`Claude Code-credentials-<hash>`) forces.
1271
+ // That cache is retired: read-only probes now authenticate ONLY with a file-based
1272
+ // setup-token and never read the interactive login (see loadClaudeOauth), so
1273
+ // nothing populates the cache anymore. deleteCachedClaudeOauth remains a
1274
+ // credential rotation still evicts a stale item an earlier version may have
1275
+ // written, so an old no-ACL copy of the interactive token can't linger.
1274
1276
  const CLAUDE_OAUTH_CACHE_PREFIX = 'agents-cli.claude-oauth-cache.';
1275
- /** Hard cap so a token with a distant or absent `expiresAt` can't pin a stale entry. */
1276
- const CLAUDE_OAUTH_CACHE_MAX_TTL_MS = 8 * 60 * 60 * 1000;
1277
1277
  /** The no-ACL cache item name for a Claude keychain service (hashed to stay tidy). */
1278
1278
  function claudeOauthCacheItem(service) {
1279
1279
  const hash = createHash('sha256').update(service).digest('hex').slice(0, 16);
1280
1280
  return `${CLAUDE_OAUTH_CACHE_PREFIX}${hash}`;
1281
1281
  }
1282
- /** Whether the no-ACL cache is meaningful: macOS (where the source read prompts) or
1283
- * whenever a test keychain backend is installed, so the path is exercisable on CI.
1284
- * Off macOS in production the source read is prompt-free, so there is nothing to cache. */
1285
- function claudeOauthCacheActive() {
1286
- return process.platform === 'darwin' || isKeychainBackendOverridden();
1287
- }
1288
- /** Read the cached access token (prompt-free). Null on miss, or when the cache entry
1289
- * or the token itself has expired — in which case the stale entry is dropped. */
1290
- function readCachedClaudeOauth(service) {
1291
- try {
1292
- // The cache item is written no-ACL (writeCachedClaudeOauth) — its whole purpose is
1293
- // serving the token prompt-free, so attest that to the raw-read storm guard.
1294
- const entry = JSON.parse(getKeychainToken(claudeOauthCacheItem(service), { silentNoAcl: true }));
1295
- if (!entry || typeof entry.accessToken !== 'string' || !entry.accessToken)
1296
- return null;
1297
- const now = Date.now();
1298
- const tokenExpired = typeof entry.expiresAt === 'number' && entry.expiresAt > 0 && now >= entry.expiresAt;
1299
- if (now >= entry.cacheExpiresAt || tokenExpired) {
1300
- try {
1301
- deleteKeychainToken(claudeOauthCacheItem(service));
1302
- }
1303
- catch {
1304
- /* best-effort eviction */
1305
- }
1306
- return null;
1307
- }
1308
- return {
1309
- accessToken: entry.accessToken,
1310
- refreshToken: null,
1311
- expiresAt: entry.expiresAt ?? null,
1312
- scopes: entry.scopes ?? null,
1313
- subscriptionType: entry.subscriptionType ?? null,
1314
- rateLimitTier: entry.rateLimitTier ?? null,
1315
- organizationUuid: entry.organizationUuid ?? null,
1316
- };
1317
- }
1318
- catch {
1319
- return null;
1320
- }
1321
- }
1322
- /** Populate the no-ACL cache after a real (prompting) source read. Best-effort — the
1323
- * cache is an optimization, so any failure just means the next read prompts again. */
1324
- function writeCachedClaudeOauth(service, creds) {
1325
- if (!creds.accessToken)
1326
- return;
1327
- try {
1328
- const now = Date.now();
1329
- const cap = now + CLAUDE_OAUTH_CACHE_MAX_TTL_MS;
1330
- const tokenExp = typeof creds.expiresAt === 'number' && creds.expiresAt > now ? creds.expiresAt : cap;
1331
- const entry = {
1332
- accessToken: creds.accessToken,
1333
- expiresAt: creds.expiresAt ?? null,
1334
- scopes: creds.scopes ?? null,
1335
- subscriptionType: creds.subscriptionType ?? null,
1336
- rateLimitTier: creds.rateLimitTier ?? null,
1337
- organizationUuid: creds.organizationUuid ?? null,
1338
- cacheExpiresAt: Math.min(tokenExp, cap),
1339
- };
1340
- setKeychainToken(claudeOauthCacheItem(service), JSON.stringify(entry), { noAcl: true });
1341
- }
1342
- catch {
1343
- /* best-effort — cache is an optimization */
1344
- }
1345
- }
1346
- /** Evict the no-ACL cache so a source rotation or sign-out is reflected immediately. */
1282
+ /**
1283
+ * Evict any no-ACL access-token cache item so a source rotation or sign-out is
1284
+ * reflected immediately. The cache itself is retired read-only probes no longer
1285
+ * read or write it (loadClaudeOauth returns a file-based setup-token or nothing)
1286
+ * but this eviction remains so a credential rotation still clears a stale cache
1287
+ * item that an earlier agents-cli version may have written no-ACL.
1288
+ */
1347
1289
  function deleteCachedClaudeOauth(service) {
1348
1290
  try {
1349
1291
  deleteKeychainToken(claudeOauthCacheItem(service));
@@ -1369,26 +1311,27 @@ function deleteCachedClaudeOauth(service) {
1369
1311
  * (run remotely over SSH by `--host`) rendered no usage bars even though the
1370
1312
  * account + plan — read from the plaintext `.claude.json` — showed fine.
1371
1313
  *
1372
- * `opts.accessTokenCache` opts INTO the no-ACL access-token cache (the Touch ID
1373
- * storm fix, see the cache helpers above). It is OFF by default so every caller
1374
- * keeps the full ACL-read credential the cached copy deliberately omits the
1375
- * refresh token, so callers that refresh (`isClaudeAuthValid` ->
1376
- * `getClaudeAccessToken`) or export the full blob (`readClaudeCredentialsBlob`
1377
- * for Rush Cloud dispatch) must NOT pass it. Only the read-only, high-frequency
1378
- * access-token-only consumers (the usage fetch and the auth-health probe) opt in.
1314
+ * `opts.accessTokenCache` marks a read-only, access-token-only consumer (the
1315
+ * usage fetch and the auth-health probe). Such a caller authenticates ONLY with
1316
+ * a file-based setup-token and, when none is provisioned, gets `null` — it never
1317
+ * reads Claude Code's interactive login (transmitting that ACL-bound OAuth token
1318
+ * to Anthropic's API is what gets it revoked; see the branch body and
1319
+ * docs/design/credential-management.md). It is OFF by default so full-credential
1320
+ * callers that refresh (`isClaudeAuthValid` -> `getClaudeAccessToken`) or export
1321
+ * the full blob (`readClaudeCredentialsBlob` for Rush Cloud dispatch) still read
1322
+ * the interactive login.
1379
1323
  *
1380
- * `opts.fileOnly` (implies access-token-only consumers) skips the ACL keychain
1381
- * read entirely — setup-token, no-ACL cache, and `.credentials.json` only. Used
1382
- * by the daemon usage refresher so a background tick can never pop Touch ID.
1324
+ * `opts.fileOnly` skips the ACL keychain read entirely — setup-token and
1325
+ * `.credentials.json` only. Used by the daemon usage refresher so a background
1326
+ * tick can never pop Touch ID.
1383
1327
  */
1384
1328
  export async function loadClaudeOauth(home, opts) {
1385
- // Read-only usage/probe callers (accessTokenCache) authenticate with a
1386
- // file-based setup-token when one is provisioned: the usage endpoint accepts
1387
- // any sk-ant-oat01 bearer, and the token is read from the file-based `auth`
1388
- // bundle never Claude Code's ACL-bound keychain item so it never pops
1389
- // Touch ID. Transitional: an account with no provisioned setup-token still
1390
- // falls through to the keychain/file read below (that fallback is removed once
1391
- // the fleet is fully seeded, per docs/design/credential-management.md).
1329
+ // Read-only usage/probe callers (accessTokenCache) authenticate ONLY with a
1330
+ // file-based setup-token from the `auth` bundle never Claude Code's
1331
+ // interactive login. The usage endpoint accepts any sk-ant-oat01 bearer, and
1332
+ // the file-based token never pops Touch ID. When no setup-token is provisioned
1333
+ // the probe reports unprovisioned rather than reading the interactive
1334
+ // credential (see below) that is the whole point of this branch.
1392
1335
  if (opts?.accessTokenCache === true) {
1393
1336
  const setupToken = resolveClaudeSetupToken(home);
1394
1337
  if (setupToken) {
@@ -1398,52 +1341,36 @@ export async function loadClaudeOauth(home, opts) {
1398
1341
  // the source of truth if it has actually been revoked.
1399
1342
  return { accessToken: setupToken };
1400
1343
  }
1344
+ // No provisioned setup-token: a read-only usage/health probe MUST NOT fall
1345
+ // through to Claude Code's interactive login credential. The daemon's usage
1346
+ // (~60s) and auth-health (~3min) warms would otherwise read the ACL-bound
1347
+ // OAuth token and transmit it to api.anthropic.com/api/oauth/usage — an
1348
+ // interactive credential used programmatically, which Anthropic flags and
1349
+ // revokes (the fleet-wide-logout class, RUSH-1822), and which violates the
1350
+ // invariant that the interactive/rotating login is untouchable
1351
+ // (docs/design/credential-management.md). Report unprovisioned (-> probe
1352
+ // token 'missing' -> auth-health 'unconfigured', benign for rotation); seed a
1353
+ // setup-token via the mint-auth path to restore usage/probe for the account.
1354
+ return null;
1401
1355
  }
1402
- // The OS keychain/keyring step is macOS/Linux-only. Windows skips straight
1403
- // to the .credentials.json fallback the Claude CLI has no keychain
1404
- // integration there and stores its OAuth token in that file too. An injected
1405
- // test backend is the exception: it makes the keychain path exercisable
1406
- // anywhere, so the platform check must yield to it exactly as the inner
1407
- // claudeOauthCacheActive() gate does otherwise this whole block is dead on
1408
- // a Windows runner and the tests below it read an empty home.
1409
- //
1410
- // fileOnly: daemon refresh must never open the ACL-bound item (Touch ID).
1411
- // It may still read the no-ACL cache item (set-no-acl — prompt-free).
1356
+ // Full-credential callers (isClaudeAuthValid -> getClaudeAccessToken; Rush
1357
+ // Cloud dispatch) legitimately read the interactive login to run/refresh
1358
+ // Claude. The OS keychain/keyring step is macOS/Linux-only; Windows and any
1359
+ // fileOnly caller skip to the .credentials.json read below (the Claude CLI
1360
+ // stores its OAuth token in that file too). An injected test backend makes the
1361
+ // keychain path exercisable anywhere, so the platform check yields to it.
1412
1362
  if (!opts?.fileOnly
1413
1363
  && (process.platform === 'darwin' || process.platform === 'linux' || isKeychainBackendOverridden())) {
1414
1364
  const service = getClaudeKeychainService(home);
1415
- // Serve the no-ACL cache first (opt-in, macOS/test only) so the ACL-gated read
1416
- // below — the one that pops Touch ID — happens at most once per token lifetime
1417
- // instead of on every usage refresh. Off unless the caller opts in, because the
1418
- // cached copy drops the refresh token (see the doc comment above).
1419
- const cacheActive = opts?.accessTokenCache === true && claudeOauthCacheActive();
1420
- if (cacheActive) {
1421
- const cached = readCachedClaudeOauth(service);
1422
- if (cached)
1423
- return cached;
1424
- }
1425
1365
  try {
1426
1366
  const fromKeychain = parseClaudeOauthPayload(getKeychainToken(service));
1427
- if (fromKeychain) {
1428
- if (cacheActive)
1429
- writeCachedClaudeOauth(service, fromKeychain);
1367
+ if (fromKeychain)
1430
1368
  return fromKeychain;
1431
- }
1432
1369
  }
1433
1370
  catch {
1434
1371
  // No keychain item, or no reachable keyring (headless Linux) — fall through.
1435
- // Evict any stale no-ACL cache so a sign-out/deletion isn't masked.
1436
- if (cacheActive)
1437
- deleteCachedClaudeOauth(service);
1438
1372
  }
1439
1373
  }
1440
- else if (opts?.fileOnly === true && opts?.accessTokenCache === true && claudeOauthCacheActive()) {
1441
- // fileOnly + accessTokenCache: still allow the no-ACL cache (never Touch ID).
1442
- const service = getClaudeKeychainService(home);
1443
- const cached = readCachedClaudeOauth(service);
1444
- if (cached)
1445
- return cached;
1446
- }
1447
1374
  const credsPath = path.join(home ?? os.homedir(), '.claude', '.credentials.json');
1448
1375
  try {
1449
1376
  if (fs.existsSync(credsPath)) {
@@ -1464,8 +1391,8 @@ export async function saveClaudeOauth(home, credentials) {
1464
1391
  // Windows not yet supported. An injected test backend is the exception, for
1465
1392
  // the same reason as the loadClaudeOauth guard above: it makes the keychain
1466
1393
  // path exercisable anywhere, and without it this returns before the rotated
1467
- // credential is written OR the no-ACL cache is evicted so a cached reader
1468
- // keeps serving the pre-rotation access token.
1394
+ // credential is written OR a stale no-ACL cache item an earlier version wrote
1395
+ // is evicted (deleteCachedClaudeOauth) leaving that stale item behind.
1469
1396
  if (process.platform !== 'darwin' && process.platform !== 'linux' && !isKeychainBackendOverridden()) {
1470
1397
  return false;
1471
1398
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.22.28",
3
+ "version": "1.22.30",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -52,6 +52,8 @@
52
52
  "postinstall": "node scripts/postinstall.js",
53
53
  "dev": "tsx src/index.ts",
54
54
  "changelog": "bun scripts/gen-changelog.ts",
55
+ "gen:index": "bun scripts/gen-command-index.ts",
56
+ "verify:index": "bash scripts/verify-command-index.sh",
55
57
  "start": "node dist/index.js",
56
58
  "test": "node ./node_modules/vitest/vitest.mjs run",
57
59
  "bench": "node ./node_modules/vitest/vitest.mjs bench --run",
@@ -97,6 +99,7 @@
97
99
  "commander": "15.0.0",
98
100
  "croner": "10.0.1",
99
101
  "diff": "9.0.0",
102
+ "lru-cache": "11.5.2",
100
103
  "marked": "15.0.12",
101
104
  "marked-terminal": "7.3.0",
102
105
  "ora": "9.4.1",