@phnx-labs/agents-cli 1.19.2 → 1.20.0

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 (103) hide show
  1. package/CHANGELOG.md +67 -0
  2. package/README.md +69 -9
  3. package/dist/browser.js +0 -0
  4. package/dist/commands/browser.js +88 -16
  5. package/dist/commands/cli.d.ts +14 -0
  6. package/dist/commands/cli.js +244 -0
  7. package/dist/commands/commands.js +3 -3
  8. package/dist/commands/computer.js +18 -1
  9. package/dist/commands/doctor.d.ts +1 -1
  10. package/dist/commands/doctor.js +2 -2
  11. package/dist/commands/exec.js +3 -3
  12. package/dist/commands/factory.d.ts +3 -14
  13. package/dist/commands/factory.js +3 -3
  14. package/dist/commands/hooks.js +3 -3
  15. package/dist/commands/plugins.js +11 -4
  16. package/dist/commands/profiles.js +1 -1
  17. package/dist/commands/prune.js +39 -160
  18. package/dist/commands/pull.js +56 -3
  19. package/dist/commands/routines.js +106 -13
  20. package/dist/commands/secrets.js +5 -7
  21. package/dist/commands/sessions.d.ts +28 -0
  22. package/dist/commands/sessions.js +98 -33
  23. package/dist/commands/setup.d.ts +1 -0
  24. package/dist/commands/setup.js +37 -28
  25. package/dist/commands/skills.js +3 -3
  26. package/dist/commands/teams.js +13 -0
  27. package/dist/commands/versions.d.ts +4 -3
  28. package/dist/commands/versions.js +131 -127
  29. package/dist/commands/view.js +12 -12
  30. package/dist/computer.js +0 -0
  31. package/dist/index.js +34 -6
  32. package/dist/lib/acp/harnesses.js +8 -0
  33. package/dist/lib/agents.js +110 -23
  34. package/dist/lib/browser/cdp.d.ts +8 -1
  35. package/dist/lib/browser/cdp.js +40 -3
  36. package/dist/lib/browser/chrome.d.ts +13 -0
  37. package/dist/lib/browser/chrome.js +42 -3
  38. package/dist/lib/browser/domain-skills.d.ts +51 -0
  39. package/dist/lib/browser/domain-skills.js +157 -0
  40. package/dist/lib/browser/drivers/local.js +45 -4
  41. package/dist/lib/browser/drivers/ssh.js +1 -1
  42. package/dist/lib/browser/ipc.d.ts +8 -1
  43. package/dist/lib/browser/ipc.js +37 -28
  44. package/dist/lib/browser/profiles.d.ts +13 -0
  45. package/dist/lib/browser/profiles.js +41 -1
  46. package/dist/lib/browser/service.d.ts +3 -0
  47. package/dist/lib/browser/service.js +21 -5
  48. package/dist/lib/browser/types.d.ts +7 -0
  49. package/dist/lib/cli-resources.d.ts +109 -0
  50. package/dist/lib/cli-resources.js +255 -0
  51. package/dist/lib/cloud/rush.js +5 -5
  52. package/dist/lib/command-skills.js +0 -2
  53. package/dist/lib/computer-rpc.d.ts +3 -0
  54. package/dist/lib/computer-rpc.js +53 -0
  55. package/dist/lib/daemon.js +20 -0
  56. package/dist/lib/exec.d.ts +3 -2
  57. package/dist/lib/exec.js +44 -9
  58. package/dist/lib/hooks.js +182 -0
  59. package/dist/lib/mcp.js +6 -0
  60. package/dist/lib/migrate.js +1 -1
  61. package/dist/lib/overdue.d.ts +26 -0
  62. package/dist/lib/overdue.js +101 -0
  63. package/dist/lib/permissions.js +5 -1
  64. package/dist/lib/plugin-marketplace.js +1 -1
  65. package/dist/lib/profiles-presets.js +37 -0
  66. package/dist/lib/resources/mcp.js +37 -0
  67. package/dist/lib/resources.d.ts +1 -1
  68. package/dist/lib/rotate.js +10 -4
  69. package/dist/lib/routines-format.d.ts +35 -0
  70. package/dist/lib/routines-format.js +173 -0
  71. package/dist/lib/routines.d.ts +7 -1
  72. package/dist/lib/routines.js +32 -12
  73. package/dist/lib/runner.js +19 -5
  74. package/dist/lib/scheduler.js +8 -1
  75. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  76. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  77. package/dist/lib/secrets/bundles.d.ts +22 -1
  78. package/dist/lib/secrets/bundles.js +234 -36
  79. package/dist/lib/secrets/index.d.ts +6 -11
  80. package/dist/lib/secrets/index.js +107 -87
  81. package/dist/lib/session/active.d.ts +8 -0
  82. package/dist/lib/session/active.js +3 -2
  83. package/dist/lib/session/db.d.ts +0 -4
  84. package/dist/lib/session/db.js +0 -26
  85. package/dist/lib/session/parse.d.ts +1 -0
  86. package/dist/lib/session/parse.js +44 -0
  87. package/dist/lib/session/types.d.ts +1 -1
  88. package/dist/lib/session/types.js +1 -1
  89. package/dist/lib/shims.d.ts +1 -1
  90. package/dist/lib/shims.js +66 -4
  91. package/dist/lib/state.d.ts +0 -1
  92. package/dist/lib/state.js +2 -15
  93. package/dist/lib/teams/agents.js +1 -1
  94. package/dist/lib/teams/parsers.d.ts +1 -1
  95. package/dist/lib/teams/parsers.js +153 -3
  96. package/dist/lib/teams/summarizer.js +18 -2
  97. package/dist/lib/teams/worktree.js +14 -3
  98. package/dist/lib/types.d.ts +6 -3
  99. package/dist/lib/types.js +6 -3
  100. package/dist/lib/versions.d.ts +10 -2
  101. package/dist/lib/versions.js +227 -35
  102. package/package.json +7 -7
  103. package/npm-shrinkwrap.json +0 -3162
@@ -7,7 +7,7 @@ import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, } f
7
7
  import { formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
8
8
  import { viewAction } from './view.js';
9
9
  import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
10
- import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, getGlobalDefault, setGlobalDefault, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, } from '../lib/versions.js';
10
+ import { installVersion, removeVersion, listInstalledVersions, isVersionInstalled, isLatestInstalled, getGlobalDefault, setGlobalDefault, getVersionHomePath, getVersionDir, syncResourcesToVersion, parseAgentSpec, promptResourceSelection, promptNewResourceSelection, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, printTrashFooter, } from '../lib/versions.js';
11
11
  import { createShim, createVersionedAlias, removeShim, shimExists, getShimsDir, getShimPath, getPathShadowingExecutable, isShimsInPath, getPathSetupInstructions, addShimsToPath, switchConfigSymlink, switchHomeFileSymlinks, } from '../lib/shims.js';
12
12
  import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
13
13
  import { tryAutoPull } from '../lib/git.js';
@@ -93,7 +93,130 @@ function warnIfShimShadowed(agent) {
93
93
  console.log(chalk.gray(` Managed shim: ${getShimPath(agent)}`));
94
94
  console.log(chalk.gray(` ${getPathSetupInstructions().split('\n').join('\n ')}`));
95
95
  }
96
- /** Register `agents add`, `agents remove`, `agents use`, and `agents list` (deprecated). */
96
+ async function versionPruneAction(specs, options, commandName) {
97
+ const isProject = options.project;
98
+ const moved = [];
99
+ for (const spec of specs) {
100
+ const parsed = parseAgentSpec(spec);
101
+ if (!parsed) {
102
+ console.log(chalk.red(`Invalid agent: ${spec}`));
103
+ console.log(chalk.gray(`Format: <agent>[@version]. Available: ${ALL_AGENT_IDS.join(', ')}`));
104
+ continue;
105
+ }
106
+ const { agent, version } = parsed;
107
+ const agentConfig = AGENTS[agent];
108
+ if (version === 'latest' || !spec.includes('@')) {
109
+ const versions = listInstalledVersions(agent);
110
+ if (versions.length === 0) {
111
+ console.log(chalk.gray(`No versions of ${agentLabel(agentConfig.id)} installed`));
112
+ continue;
113
+ }
114
+ if (!isInteractiveTerminal()) {
115
+ requireInteractiveSelection(`Selecting ${agentLabel(agentConfig.id)} versions to ${commandName}`, [
116
+ `agents ${commandName} ${agent}@${versions[0]}`,
117
+ ]);
118
+ }
119
+ const globalDefault = getGlobalDefault(agent);
120
+ const sortedVersions = [...versions].sort((a, b) => {
121
+ if (a === globalDefault)
122
+ return -1;
123
+ if (b === globalDefault)
124
+ return 1;
125
+ return 0;
126
+ });
127
+ try {
128
+ const toRemove = await checkbox({
129
+ message: `Select ${agentLabel(agentConfig.id)} versions to ${commandName}:`,
130
+ choices: sortedVersions.map((v) => ({
131
+ name: v === globalDefault ? `${v} ${chalk.green('(default)')}` : v,
132
+ value: v,
133
+ checked: false,
134
+ })),
135
+ });
136
+ if (toRemove.length === 0) {
137
+ console.log(chalk.gray('No versions selected'));
138
+ continue;
139
+ }
140
+ for (const v of toRemove) {
141
+ const versionDir = getVersionDir(agent, v);
142
+ removeVersion(agent, v);
143
+ fixSessionFilePaths(agent, v, versionDir);
144
+ console.log(chalk.green(`Moved ${agentLabel(agentConfig.id)}@${v} to trash`));
145
+ moved.push({ agent, version: v });
146
+ }
147
+ if (globalDefault && toRemove.includes(globalDefault)) {
148
+ setGlobalDefault(agent, undefined);
149
+ console.log(chalk.yellow(`Default version removed. Run: agents use ${agent}@<version> to set a new default`));
150
+ }
151
+ const remaining = listInstalledVersions(agent);
152
+ if (remaining.length === 0) {
153
+ removeShim(agent);
154
+ }
155
+ }
156
+ catch (err) {
157
+ if (isPromptCancelled(err)) {
158
+ console.log(chalk.gray('Cancelled'));
159
+ continue;
160
+ }
161
+ throw err;
162
+ }
163
+ }
164
+ else if (!isVersionInstalled(agent, version)) {
165
+ console.log(chalk.gray(`${agentLabel(agentConfig.id)}@${version} not installed`));
166
+ }
167
+ else {
168
+ const versionDir = getVersionDir(agent, version);
169
+ removeVersion(agent, version);
170
+ fixSessionFilePaths(agent, version, versionDir);
171
+ console.log(chalk.green(`Moved ${agentLabel(agentConfig.id)}@${version} to trash`));
172
+ moved.push({ agent, version });
173
+ const remaining = listInstalledVersions(agent);
174
+ if (remaining.length === 0) {
175
+ removeShim(agent);
176
+ }
177
+ }
178
+ if (isProject) {
179
+ const projectManifestPath = path.join(process.cwd(), '.agents', 'agents.yaml');
180
+ if (fs.existsSync(projectManifestPath)) {
181
+ const manifest = readManifest(process.cwd());
182
+ if (manifest?.agents?.[agent]) {
183
+ delete manifest.agents[agent];
184
+ writeManifest(process.cwd(), manifest);
185
+ console.log(chalk.gray(` Removed from .agents/agents.yaml`));
186
+ }
187
+ }
188
+ }
189
+ }
190
+ printTrashFooter(moved);
191
+ }
192
+ function configureVersionPruneCommand(cmd, commandName) {
193
+ const isAlias = commandName === 'remove';
194
+ cmd
195
+ .description(isAlias
196
+ ? 'Alias for agents prune. Uninstalls agent CLI versions.'
197
+ : 'Uninstall agent CLI versions. Moves version data to trash for recovery.')
198
+ .option('-p, --project', 'Also clear the pinned version from .agents/agents.yaml in the current project');
199
+ setHelpSections(cmd, {
200
+ examples: `
201
+ # Prune a specific version
202
+ agents ${commandName} claude@2.0.50
203
+
204
+ # Pick interactively if you omit the version
205
+ agents ${commandName} claude
206
+
207
+ # Prune and also clear the project pin
208
+ agents ${commandName} claude@2.0.50 --project
209
+ `,
210
+ notes: `
211
+ - Pruned version directories move to trash with their home/ data intact.
212
+ - Session file paths are rewritten so session history remains readable.
213
+ - Removing the default version unsets the default; run 'agents use' to pick a new one.
214
+ - Reinstall any time with 'agents add'.
215
+ `,
216
+ });
217
+ cmd.action((specs, options) => versionPruneAction(specs, options, commandName));
218
+ }
219
+ /** Register `agents add`, `agents prune`, `agents remove`, `agents use`, and `agents list` (deprecated). */
97
220
  export function registerVersionsCommands(program) {
98
221
  const addCmd = program
99
222
  .command('add <specs...>')
@@ -195,9 +318,9 @@ export function registerVersionsCommands(program) {
195
318
  selection = userSelection;
196
319
  }
197
320
  }
198
- else if (hasNewResources(newResources, agent)) {
321
+ else if (hasNewResources(newResources, agent, installedVersion)) {
199
322
  // Some synced, but NEW resources available - prompt for new only
200
- const userSelection = await promptNewResourceSelection(agent, newResources);
323
+ const userSelection = await promptNewResourceSelection(agent, newResources, installedVersion);
201
324
  if (userSelection) {
202
325
  selection = userSelection;
203
326
  }
@@ -311,127 +434,8 @@ export function registerVersionsCommands(program) {
311
434
  }
312
435
  }
313
436
  });
314
- const removeCmd = program
315
- .command('remove <specs...>')
316
- .description('Uninstall agent CLI versions. Frees disk space and removes the version\'s auth token.')
317
- .option('-p, --project', 'Also clear the pinned version from .agents/agents.yaml in the current project');
318
- setHelpSections(removeCmd, {
319
- examples: `
320
- # Remove a specific version
321
- agents remove claude@2.0.50
322
-
323
- # Pick interactively if you omit the version
324
- agents remove claude
325
-
326
- # Remove and also clear the project pin
327
- agents remove claude@2.0.50 --project
328
- `,
329
- notes: `
330
- - Removing the default version unsets the default; run 'agents use' to pick a new one.
331
- - Reinstall any time with 'agents add'.
332
- `,
333
- });
334
- removeCmd.action(async (specs, options) => {
335
- const isProject = options.project;
336
- for (const spec of specs) {
337
- const parsed = parseAgentSpec(spec);
338
- if (!parsed) {
339
- console.log(chalk.red(`Invalid agent: ${spec}`));
340
- console.log(chalk.gray(`Format: <agent>[@version]. Available: ${ALL_AGENT_IDS.join(', ')}`));
341
- continue;
342
- }
343
- const { agent, version } = parsed;
344
- const agentConfig = AGENTS[agent];
345
- if (version === 'latest' || !spec.includes('@')) {
346
- // Show picker for which versions to remove
347
- const versions = listInstalledVersions(agent);
348
- if (versions.length === 0) {
349
- console.log(chalk.gray(`No versions of ${agentLabel(agentConfig.id)} installed`));
350
- continue;
351
- }
352
- if (!isInteractiveTerminal()) {
353
- requireInteractiveSelection(`Selecting ${agentLabel(agentConfig.id)} versions to remove`, [
354
- `agents remove ${agent}@${versions[0]}`,
355
- ]);
356
- }
357
- const globalDefault = getGlobalDefault(agent);
358
- // Sort versions with default first
359
- const sortedVersions = [...versions].sort((a, b) => {
360
- if (a === globalDefault)
361
- return -1;
362
- if (b === globalDefault)
363
- return 1;
364
- return 0;
365
- });
366
- try {
367
- const toRemove = await checkbox({
368
- message: `Select ${agentLabel(agentConfig.id)} versions to remove:`,
369
- choices: sortedVersions.map((v) => ({
370
- name: v === globalDefault ? `${v} ${chalk.green('(default)')}` : v,
371
- value: v,
372
- checked: false, // All unchecked by default
373
- })),
374
- });
375
- if (toRemove.length === 0) {
376
- console.log(chalk.gray('No versions selected'));
377
- continue;
378
- }
379
- for (const v of toRemove) {
380
- const versionDir = getVersionDir(agent, v);
381
- removeVersion(agent, v);
382
- fixSessionFilePaths(agent, v, versionDir);
383
- console.log(chalk.green(`Removed ${agentLabel(agentConfig.id)}@${v}`));
384
- }
385
- // Check if default was removed
386
- if (globalDefault && toRemove.includes(globalDefault)) {
387
- setGlobalDefault(agent, undefined);
388
- console.log(chalk.yellow(`Default version removed. Run: agents use ${agent}@<version> to set a new default`));
389
- }
390
- // Remove shim if no versions left
391
- const remaining = listInstalledVersions(agent);
392
- if (remaining.length === 0) {
393
- removeShim(agent);
394
- }
395
- }
396
- catch (err) {
397
- if (isPromptCancelled(err)) {
398
- console.log(chalk.gray('Cancelled'));
399
- continue;
400
- }
401
- throw err;
402
- }
403
- }
404
- else {
405
- // Remove specific version
406
- if (!isVersionInstalled(agent, version)) {
407
- console.log(chalk.gray(`${agentLabel(agentConfig.id)}@${version} not installed`));
408
- }
409
- else {
410
- const versionDir = getVersionDir(agent, version);
411
- removeVersion(agent, version);
412
- fixSessionFilePaths(agent, version, versionDir);
413
- console.log(chalk.green(`Removed ${agentLabel(agentConfig.id)}@${version}`));
414
- // Remove shim if no versions left
415
- const remaining = listInstalledVersions(agent);
416
- if (remaining.length === 0) {
417
- removeShim(agent);
418
- }
419
- }
420
- }
421
- // Update project manifest if -p flag
422
- if (isProject) {
423
- const projectManifestPath = path.join(process.cwd(), '.agents', 'agents.yaml');
424
- if (fs.existsSync(projectManifestPath)) {
425
- const manifest = readManifest(process.cwd());
426
- if (manifest?.agents?.[agent]) {
427
- delete manifest.agents[agent];
428
- writeManifest(process.cwd(), manifest);
429
- console.log(chalk.gray(` Removed from .agents/agents.yaml`));
430
- }
431
- }
432
- }
433
- }
434
- });
437
+ configureVersionPruneCommand(program.command('prune <specs...>'), 'prune');
438
+ configureVersionPruneCommand(program.command('remove <specs...>', { hidden: true }), 'remove');
435
439
  const useCmd = program
436
440
  .command('use <agent> [version]')
437
441
  .description('Switch the active version for an agent. This is the only command that sets the default.')
@@ -638,9 +642,9 @@ export function registerVersionsCommands(program) {
638
642
  }
639
643
  }
640
644
  }
641
- else if (hasNewResources(newResources, agentId)) {
645
+ else if (hasNewResources(newResources, agentId, finalVersion)) {
642
646
  // Has synced before, but NEW items available
643
- const userSelection = await promptNewResourceSelection(agentId, newResources);
647
+ const userSelection = await promptNewResourceSelection(agentId, newResources, finalVersion);
644
648
  if (userSelection && Object.keys(userSelection).length > 0) {
645
649
  const syncResult = syncResourcesToVersion(agentId, finalVersion, userSelection);
646
650
  const syncedTypes = [];
@@ -5,7 +5,7 @@ import * as path from 'path';
5
5
  import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
6
6
  import { formatUsageSection, formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
7
7
  import { readManifest } from '../lib/manifest.js';
8
- import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, resolveVersionAlias, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, } from '../lib/versions.js';
8
+ import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, resolveVersionAlias, getAvailableResources, getActuallySyncedResources, getNewResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, } from '../lib/versions.js';
9
9
  import { getShimsDir, isShimsInPath, ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
10
10
  import { getAgentResources } from '../lib/resources.js';
11
11
  import { WORKFLOW_CAPABLE_AGENTS } from '../lib/workflows.js';
@@ -366,7 +366,7 @@ async function showInstalledVersions(filterAgentId) {
366
366
  const newResources = getNewResources(available, synced);
367
367
  if (hasNewResources(newResources, filterAgentId, defaultVersion)) {
368
368
  try {
369
- const selection = await promptNewResourceSelection(filterAgentId, newResources);
369
+ const selection = await promptNewResourceSelection(filterAgentId, newResources, defaultVersion);
370
370
  if (selection && Object.keys(selection).length > 0) {
371
371
  const result = syncResourcesToVersion(filterAgentId, defaultVersion, selection);
372
372
  const synced = [];
@@ -833,13 +833,12 @@ async function buildAgentPrunePlan(agentId) {
833
833
  return { agentId, toPrune, skippedDefaults };
834
834
  }
835
835
  async function executePrunePlan(plan) {
836
- let removed = 0;
836
+ const moved = [];
837
837
  for (const p of plan.toPrune) {
838
- console.log(chalk.gray(`Removing ${agentLabel(p.agentId)}@${p.version}...`));
839
838
  const ok = removeVersion(p.agentId, p.version);
840
839
  if (ok) {
841
- console.log(chalk.green(`Removed ${agentLabel(p.agentId)}@${p.version}`));
842
- removed++;
840
+ console.log(chalk.green(`Moved ${agentLabel(p.agentId)}@${p.version} to trash`));
841
+ moved.push({ agent: p.agentId, version: p.version });
843
842
  }
844
843
  else {
845
844
  console.log(chalk.yellow(`Already gone: ${agentLabel(p.agentId)}@${p.version}`));
@@ -848,7 +847,7 @@ async function executePrunePlan(plan) {
848
847
  if (listInstalledVersions(plan.agentId).length === 0) {
849
848
  removeShim(plan.agentId);
850
849
  }
851
- return removed;
850
+ return moved;
852
851
  }
853
852
  function printPrunePlan(plan, isFirst) {
854
853
  if (plan.skippedDefaults.length > 0) {
@@ -897,7 +896,7 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
897
896
  return;
898
897
  }
899
898
  const totalCandidates = actionable.reduce((n, plan) => n + plan.toPrune.length, 0);
900
- let totalRemoved = 0;
899
+ const allMoved = [];
901
900
  let isFirst = true;
902
901
  let processedAny = false;
903
902
  for (const plan of actionable) {
@@ -916,10 +915,10 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
916
915
  if (!isInteractiveTerminal()) {
917
916
  console.log(chalk.red('Refusing to prune in a non-interactive shell without --yes.'));
918
917
  if (filterAgentId) {
919
- console.log(chalk.gray(`Re-run with: agents prune ${filterAgentId} --dry-run`));
918
+ console.log(chalk.gray(`Re-run with: agents prune cleanup ${filterAgentId} --dry-run`));
920
919
  }
921
920
  else {
922
- console.log(chalk.gray('Re-run with: agents prune --dry-run'));
921
+ console.log(chalk.gray('Re-run with: agents prune cleanup --dry-run'));
923
922
  }
924
923
  process.exit(1);
925
924
  }
@@ -943,7 +942,7 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
943
942
  break;
944
943
  }
945
944
  }
946
- totalRemoved += await executePrunePlan(plan);
945
+ allMoved.push(...(await executePrunePlan(plan)));
947
946
  processedAny = true;
948
947
  isFirst = false;
949
948
  console.log();
@@ -953,7 +952,8 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
953
952
  return;
954
953
  }
955
954
  if (processedAny) {
956
- console.log(chalk.bold(`Pruned ${totalRemoved} version${totalRemoved === 1 ? '' : 's'}.`));
955
+ console.log(chalk.bold(`Pruned ${allMoved.length} version${allMoved.length === 1 ? '' : 's'}.`));
956
+ printTrashFooter(allMoved);
957
957
  }
958
958
  }
959
959
  /**
package/dist/computer.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -66,6 +66,7 @@ import { registerSkillsCommands } from './commands/skills.js';
66
66
  import { registerRulesCommands } from './commands/rules.js';
67
67
  import { registerPermissionsCommands } from './commands/permissions.js';
68
68
  import { registerMcpCommands } from './commands/mcp.js';
69
+ import { registerCliCommands } from './commands/cli.js';
69
70
  import { registerVersionsCommands } from './commands/versions.js';
70
71
  import { registerImportCommand } from './commands/import.js';
71
72
  import { registerPackagesCommands } from './commands/packages.js';
@@ -120,11 +121,12 @@ Quick start:
120
121
  agents sessions Browse past sessions across all agents
121
122
 
122
123
  Agent versions:
123
- add <agent>[@version] Install an agent CLI (e.g. agents add codex)
124
+ add <agent>[@version] Install an agent CLI (e.g. agents add grok or agents add codex)
124
125
  import <agent> Adopt an existing global install (npm/homebrew) into agents-cli
125
- remove <agent>[@version] Uninstall a version
126
+ prune <agent>[@version] Uninstall a version
127
+ remove <agent>[@version] Alias for prune
126
128
  use <agent>@<version> Set the default version
127
- prune [target] Remove orphan resources and older duplicate version installs (targets: commands, sessions, runs, trash)
129
+ prune cleanup [target] Remove orphan resources and older duplicate version installs
128
130
  trash Inspect and restore soft-deleted version directories
129
131
  view [agent[@version]] List versions, or inspect one in detail
130
132
 
@@ -171,7 +173,7 @@ Automation tips:
171
173
  Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
172
174
  Use --yes for defaults Auto-accept sync/default prompts on add/use/pull
173
175
  Use --names for central items e.g. agents commands add --names review-pr,debug
174
- Use agent@version targets e.g. --agents claude@2.1.79,codex@default
176
+ Use agent@version targets e.g. --agents grok@0.1.218,claude@2.1.79,codex@default
175
177
  Non-TTY shells apply defaults Omitted required selections fail with a plain hint
176
178
 
177
179
  Options:
@@ -361,7 +363,13 @@ async function promptUpgrade(latestVersion) {
361
363
  console.log();
362
364
  }
363
365
  }
364
- /** Fire-and-forget: refresh the registry cache in background. Never blocks the command. */
366
+ /**
367
+ * Background update check — fires once per 24h cache window.
368
+ * Network: GET registry.npmjs.org/@phnx-labs/agents-cli/latest.
369
+ * Disable: set AGENTS_CLI_DISABLE_AUTO_UPDATE=1 in shell rc.
370
+ *
371
+ * Fire-and-forget; never blocks the CLI's foreground operation.
372
+ */
365
373
  function refreshUpdateCacheInBackground() {
366
374
  fetch('https://registry.npmjs.org/@phnx-labs/agents-cli/latest', {
367
375
  signal: AbortSignal.timeout(2000),
@@ -529,6 +537,7 @@ program
529
537
  await program.parseAsync(['node', 'agents', ...args]);
530
538
  });
531
539
  registerMcpCommands(program);
540
+ registerCliCommands(program);
532
541
  registerSubagentsCommands(program);
533
542
  registerPluginsCommands(program);
534
543
  registerWorkflowsCommands(program);
@@ -726,7 +735,13 @@ if (firstRun) {
726
735
  // Every command requires the system repo to be cloned first. `setup` is the
727
736
  // only exemption — it's the command that does the cloning.
728
737
  const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help']);
729
- if (!firstRun && requestedCommand && !SETUP_EXEMPT_COMMANDS.has(requestedCommand)) {
738
+ // Help and version output are pure documentation — they must never gate on
739
+ // setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
740
+ const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
741
+ if (!firstRun &&
742
+ requestedCommand &&
743
+ !SETUP_EXEMPT_COMMANDS.has(requestedCommand) &&
744
+ !helpOrVersionRequested) {
730
745
  const { ensureInitialized } = await import('./commands/setup.js');
731
746
  await ensureInitialized(program);
732
747
  }
@@ -771,5 +786,18 @@ catch (err) {
771
786
  if (err instanceof Error && err.name === 'ExitPromptError') {
772
787
  process.exit(130);
773
788
  }
789
+ // Browser-daemon-not-running and CDP-not-reachable surface as typed errors
790
+ // from src/lib/browser/. Don't dump a Node stacktrace for these — they are
791
+ // user-actionable, not engineering bugs. See issues #41 and #43.
792
+ if (err instanceof Error) {
793
+ const isBrowserDaemonNotRunning = err.name === 'BrowserDaemonNotRunningError';
794
+ const isBrowserCdpUnreachable = err.name === 'BrowserCdpConnectionError';
795
+ const isBrowserIpcDown = err.message.startsWith('IPC error:') &&
796
+ (err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT'));
797
+ if (isBrowserDaemonNotRunning || isBrowserCdpUnreachable || isBrowserIpcDown) {
798
+ console.error(err.message);
799
+ process.exit(1);
800
+ }
801
+ }
774
802
  throw err;
775
803
  }
@@ -50,6 +50,14 @@ export const ACP_HARNESSES = {
50
50
  confidence: 'documented',
51
51
  source: 'https://docs.openclaw.ai/tools/acp-agents',
52
52
  },
53
+ grok: {
54
+ command: 'grok',
55
+ args: ['agent', 'stdio'],
56
+ installHint: 'see https://docs.x.ai/build/cli',
57
+ confidence: 'documented',
58
+ source: 'https://docs.x.ai/build/cli/headless-scripting',
59
+ },
60
+ // antigravity: no documented ACP support (May 2026).
53
61
  // goose: ACP over HTTP via `goosed`, not a clean stdio subcommand.
54
62
  // copilot, kiro: excluded for now (not installed in the reference environment,
55
63
  // no local verification possible).