@phnx-labs/agents-cli 1.20.33 → 1.20.35

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 (159) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/README.md +32 -2
  3. package/dist/commands/computer-actions.d.ts +15 -0
  4. package/dist/commands/computer-actions.js +120 -22
  5. package/dist/commands/computer.d.ts +23 -0
  6. package/dist/commands/computer.js +45 -3
  7. package/dist/commands/cost.js +17 -12
  8. package/dist/commands/doctor.d.ts +11 -0
  9. package/dist/commands/doctor.js +108 -14
  10. package/dist/commands/events.d.ts +16 -0
  11. package/dist/commands/events.js +173 -0
  12. package/dist/commands/exec.js +6 -1
  13. package/dist/commands/hosts.js +28 -24
  14. package/dist/commands/import.js +1 -1
  15. package/dist/commands/inspect.d.ts +2 -0
  16. package/dist/commands/inspect.js +54 -7
  17. package/dist/commands/logs.d.ts +17 -0
  18. package/dist/commands/logs.js +139 -0
  19. package/dist/commands/models.d.ts +3 -0
  20. package/dist/commands/models.js +27 -8
  21. package/dist/commands/permissions.js +9 -2
  22. package/dist/commands/repo.d.ts +34 -0
  23. package/dist/commands/repo.js +243 -65
  24. package/dist/commands/resource-view.d.ts +20 -0
  25. package/dist/commands/resource-view.js +90 -28
  26. package/dist/commands/rules.js +1 -1
  27. package/dist/commands/secrets-migrate.js +23 -11
  28. package/dist/commands/secrets.d.ts +20 -0
  29. package/dist/commands/secrets.js +93 -17
  30. package/dist/commands/sessions-resume.d.ts +2 -0
  31. package/dist/commands/sessions-resume.js +209 -0
  32. package/dist/commands/sessions-tail.d.ts +10 -0
  33. package/dist/commands/sessions-tail.js +11 -0
  34. package/dist/commands/sessions.d.ts +73 -1
  35. package/dist/commands/sessions.js +273 -53
  36. package/dist/commands/status.d.ts +12 -0
  37. package/dist/commands/status.js +81 -0
  38. package/dist/commands/teams.js +70 -6
  39. package/dist/commands/versions.js +2 -1
  40. package/dist/commands/view.d.ts +42 -0
  41. package/dist/commands/view.js +213 -83
  42. package/dist/commands/wallet.d.ts +6 -0
  43. package/dist/commands/wallet.js +22 -5
  44. package/dist/index.js +61 -32
  45. package/dist/lib/acp/harnesses.d.ts +1 -1
  46. package/dist/lib/acp/harnesses.js +2 -2
  47. package/dist/lib/agents.d.ts +12 -0
  48. package/dist/lib/agents.js +145 -41
  49. package/dist/lib/browser/cdp.js +5 -1
  50. package/dist/lib/browser/chrome.js +20 -0
  51. package/dist/lib/browser/drivers/ssh.d.ts +27 -0
  52. package/dist/lib/browser/drivers/ssh.js +94 -19
  53. package/dist/lib/browser/ipc.d.ts +3 -0
  54. package/dist/lib/browser/ipc.js +13 -9
  55. package/dist/lib/browser/service.js +71 -15
  56. package/dist/lib/daemon.d.ts +35 -0
  57. package/dist/lib/daemon.js +33 -5
  58. package/dist/lib/devices/connect.d.ts +3 -2
  59. package/dist/lib/devices/connect.js +5 -3
  60. package/dist/lib/doctor-diff.js +29 -2
  61. package/dist/lib/drift-sync.d.ts +43 -0
  62. package/dist/lib/drift-sync.js +179 -0
  63. package/dist/lib/events.d.ts +9 -2
  64. package/dist/lib/events.js +70 -11
  65. package/dist/lib/exec.d.ts +15 -0
  66. package/dist/lib/exec.js +43 -11
  67. package/dist/lib/hooks.js +8 -2
  68. package/dist/lib/hosts/dispatch.js +6 -7
  69. package/dist/lib/hosts/logs.d.ts +16 -0
  70. package/dist/lib/hosts/logs.js +45 -0
  71. package/dist/lib/hosts/progress.d.ts +66 -0
  72. package/dist/lib/hosts/progress.js +125 -17
  73. package/dist/lib/hosts/ready.d.ts +23 -2
  74. package/dist/lib/hosts/ready.js +35 -13
  75. package/dist/lib/hosts/reconcile.d.ts +53 -0
  76. package/dist/lib/hosts/reconcile.js +81 -0
  77. package/dist/lib/hosts/tasks.d.ts +8 -0
  78. package/dist/lib/hosts/tasks.js +14 -0
  79. package/dist/lib/permissions.d.ts +4 -0
  80. package/dist/lib/permissions.js +35 -0
  81. package/dist/lib/picker.d.ts +24 -0
  82. package/dist/lib/picker.js +124 -0
  83. package/dist/lib/platform/winpath.d.ts +31 -2
  84. package/dist/lib/platform/winpath.js +133 -24
  85. package/dist/lib/plugin-marketplace.d.ts +30 -0
  86. package/dist/lib/plugin-marketplace.js +215 -2
  87. package/dist/lib/plugins.d.ts +5 -0
  88. package/dist/lib/plugins.js +45 -4
  89. package/dist/lib/pwsh.d.ts +11 -0
  90. package/dist/lib/pwsh.js +13 -0
  91. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  92. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  93. package/dist/lib/secrets/agent.d.ts +42 -1
  94. package/dist/lib/secrets/agent.js +89 -11
  95. package/dist/lib/secrets/bundles.js +40 -9
  96. package/dist/lib/secrets/filestore.js +31 -1
  97. package/dist/lib/secrets/index.d.ts +33 -1
  98. package/dist/lib/secrets/index.js +90 -9
  99. package/dist/lib/secrets/windows.d.ts +74 -0
  100. package/dist/lib/secrets/windows.js +440 -0
  101. package/dist/lib/session/active.d.ts +7 -0
  102. package/dist/lib/session/active.js +11 -3
  103. package/dist/lib/session/discover.js +94 -0
  104. package/dist/lib/session/parse.d.ts +6 -0
  105. package/dist/lib/session/parse.js +283 -2
  106. package/dist/lib/session/pid-registry.d.ts +21 -0
  107. package/dist/lib/session/pid-registry.js +80 -0
  108. package/dist/lib/session/remote-active.d.ts +26 -0
  109. package/dist/lib/session/remote-active.js +141 -0
  110. package/dist/lib/session/remote.js +2 -6
  111. package/dist/lib/session/render.js +1 -1
  112. package/dist/lib/session/sync/config.d.ts +8 -0
  113. package/dist/lib/session/sync/config.js +11 -2
  114. package/dist/lib/session/types.d.ts +1 -1
  115. package/dist/lib/session/types.js +1 -1
  116. package/dist/lib/shims.d.ts +30 -2
  117. package/dist/lib/shims.js +107 -50
  118. package/dist/lib/ssh-exec.d.ts +26 -3
  119. package/dist/lib/ssh-exec.js +45 -3
  120. package/dist/lib/ssh-tunnel.d.ts +24 -5
  121. package/dist/lib/ssh-tunnel.js +60 -62
  122. package/dist/lib/staleness/writers/hooks.js +1 -1
  123. package/dist/lib/startup/command-registry.d.ts +3 -0
  124. package/dist/lib/startup/command-registry.js +6 -0
  125. package/dist/lib/sync-status.d.ts +102 -0
  126. package/dist/lib/sync-status.js +135 -0
  127. package/dist/lib/teams/agents.d.ts +24 -0
  128. package/dist/lib/teams/agents.js +30 -1
  129. package/dist/lib/teams/registry.js +25 -9
  130. package/dist/lib/terminal/backends/ghostty.d.ts +6 -0
  131. package/dist/lib/terminal/backends/ghostty.js +69 -0
  132. package/dist/lib/terminal/backends/index.d.ts +18 -0
  133. package/dist/lib/terminal/backends/index.js +29 -0
  134. package/dist/lib/terminal/backends/iterm.d.ts +6 -0
  135. package/dist/lib/terminal/backends/iterm.js +62 -0
  136. package/dist/lib/terminal/backends/tmux.d.ts +14 -0
  137. package/dist/lib/terminal/backends/tmux.js +23 -0
  138. package/dist/lib/terminal/engine.d.ts +39 -0
  139. package/dist/lib/terminal/engine.js +54 -0
  140. package/dist/lib/terminal/index.d.ts +14 -0
  141. package/dist/lib/terminal/index.js +5 -0
  142. package/dist/lib/terminal/policy.d.ts +11 -0
  143. package/dist/lib/terminal/policy.js +11 -0
  144. package/dist/lib/terminal/quote.d.ts +11 -0
  145. package/dist/lib/terminal/quote.js +13 -0
  146. package/dist/lib/terminal/shell.d.ts +6 -0
  147. package/dist/lib/terminal/shell.js +23 -0
  148. package/dist/lib/terminal/transport.d.ts +15 -0
  149. package/dist/lib/terminal/transport.js +40 -0
  150. package/dist/lib/terminal/types.d.ts +59 -0
  151. package/dist/lib/terminal/types.js +13 -0
  152. package/dist/lib/types.d.ts +27 -1
  153. package/dist/lib/usage.d.ts +30 -0
  154. package/dist/lib/usage.js +159 -2
  155. package/dist/lib/versions.d.ts +7 -3
  156. package/dist/lib/versions.js +10 -5
  157. package/dist/lib/whats-new.d.ts +9 -0
  158. package/dist/lib/whats-new.js +35 -0
  159. package/package.json +1 -1
package/dist/lib/usage.js CHANGED
@@ -33,6 +33,7 @@ const CLAUDE_SCOPES = [
33
33
  const CLAUDE_KEYCHAIN_SERVICE = 'Claude Code-credentials';
34
34
  const getClaudeUsageCachePath = () => path.join(getCacheDir(), 'claude-usage.json');
35
35
  const CACHED_CLAUDE_USAGE_SOURCE_LABEL = 'last seen live account data';
36
+ const KIMI_USAGES_URL = 'https://api.kimi.com/coding/v1/usages';
36
37
  const COMPACT_BAR_LEN = 5;
37
38
  const USAGE_BAR_LEN = 10;
38
39
  const FULL = '\u2588';
@@ -44,6 +45,8 @@ export async function getUsageInfo(agentId, options) {
44
45
  return getClaudeUsageInfo(options);
45
46
  case 'codex':
46
47
  return getCodexUsageInfo(options);
48
+ case 'kimi':
49
+ return getKimiUsageInfo(options);
47
50
  default:
48
51
  return { snapshot: null, error: null };
49
52
  }
@@ -114,8 +117,14 @@ const inFlightRefreshes = new Map();
114
117
  */
115
118
  export async function getUsageInfoForIdentity(input) {
116
119
  const usageKey = getUsageLookupKey(input.info);
117
- // Non-Claude or no identity: legacy path, blocking fetch.
118
- if (input.agentId !== 'claude' || !usageKey) {
120
+ // Agents whose usage comes from a live network call (Claude, Kimi) go through
121
+ // the stale-while-revalidate cache below so `agents run`/`agents view` stay off
122
+ // the network on the hot path. Everything else (Codex reads local session
123
+ // logs) takes the legacy blocking path. The on-disk cache is shared and keyed
124
+ // by usageKey, which is namespaced per agent (`claude:org=…`, `kimi:user=…`),
125
+ // so one cache file holds every account without collision.
126
+ const usesNetworkUsage = input.agentId === 'claude' || input.agentId === 'kimi';
127
+ if (!usesNetworkUsage || !usageKey) {
119
128
  return getUsageInfo(input.agentId, {
120
129
  home: input.home,
121
130
  cliVersion: input.cliVersion,
@@ -356,6 +365,152 @@ async function getClaudeUsageInfo(options) {
356
365
  return { snapshot: null, error: 'Usage data unavailable right now.' };
357
366
  }
358
367
  }
368
+ /**
369
+ * Resolve Kimi's OAuth credential file. Sign-in is account-global but each
370
+ * installed version has an isolated home; the file physically lives only in the
371
+ * home the user logged in under. Check the per-version home first, then the
372
+ * active location under the real HOME — mirrors resolveAccountCredentialPath in
373
+ * agents.ts so every version reflects the true account state.
374
+ */
375
+ function resolveKimiCredentialPath(home) {
376
+ const rel = ['.kimi-code', 'credentials', 'kimi-code.json'];
377
+ const perVersion = path.join(home || os.homedir(), ...rel);
378
+ try {
379
+ if (fs.existsSync(perVersion))
380
+ return perVersion;
381
+ }
382
+ catch { /* unreadable */ }
383
+ const active = path.join(process.env.AGENTS_REAL_HOME || os.homedir(), ...rel);
384
+ if (active !== perVersion) {
385
+ try {
386
+ if (fs.existsSync(active))
387
+ return active;
388
+ }
389
+ catch { /* unreadable */ }
390
+ }
391
+ return null;
392
+ }
393
+ /**
394
+ * Fetch Kimi usage via the Kimi Code /usages API. Kimi's JWT has no email
395
+ * claim, so the account row can't show an address — but /usages returns quota
396
+ * windows and the membership tier, which is what we render.
397
+ *
398
+ * Deliberately NO token refresh: `agents view` is a read/inspect command and
399
+ * must not rotate the user's Kimi OAuth credential (rewriting the file,
400
+ * invalidating the old refresh token, racing a concurrently-running kimi CLI).
401
+ * The kimi CLI refreshes on its own launch; if the stored token is expired we
402
+ * skip the live fetch and let the SWR cache serve the last-seen snapshot.
403
+ */
404
+ async function getKimiUsageInfo(options) {
405
+ try {
406
+ const credPath = resolveKimiCredentialPath(options?.home);
407
+ if (!credPath)
408
+ return { snapshot: null, error: null };
409
+ const cred = JSON.parse(fs.readFileSync(credPath, 'utf-8'));
410
+ const accessToken = cred?.access_token;
411
+ if (typeof accessToken !== 'string' || !accessToken) {
412
+ return { snapshot: null, error: null };
413
+ }
414
+ const expiresAt = typeof cred?.expires_at === 'number' ? cred.expires_at : null;
415
+ if (expiresAt !== null && Date.now() / 1000 >= expiresAt) {
416
+ return { snapshot: null, error: null };
417
+ }
418
+ const response = await fetch(KIMI_USAGES_URL, {
419
+ method: 'GET',
420
+ headers: {
421
+ Authorization: `Bearer ${accessToken}`,
422
+ Accept: 'application/json',
423
+ },
424
+ signal: AbortSignal.timeout(5000),
425
+ });
426
+ // 401/403/404 => expired token or no Kimi For Coding subscription; render
427
+ // nothing rather than a misleading empty bar.
428
+ if (!response.ok) {
429
+ return { snapshot: null, error: null };
430
+ }
431
+ const data = await response.json();
432
+ const windows = normalizeKimiWindows(data);
433
+ if (windows.length === 0) {
434
+ return { snapshot: null, error: null };
435
+ }
436
+ return {
437
+ snapshot: {
438
+ source: 'live',
439
+ sourceLabel: 'live account data',
440
+ capturedAt: new Date(),
441
+ windows,
442
+ plan: formatKimiPlan(data),
443
+ },
444
+ error: null,
445
+ };
446
+ }
447
+ catch {
448
+ return { snapshot: null, error: null };
449
+ }
450
+ }
451
+ /** Normalize the Kimi /usages payload into the common UsageWindow shape. */
452
+ export function normalizeKimiWindows(data) {
453
+ const windows = [];
454
+ // Per-window rate limit (e.g. a 300-minute bucket) -> "session".
455
+ const shortLimit = Array.isArray(data.limits)
456
+ ? data.limits.find((entry) => entry?.detail)
457
+ : null;
458
+ const session = normalizeKimiWindow(shortLimit?.detail, 'session', 'Current session', 'S', kimiWindowMinutes(shortLimit?.window));
459
+ if (session)
460
+ windows.push(session);
461
+ // Rolling account quota -> "week".
462
+ const period = normalizeKimiWindow(data.usage, 'week', 'Current period', 'W', null);
463
+ if (period)
464
+ windows.push(period);
465
+ return windows;
466
+ }
467
+ /** Normalize a single Kimi quota bucket (used/limit strings) into a UsageWindow. */
468
+ function normalizeKimiWindow(quota, key, label, shortLabel, windowMinutes) {
469
+ const limit = kimiNumber(quota?.limit);
470
+ const used = kimiNumber(quota?.used);
471
+ if (limit === null || used === null || limit <= 0)
472
+ return null;
473
+ const usedPercent = normalizePercent((used / limit) * 100);
474
+ if (usedPercent === null)
475
+ return null;
476
+ return {
477
+ key,
478
+ label,
479
+ shortLabel,
480
+ usedPercent,
481
+ resetsAt: parseDateValue(quota?.resetTime),
482
+ windowMinutes: windowMinutes ?? inferWindowMinutes(key),
483
+ };
484
+ }
485
+ /** Parse a numeric field that Kimi serializes as a string (e.g. "100"). */
486
+ function kimiNumber(value) {
487
+ if (typeof value === 'number' && Number.isFinite(value))
488
+ return value;
489
+ if (typeof value === 'string' && value.trim() !== '' && !Number.isNaN(Number(value))) {
490
+ return Number(value);
491
+ }
492
+ return null;
493
+ }
494
+ /** Convert a Kimi limit window (duration + timeUnit enum) to minutes. */
495
+ function kimiWindowMinutes(window) {
496
+ const duration = typeof window?.duration === 'number' ? window.duration : null;
497
+ if (duration === null || duration <= 0)
498
+ return null;
499
+ switch (window?.timeUnit) {
500
+ case 'TIME_UNIT_HOUR': return duration * 60;
501
+ case 'TIME_UNIT_SECOND': return duration / 60;
502
+ default: return duration; // TIME_UNIT_MINUTE or unknown -> minutes
503
+ }
504
+ }
505
+ /** Derive a display plan label from Kimi's membership tier or subscription type. */
506
+ export function formatKimiPlan(data) {
507
+ const level = data.user?.membership?.level;
508
+ const raw = (typeof level === 'string' && level) || (typeof data.subType === 'string' && data.subType) || '';
509
+ const tail = raw.split('_').pop() || ''; // LEVEL_INTERMEDIATE -> INTERMEDIATE
510
+ if (!tail)
511
+ return null;
512
+ return tail.charAt(0).toUpperCase() + tail.slice(1).toLowerCase();
513
+ }
359
514
  /** Collect Codex JSONL session files sorted newest-first. */
360
515
  function collectCodexSessionFiles(home) {
361
516
  const base = home || os.homedir();
@@ -590,6 +745,7 @@ function writeClaudeUsageCacheFile(cache, cachePath) {
590
745
  function serializeClaudeUsageSnapshot(snapshot) {
591
746
  return {
592
747
  capturedAt: snapshot.capturedAt?.toISOString() || null,
748
+ plan: snapshot.plan ?? null,
593
749
  windows: snapshot.windows.map((window) => ({
594
750
  key: window.key,
595
751
  label: window.label,
@@ -626,6 +782,7 @@ function deserializeClaudeUsageSnapshot(snapshot, now) {
626
782
  sourceLabel: CACHED_CLAUDE_USAGE_SOURCE_LABEL,
627
783
  capturedAt,
628
784
  windows,
785
+ plan: snapshot.plan ?? null,
629
786
  };
630
787
  }
631
788
  /** Check whether a cached usage window is still relevant (not expired or reset). */
@@ -337,9 +337,13 @@ export declare function listRepoNames(): string[];
337
337
  * sync path uses. Passing the result as an explicit `selection` means the sync
338
338
  * touches only that repo's resources — no orphan-sweep of the other layers.
339
339
  *
340
- * `memory` is set to `[]` (not omitted): that empty-array sentinel is what
341
- * `syncResourcesToVersion`'s `skipMemory` gate keys on to leave the memory
342
- * file untouched it's a merge of all layers, not a per-repo artifact.
340
+ * `memory` is set to `'all'`, NOT `[]`: the composed rules-memory file is a
341
+ * merge of ALL layers, so scoping the sync to a single repo must still
342
+ * recompile it in the same pass (RUSH-1354). Leaving it out or using the
343
+ * `[]` skip-sentinel — silently strands the memory file at its old content
344
+ * whenever a repo-scoped sync runs after a rules change. `'all'` makes
345
+ * `syncResourcesToVersion` recompose from every layer via the active preset,
346
+ * exactly as an unscoped full reconcile does.
343
347
  */
344
348
  export declare function buildRepoScopedSelection(repo: string, cwd?: string): ResourceSelection;
345
349
  /**
@@ -1692,9 +1692,13 @@ function resourceSourceMap(kind, cwd, available) {
1692
1692
  * sync path uses. Passing the result as an explicit `selection` means the sync
1693
1693
  * touches only that repo's resources — no orphan-sweep of the other layers.
1694
1694
  *
1695
- * `memory` is set to `[]` (not omitted): that empty-array sentinel is what
1696
- * `syncResourcesToVersion`'s `skipMemory` gate keys on to leave the memory
1697
- * file untouched it's a merge of all layers, not a per-repo artifact.
1695
+ * `memory` is set to `'all'`, NOT `[]`: the composed rules-memory file is a
1696
+ * merge of ALL layers, so scoping the sync to a single repo must still
1697
+ * recompile it in the same pass (RUSH-1354). Leaving it out or using the
1698
+ * `[]` skip-sentinel — silently strands the memory file at its old content
1699
+ * whenever a repo-scoped sync runs after a rules change. `'all'` makes
1700
+ * `syncResourcesToVersion` recompose from every layer via the active preset,
1701
+ * exactly as an unscoped full reconcile does.
1698
1702
  */
1699
1703
  export function buildRepoScopedSelection(repo, cwd = process.cwd()) {
1700
1704
  const patterns = [`${repo}:*`];
@@ -1706,8 +1710,9 @@ export function buildRepoScopedSelection(repo, cwd = process.cwd()) {
1706
1710
  if (names.length > 0)
1707
1711
  selection[kind] = names;
1708
1712
  }
1709
- // Empty-array sentinel skip the memory writer (see skipMemory below).
1710
- selection.memory = [];
1713
+ // Recompile the memory file from all layers even under a repo scope — it is
1714
+ // composed, not a per-repo artifact (see skipMemory below and RUSH-1354).
1715
+ selection.memory = 'all';
1711
1716
  return selection;
1712
1717
  }
1713
1718
  /**
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Render a compact "What's new" summary from a CHANGELOG.md body: one bullet
3
+ * per feature/fix heading (the `**...**` lines) for each version in the range
4
+ * the user actually moved through, `(fromVersion, toVersion]`. The verbose
5
+ * sub-bullets are intentionally dropped — the full notes live in the changelog.
6
+ *
7
+ * Returns colored lines ready to print, empty when nothing is in range.
8
+ */
9
+ export declare function renderWhatsNew(changelog: string, fromVersion: string, toVersion: string): string[];
@@ -0,0 +1,35 @@
1
+ import chalk from 'chalk';
2
+ import { compareVersions } from './agent-spec/primitives.js';
3
+ /**
4
+ * Render a compact "What's new" summary from a CHANGELOG.md body: one bullet
5
+ * per feature/fix heading (the `**...**` lines) for each version in the range
6
+ * the user actually moved through, `(fromVersion, toVersion]`. The verbose
7
+ * sub-bullets are intentionally dropped — the full notes live in the changelog.
8
+ *
9
+ * Returns colored lines ready to print, empty when nothing is in range.
10
+ */
11
+ export function renderWhatsNew(changelog, fromVersion, toVersion) {
12
+ const out = [];
13
+ let inRelevantSection = false;
14
+ for (const line of changelog.split('\n')) {
15
+ const versionMatch = line.match(/^## (\d+\.\d+\.\d+)/);
16
+ if (versionMatch) {
17
+ const currentVersion = versionMatch[1];
18
+ // Bounding the top end matters when upgrading to a specific older
19
+ // version, and guards against a changelog that lists unreleased entries.
20
+ inRelevantSection =
21
+ compareVersions(currentVersion, fromVersion) > 0 &&
22
+ compareVersions(currentVersion, toVersion) <= 0;
23
+ if (inRelevantSection) {
24
+ out.push('');
25
+ out.push(chalk.bold(`v${currentVersion}`));
26
+ }
27
+ continue;
28
+ }
29
+ // Only the bold headings — one bullet per feature/fix.
30
+ if (inRelevantSection && line.startsWith('**') && line.endsWith('**')) {
31
+ out.push(` ${chalk.cyan('•')} ${line.replace(/\*\*/g, '')}`);
32
+ }
33
+ }
34
+ return out;
35
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.20.33",
3
+ "version": "1.20.35",
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",