@phnx-labs/agents-cli 1.19.2 → 1.20.3

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 (156) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/README.md +72 -12
  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/cloud.js +1 -1
  8. package/dist/commands/commands.js +27 -10
  9. package/dist/commands/computer.js +18 -1
  10. package/dist/commands/doctor.d.ts +1 -1
  11. package/dist/commands/doctor.js +2 -2
  12. package/dist/commands/exec.js +38 -18
  13. package/dist/commands/factory.d.ts +3 -14
  14. package/dist/commands/factory.js +3 -3
  15. package/dist/commands/feedback.d.ts +7 -0
  16. package/dist/commands/feedback.js +89 -0
  17. package/dist/commands/helper.d.ts +12 -0
  18. package/dist/commands/helper.js +87 -0
  19. package/dist/commands/hooks.js +89 -10
  20. package/dist/commands/mcp.js +166 -10
  21. package/dist/commands/packages.js +196 -27
  22. package/dist/commands/permissions.js +21 -6
  23. package/dist/commands/plugins.js +11 -4
  24. package/dist/commands/profiles.d.ts +8 -0
  25. package/dist/commands/profiles.js +118 -5
  26. package/dist/commands/prune.js +39 -160
  27. package/dist/commands/pull.js +58 -5
  28. package/dist/commands/routines.js +107 -14
  29. package/dist/commands/rules.js +8 -4
  30. package/dist/commands/secrets-migrate.d.ts +24 -0
  31. package/dist/commands/secrets-migrate.js +198 -0
  32. package/dist/commands/secrets-sync.d.ts +11 -0
  33. package/dist/commands/secrets-sync.js +155 -0
  34. package/dist/commands/secrets.js +79 -46
  35. package/dist/commands/sessions.d.ts +28 -0
  36. package/dist/commands/sessions.js +98 -33
  37. package/dist/commands/setup.d.ts +1 -0
  38. package/dist/commands/setup.js +37 -28
  39. package/dist/commands/skills.js +25 -8
  40. package/dist/commands/subagents.js +69 -49
  41. package/dist/commands/teams.js +61 -10
  42. package/dist/commands/utils.d.ts +33 -0
  43. package/dist/commands/utils.js +139 -0
  44. package/dist/commands/versions.d.ts +4 -3
  45. package/dist/commands/versions.js +134 -130
  46. package/dist/commands/view.d.ts +6 -0
  47. package/dist/commands/view.js +175 -19
  48. package/dist/commands/workflows.js +29 -6
  49. package/dist/computer.js +0 -0
  50. package/dist/index.js +38 -6
  51. package/dist/lib/acp/client.js +6 -1
  52. package/dist/lib/acp/harnesses.js +8 -0
  53. package/dist/lib/agents.d.ts +4 -0
  54. package/dist/lib/agents.js +125 -34
  55. package/dist/lib/auto-pull-worker.js +18 -1
  56. package/dist/lib/browser/cdp.d.ts +8 -1
  57. package/dist/lib/browser/cdp.js +40 -3
  58. package/dist/lib/browser/chrome.d.ts +13 -0
  59. package/dist/lib/browser/chrome.js +46 -3
  60. package/dist/lib/browser/domain-skills.d.ts +51 -0
  61. package/dist/lib/browser/domain-skills.js +157 -0
  62. package/dist/lib/browser/drivers/local.js +45 -4
  63. package/dist/lib/browser/drivers/ssh.js +2 -2
  64. package/dist/lib/browser/ipc.d.ts +8 -1
  65. package/dist/lib/browser/ipc.js +37 -28
  66. package/dist/lib/browser/profiles.d.ts +16 -3
  67. package/dist/lib/browser/profiles.js +44 -4
  68. package/dist/lib/browser/service.d.ts +3 -0
  69. package/dist/lib/browser/service.js +40 -5
  70. package/dist/lib/browser/types.d.ts +11 -4
  71. package/dist/lib/cli-resources.d.ts +137 -0
  72. package/dist/lib/cli-resources.js +477 -0
  73. package/dist/lib/cloud/factory.d.ts +1 -1
  74. package/dist/lib/cloud/factory.js +1 -1
  75. package/dist/lib/cloud/rush.js +5 -5
  76. package/dist/lib/command-skills.js +0 -2
  77. package/dist/lib/computer-rpc.d.ts +3 -0
  78. package/dist/lib/computer-rpc.js +53 -0
  79. package/dist/lib/daemon.js +20 -0
  80. package/dist/lib/events.d.ts +16 -2
  81. package/dist/lib/events.js +33 -2
  82. package/dist/lib/exec.d.ts +42 -13
  83. package/dist/lib/exec.js +127 -33
  84. package/dist/lib/help.js +11 -5
  85. package/dist/lib/hooks/cache.d.ts +38 -0
  86. package/dist/lib/hooks/cache.js +242 -0
  87. package/dist/lib/hooks/profile.d.ts +33 -0
  88. package/dist/lib/hooks/profile.js +129 -0
  89. package/dist/lib/hooks.d.ts +0 -10
  90. package/dist/lib/hooks.js +246 -11
  91. package/dist/lib/mcp.d.ts +15 -0
  92. package/dist/lib/mcp.js +46 -0
  93. package/dist/lib/migrate.js +1 -1
  94. package/dist/lib/overdue.d.ts +26 -0
  95. package/dist/lib/overdue.js +101 -0
  96. package/dist/lib/permissions.d.ts +13 -0
  97. package/dist/lib/permissions.js +55 -1
  98. package/dist/lib/plugin-marketplace.js +1 -1
  99. package/dist/lib/plugins.js +15 -1
  100. package/dist/lib/profiles-presets.d.ts +26 -0
  101. package/dist/lib/profiles-presets.js +216 -0
  102. package/dist/lib/profiles.d.ts +34 -0
  103. package/dist/lib/profiles.js +112 -1
  104. package/dist/lib/resources/mcp.js +37 -0
  105. package/dist/lib/resources.d.ts +1 -1
  106. package/dist/lib/rotate.js +10 -4
  107. package/dist/lib/routines-format.d.ts +47 -0
  108. package/dist/lib/routines-format.js +194 -0
  109. package/dist/lib/routines.d.ts +8 -2
  110. package/dist/lib/routines.js +34 -14
  111. package/dist/lib/runner.js +83 -15
  112. package/dist/lib/scheduler.js +8 -1
  113. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  114. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  115. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
  116. package/dist/lib/secrets/bundles.d.ts +34 -17
  117. package/dist/lib/secrets/bundles.js +210 -36
  118. package/dist/lib/secrets/index.d.ts +49 -30
  119. package/dist/lib/secrets/index.js +126 -115
  120. package/dist/lib/secrets/install-helper.d.ts +45 -0
  121. package/dist/lib/secrets/install-helper.js +165 -0
  122. package/dist/lib/secrets/linux.js +4 -4
  123. package/dist/lib/secrets/sync.d.ts +56 -0
  124. package/dist/lib/secrets/sync.js +180 -0
  125. package/dist/lib/session/active.d.ts +8 -0
  126. package/dist/lib/session/active.js +3 -2
  127. package/dist/lib/session/db.d.ts +0 -4
  128. package/dist/lib/session/db.js +0 -26
  129. package/dist/lib/session/parse.d.ts +1 -0
  130. package/dist/lib/session/parse.js +44 -0
  131. package/dist/lib/session/render.js +4 -4
  132. package/dist/lib/session/types.d.ts +2 -2
  133. package/dist/lib/session/types.js +1 -1
  134. package/dist/lib/shims.d.ts +5 -2
  135. package/dist/lib/shims.js +70 -38
  136. package/dist/lib/state.d.ts +14 -2
  137. package/dist/lib/state.js +51 -20
  138. package/dist/lib/teams/agents.d.ts +5 -4
  139. package/dist/lib/teams/agents.js +48 -22
  140. package/dist/lib/teams/api.d.ts +2 -1
  141. package/dist/lib/teams/api.js +4 -3
  142. package/dist/lib/teams/parsers.d.ts +1 -1
  143. package/dist/lib/teams/parsers.js +153 -3
  144. package/dist/lib/teams/summarizer.js +18 -2
  145. package/dist/lib/teams/worktree.js +14 -3
  146. package/dist/lib/types.d.ts +63 -4
  147. package/dist/lib/types.js +8 -3
  148. package/dist/lib/usage.d.ts +27 -2
  149. package/dist/lib/usage.js +100 -17
  150. package/dist/lib/versions.d.ts +45 -3
  151. package/dist/lib/versions.js +455 -60
  152. package/package.json +15 -14
  153. package/scripts/install-helper.js +97 -0
  154. package/scripts/postinstall.js +16 -0
  155. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  156. package/npm-shrinkwrap.json +0 -3162
@@ -3,9 +3,9 @@ import ora from 'ora';
3
3
  import * as fs from 'fs';
4
4
  import * as path from 'path';
5
5
  import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
6
- import { formatUsageSection, formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
6
+ import { formatUsageSection, formatUsageSummary, formatUsageStatusBadge, 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, getProjectOnlyResources, 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';
@@ -16,8 +16,33 @@ import { isGitRepo, getGitSyncStatus } from '../lib/git.js';
16
16
  import { getCentralRulesFileName } from '../lib/rules/rules.js';
17
17
  import { composeRulesFromState } from '../lib/rules/compose.js';
18
18
  import { getConfiguredRunStrategy } from '../lib/rotate.js';
19
+ import { listProfiles, profileSummary } from '../lib/profiles.js';
19
20
  import { confirm } from '@inquirer/prompts';
20
21
  import { formatPath, isInteractiveTerminal, isPromptCancelled } from './utils.js';
22
+ /**
23
+ * Group profile summaries by their host harness, optionally filtered to a
24
+ * single agent. Profile YAMLs that fail validation are silently skipped by
25
+ * `listProfiles` so this never throws on a malformed file.
26
+ */
27
+ function getProfilesByAgent(filterAgentId) {
28
+ const byAgent = new Map();
29
+ for (const profile of listProfiles()) {
30
+ if (filterAgentId && profile.host.agent !== filterAgentId)
31
+ continue;
32
+ const summary = profileSummary(profile);
33
+ const existing = byAgent.get(profile.host.agent);
34
+ if (existing)
35
+ existing.push(summary);
36
+ else
37
+ byAgent.set(profile.host.agent, [summary]);
38
+ }
39
+ return byAgent;
40
+ }
41
+ /** Build the usage-column equivalent for a profile row: "profile <model>". */
42
+ function profileKindAndModel(model, planWidth) {
43
+ const kind = 'profile'.padEnd(Math.max(planWidth, 'profile'.length));
44
+ return `${kind} ${model}`;
45
+ }
21
46
  function termLink(text, filePath) {
22
47
  const url = `file://${filePath}`;
23
48
  return `\x1b]8;;${url}\x1b\\${text}\x1b]8;;\x1b\\`;
@@ -88,6 +113,32 @@ function getProjectVersionFromCwd(agent) {
88
113
  return null;
89
114
  }
90
115
  }
116
+ function getProfileSummaries(filterAgentId) {
117
+ return listProfiles()
118
+ .filter((profile) => !filterAgentId || profile.host.agent === filterAgentId)
119
+ .map(profileSummary);
120
+ }
121
+ function renderProfilesSection(profiles) {
122
+ if (profiles.length === 0)
123
+ return;
124
+ const nameWidth = Math.max(4, ...profiles.map((p) => p.name.length));
125
+ const hostWidth = Math.max(4, ...profiles.map((p) => p.host.length));
126
+ const providerWidth = Math.max(8, ...profiles.map((p) => p.provider.length));
127
+ console.log(chalk.bold('Profiles\n'));
128
+ console.log(` ${chalk.gray('NAME'.padEnd(nameWidth))} ` +
129
+ `${chalk.gray('HOST'.padEnd(hostWidth))} ` +
130
+ `${chalk.gray('PROVIDER'.padEnd(providerWidth))} ` +
131
+ chalk.gray('MODEL'));
132
+ for (const profile of profiles) {
133
+ console.log(` ${chalk.cyan(profile.name.padEnd(nameWidth))} ` +
134
+ `${profile.host.padEnd(hostWidth)} ` +
135
+ `${profile.provider.padEnd(providerWidth)} ` +
136
+ chalk.gray(profile.model));
137
+ }
138
+ console.log(chalk.gray('\n Run: agents run <profile> [prompt]'));
139
+ console.log(chalk.gray(' agents profiles view <profile>'));
140
+ console.log();
141
+ }
91
142
  /**
92
143
  * Show installed versions for one or all agents.
93
144
  * Called when: `agents view` or `agents view claude`
@@ -101,6 +152,8 @@ async function showInstalledVersions(filterAgentId) {
101
152
  spinner.stop();
102
153
  const agentsToShow = filterAgentId ? [filterAgentId] : ALL_AGENT_IDS;
103
154
  const showPaths = !!filterAgentId;
155
+ const profilesByAgent = getProfilesByAgent(filterAgentId);
156
+ const profileSummaries = [...profilesByAgent.values()].flat();
104
157
  // Auto-heal stale versioned aliases. Pre-v2 aliases (e.g. pre-CLAUDE_CONFIG_DIR
105
158
  // claude shims) silently route login through the default version's symlinked
106
159
  // home, so `agents view` would never reflect the right account. Regenerate on
@@ -190,15 +243,20 @@ async function showInstalledVersions(filterAgentId) {
190
243
  // Separate version-managed from globally-installed agents
191
244
  const versionManaged = [];
192
245
  const globallyInstalled = [];
246
+ const profileOnly = [];
193
247
  for (const agentId of agentsToShow) {
194
248
  const versions = listInstalledVersions(agentId);
195
249
  const cliState = cliStates[agentId];
250
+ const hasProfiles = (profilesByAgent.get(agentId)?.length ?? 0) > 0;
196
251
  if (versions.length > 0) {
197
252
  versionManaged.push(agentId);
198
253
  }
199
254
  else if (cliState?.installed) {
200
255
  globallyInstalled.push(agentId);
201
256
  }
257
+ else if (hasProfiles) {
258
+ profileOnly.push(agentId);
259
+ }
202
260
  }
203
261
  // Show version-managed agents
204
262
  if (versionManaged.length > 0) {
@@ -207,6 +265,7 @@ async function showInstalledVersions(filterAgentId) {
207
265
  let maxEmail = 0;
208
266
  let maxPlanWidth = 3;
209
267
  let maxUsageWidth = 0;
268
+ let maxStatusWidth = 0;
210
269
  for (const agentId of versionManaged) {
211
270
  const versions = listInstalledVersions(agentId);
212
271
  const globalDefault = getGlobalDefault(agentId);
@@ -220,8 +279,14 @@ async function showInstalledVersions(filterAgentId) {
220
279
  if (info?.plan)
221
280
  maxPlanWidth = Math.max(maxPlanWidth, info.plan.length);
222
281
  }
282
+ // Profile rows share these columns with version rows so they line up.
283
+ for (const profile of profilesByAgent.get(agentId) ?? []) {
284
+ maxVerLabel = Math.max(maxVerLabel, profile.name.length);
285
+ maxEmail = Math.max(maxEmail, profile.auth.length);
286
+ maxPlanWidth = Math.max(maxPlanWidth, 'profile'.length);
287
+ }
223
288
  }
224
- // Second pass: compute max visible usage width (now that maxPlanWidth is settled)
289
+ // Second pass: compute max visible usage + status widths (now that maxPlanWidth is settled)
225
290
  for (const agentId of versionManaged) {
226
291
  const versions = listInstalledVersions(agentId);
227
292
  for (const v of versions) {
@@ -231,6 +296,12 @@ async function showInstalledVersions(filterAgentId) {
231
296
  const usageInfo = usageKey ? usageByKey.get(usageKey) : undefined;
232
297
  const usageStr = formatUsageSummary(info?.plan || null, usageInfo?.snapshot || null, maxPlanWidth);
233
298
  maxUsageWidth = Math.max(maxUsageWidth, visibleWidth(usageStr));
299
+ const statusStr = formatUsageStatusBadge(info?.usageStatus);
300
+ maxStatusWidth = Math.max(maxStatusWidth, visibleWidth(statusStr));
301
+ }
302
+ for (const profile of profilesByAgent.get(agentId) ?? []) {
303
+ const usageEquivalent = profileKindAndModel(profile.model, maxPlanWidth);
304
+ maxUsageWidth = Math.max(maxUsageWidth, visibleWidth(usageEquivalent));
234
305
  }
235
306
  }
236
307
  for (const agentId of versionManaged) {
@@ -280,6 +351,11 @@ async function showInstalledVersions(filterAgentId) {
280
351
  const usagePad = ' '.repeat(Math.max(0, maxUsageWidth - visibleWidth(usageStr)));
281
352
  parts.push(usageStr + usagePad);
282
353
  }
354
+ const statusStr = formatUsageStatusBadge(vInfo?.usageStatus);
355
+ if (maxStatusWidth > 0) {
356
+ const statusPad = ' '.repeat(Math.max(0, maxStatusWidth - visibleWidth(statusStr)));
357
+ parts.push(statusStr + statusPad);
358
+ }
283
359
  if (hasActive)
284
360
  parts.push(activeStr);
285
361
  }
@@ -289,6 +365,18 @@ async function showInstalledVersions(filterAgentId) {
289
365
  console.log(chalk.gray(` ${versionDir}`));
290
366
  }
291
367
  }
368
+ // Profile rows share the same columns as versions: name | auth | "profile"+model.
369
+ // No status badge, no last-active — profiles don't accumulate usage state.
370
+ for (const profile of profilesByAgent.get(agentId) ?? []) {
371
+ const nameCol = chalk.cyan(profile.name.padEnd(maxVerLabel));
372
+ const authCol = chalk.gray(profile.auth.padEnd(maxEmail));
373
+ const usageEquivalent = profileKindAndModel(profile.model, maxPlanWidth);
374
+ const usagePad = ' '.repeat(Math.max(0, maxUsageWidth - visibleWidth(usageEquivalent)));
375
+ console.log(` ${nameCol} ${authCol} ${chalk.gray(usageEquivalent + usagePad)}`);
376
+ if (showPaths) {
377
+ console.log(chalk.gray(` ${profile.path}`));
378
+ }
379
+ }
292
380
  // Check for project override
293
381
  const projectVersion = getProjectVersionFromCwd(agentId);
294
382
  if (projectVersion && projectVersion !== globalDefault) {
@@ -305,6 +393,19 @@ async function showInstalledVersions(filterAgentId) {
305
393
  const cliState = cliStates[agentId];
306
394
  return `${cliState?.version || 'installed'} (global)`.length;
307
395
  }));
396
+ // Pre-pass: max badge width so rows with `lastActive` line up whether or
397
+ // not THIS row carries a throttle badge. Without this, the row that DOES
398
+ // have "out of credits" shifts every other row's `lastActive` left by
399
+ // ~16 chars, exactly what the version-managed block at maxStatusWidth
400
+ // already solves above.
401
+ let gMaxStatusWidth = 0;
402
+ for (const agentId of globallyInstalled) {
403
+ const gInfoRaw = globalInfoMap.get(agentId);
404
+ const gInfo = gInfoRaw ? mergeCanonical(gInfoRaw) : undefined;
405
+ const w = visibleWidth(formatUsageStatusBadge(gInfo?.usageStatus));
406
+ if (w > gMaxStatusWidth)
407
+ gMaxStatusWidth = w;
408
+ }
308
409
  for (const agentId of globallyInstalled) {
309
410
  const agent = AGENTS[agentId];
310
411
  const cliState = cliStates[agentId];
@@ -323,25 +424,77 @@ async function showInstalledVersions(filterAgentId) {
323
424
  parts.push(gInfo?.email ? chalk.cyan(gInfo.email) : '');
324
425
  if (gUsageStr || gActiveStr)
325
426
  parts.push(gUsageStr);
427
+ const gStatusStr = formatUsageStatusBadge(gInfo?.usageStatus);
428
+ if (gMaxStatusWidth > 0) {
429
+ const statusPad = ' '.repeat(Math.max(0, gMaxStatusWidth - visibleWidth(gStatusStr)));
430
+ parts.push(gStatusStr + statusPad);
431
+ }
326
432
  if (gActiveStr)
327
433
  parts.push(gActiveStr);
328
434
  console.log(parts.join(' '));
329
435
  if (showPaths && cliState?.path) {
330
436
  console.log(chalk.gray(` ${cliState.path}`));
331
437
  }
438
+ // Profile rows under a globally-installed harness. Use a simpler
439
+ // alignment here since this section doesn't share column state with
440
+ // the version-managed block.
441
+ const profilesHere = profilesByAgent.get(agentId) ?? [];
442
+ if (profilesHere.length > 0) {
443
+ const nameWidth = Math.max(globalMaxVerLabel, ...profilesHere.map((p) => p.name.length));
444
+ const authWidth = Math.max(...profilesHere.map((p) => p.auth.length));
445
+ for (const profile of profilesHere) {
446
+ console.log(` ${chalk.cyan(profile.name.padEnd(nameWidth))} ` +
447
+ `${chalk.gray(profile.auth.padEnd(authWidth))} ` +
448
+ `${chalk.gray('profile')} ` +
449
+ chalk.gray(profile.model));
450
+ if (showPaths) {
451
+ console.log(chalk.gray(` ${profile.path}`));
452
+ }
453
+ }
454
+ }
332
455
  if (agent.npmPackage && cliState?.version) {
333
456
  console.log(chalk.gray(` Manage: agents add ${agentId}@${cliState.version} -y`));
334
457
  }
335
458
  console.log();
336
459
  }
337
460
  }
461
+ // Agents with no install but with profiles defined — render under the same
462
+ // harness header so users find them where they look.
463
+ if (profileOnly.length > 0) {
464
+ if (versionManaged.length === 0 && globallyInstalled.length === 0) {
465
+ console.log(chalk.bold('Profile-only Agents\n'));
466
+ }
467
+ for (const agentId of profileOnly) {
468
+ const profilesHere = profilesByAgent.get(agentId) ?? [];
469
+ console.log(` ${chalk.bold(agentLabel(agentId))}${chalk.yellow(' (profile only)')}`);
470
+ const nameWidth = Math.max(...profilesHere.map((p) => p.name.length));
471
+ const authWidth = Math.max(...profilesHere.map((p) => p.auth.length));
472
+ for (const profile of profilesHere) {
473
+ console.log(` ${chalk.cyan(profile.name.padEnd(nameWidth))} ` +
474
+ `${chalk.gray(profile.auth.padEnd(authWidth))} ` +
475
+ `${chalk.gray('profile')} ` +
476
+ chalk.gray(profile.model));
477
+ if (showPaths) {
478
+ console.log(chalk.gray(` ${profile.path}`));
479
+ }
480
+ }
481
+ console.log();
482
+ }
483
+ }
338
484
  // If filtering to a specific agent and not found
339
- if (filterAgentId && versionManaged.length === 0 && globallyInstalled.length === 0) {
485
+ if (filterAgentId &&
486
+ versionManaged.length === 0 &&
487
+ globallyInstalled.length === 0 &&
488
+ profileOnly.length === 0) {
340
489
  console.log(` ${chalk.bold(agentLabel(filterAgentId))}: ${chalk.gray('not installed')}`);
341
490
  console.log();
342
491
  }
343
492
  // No agents installed at all
344
- if (versionManaged.length === 0 && globallyInstalled.length === 0 && !filterAgentId) {
493
+ if (versionManaged.length === 0 &&
494
+ globallyInstalled.length === 0 &&
495
+ profileOnly.length === 0 &&
496
+ profileSummaries.length === 0 &&
497
+ !filterAgentId) {
345
498
  console.log(chalk.gray(' No agent CLIs installed.'));
346
499
  console.log(chalk.gray(' Run: agents add claude@latest'));
347
500
  console.log();
@@ -363,10 +516,11 @@ async function showInstalledVersions(filterAgentId) {
363
516
  if (defaultVersion) {
364
517
  const available = getAvailableResources();
365
518
  const synced = getActuallySyncedResources(filterAgentId, defaultVersion);
366
- const newResources = getNewResources(available, synced);
519
+ const projectOnly = getProjectOnlyResources();
520
+ const newResources = getNewResources(available, synced, projectOnly);
367
521
  if (hasNewResources(newResources, filterAgentId, defaultVersion)) {
368
522
  try {
369
- const selection = await promptNewResourceSelection(filterAgentId, newResources);
523
+ const selection = await promptNewResourceSelection(filterAgentId, newResources, defaultVersion);
370
524
  if (selection && Object.keys(selection).length > 0) {
371
525
  const result = syncResourcesToVersion(filterAgentId, defaultVersion, selection);
372
526
  const synced = [];
@@ -742,6 +896,7 @@ async function collectAgentsJson(filterAgentId) {
742
896
  email: info.email,
743
897
  plan: info.plan,
744
898
  usageStatus: info.usageStatus,
899
+ overageCredits: info.overageCredits,
745
900
  windows: snapshot
746
901
  ? snapshot.windows.map((w) => ({
747
902
  key: w.key,
@@ -758,6 +913,7 @@ async function collectAgentsJson(filterAgentId) {
758
913
  else
759
914
  byAgent.set(agentId, [entry]);
760
915
  }
916
+ const profilesByAgent = getProfilesByAgent(filterAgentId);
761
917
  const out = [];
762
918
  for (const agentId of agentsToShow) {
763
919
  const versions = byAgent.get(agentId) ?? [];
@@ -766,7 +922,7 @@ async function collectAgentsJson(filterAgentId) {
766
922
  return a.isDefault ? -1 : 1;
767
923
  return compareVersions(b.version, a.version);
768
924
  });
769
- out.push({ agent: agentId, versions });
925
+ out.push({ agent: agentId, versions, profiles: profilesByAgent.get(agentId) ?? [] });
770
926
  }
771
927
  return out;
772
928
  }
@@ -833,13 +989,12 @@ async function buildAgentPrunePlan(agentId) {
833
989
  return { agentId, toPrune, skippedDefaults };
834
990
  }
835
991
  async function executePrunePlan(plan) {
836
- let removed = 0;
992
+ const moved = [];
837
993
  for (const p of plan.toPrune) {
838
- console.log(chalk.gray(`Removing ${agentLabel(p.agentId)}@${p.version}...`));
839
994
  const ok = removeVersion(p.agentId, p.version);
840
995
  if (ok) {
841
- console.log(chalk.green(`Removed ${agentLabel(p.agentId)}@${p.version}`));
842
- removed++;
996
+ console.log(chalk.green(`Moved ${agentLabel(p.agentId)}@${p.version} to trash`));
997
+ moved.push({ agent: p.agentId, version: p.version });
843
998
  }
844
999
  else {
845
1000
  console.log(chalk.yellow(`Already gone: ${agentLabel(p.agentId)}@${p.version}`));
@@ -848,7 +1003,7 @@ async function executePrunePlan(plan) {
848
1003
  if (listInstalledVersions(plan.agentId).length === 0) {
849
1004
  removeShim(plan.agentId);
850
1005
  }
851
- return removed;
1006
+ return moved;
852
1007
  }
853
1008
  function printPrunePlan(plan, isFirst) {
854
1009
  if (plan.skippedDefaults.length > 0) {
@@ -897,7 +1052,7 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
897
1052
  return;
898
1053
  }
899
1054
  const totalCandidates = actionable.reduce((n, plan) => n + plan.toPrune.length, 0);
900
- let totalRemoved = 0;
1055
+ const allMoved = [];
901
1056
  let isFirst = true;
902
1057
  let processedAny = false;
903
1058
  for (const plan of actionable) {
@@ -916,10 +1071,10 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
916
1071
  if (!isInteractiveTerminal()) {
917
1072
  console.log(chalk.red('Refusing to prune in a non-interactive shell without --yes.'));
918
1073
  if (filterAgentId) {
919
- console.log(chalk.gray(`Re-run with: agents prune ${filterAgentId} --dry-run`));
1074
+ console.log(chalk.gray(`Re-run with: agents prune cleanup ${filterAgentId} --dry-run`));
920
1075
  }
921
1076
  else {
922
- console.log(chalk.gray('Re-run with: agents prune --dry-run'));
1077
+ console.log(chalk.gray('Re-run with: agents prune cleanup --dry-run'));
923
1078
  }
924
1079
  process.exit(1);
925
1080
  }
@@ -943,7 +1098,7 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
943
1098
  break;
944
1099
  }
945
1100
  }
946
- totalRemoved += await executePrunePlan(plan);
1101
+ allMoved.push(...(await executePrunePlan(plan)));
947
1102
  processedAny = true;
948
1103
  isFirst = false;
949
1104
  console.log();
@@ -953,7 +1108,8 @@ export async function pruneDuplicates(filterAgentId, yes, dryRun) {
953
1108
  return;
954
1109
  }
955
1110
  if (processedAny) {
956
- console.log(chalk.bold(`Pruned ${totalRemoved} version${totalRemoved === 1 ? '' : 's'}.`));
1111
+ console.log(chalk.bold(`Pruned ${allMoved.length} version${allMoved.length === 1 ? '' : 's'}.`));
1112
+ printTrashFooter(allMoved);
957
1113
  }
958
1114
  }
959
1115
  /**
@@ -1009,7 +1165,7 @@ export async function viewAction(agentArg, options) {
1009
1165
  // --json ignores the @version suffix (detailed resource view is not yet
1010
1166
  // exposed as structured data). Emit the version list for the agent.
1011
1167
  const data = await collectAgentsJson(agentId);
1012
- console.log(JSON.stringify(data[0] ?? { agent: agentId, versions: [] }, null, 2));
1168
+ console.log(JSON.stringify(data[0] ?? { agent: agentId, versions: [], profiles: [] }, null, 2));
1013
1169
  return;
1014
1170
  }
1015
1171
  if (requestedVersion) {
@@ -7,9 +7,9 @@ import { select, checkbox } from '@inquirer/prompts';
7
7
  import { resolveAgentName, agentLabel } from '../lib/agents.js';
8
8
  import { cloneRepo } from '../lib/git.js';
9
9
  import { WORKFLOW_CAPABLE_AGENTS, discoverWorkflowsFromRepo, installWorkflowCentrally, removeWorkflow, listInstalledWorkflows, listWorkflowsForAgent, removeWorkflowFromVersion, iterWorkflowsCapableVersions, } from '../lib/workflows.js';
10
- import { getVersionHomePath, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, resolveAgentVersionTargets, } from '../lib/versions.js';
10
+ import { getVersionHomePath, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, } from '../lib/versions.js';
11
11
  import { recordVersionResources, getUserWorkflowsDir } from '../lib/state.js';
12
- import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection, printWithPager, promptRemovalTargets, } from './utils.js';
12
+ import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection, printWithPager, promptRemovalTargets, parseCommaSeparatedList, resolveAgentTargetsAutoInstalling, } from './utils.js';
13
13
  import { showResourceList, buildTargetsSection, } from './resource-view.js';
14
14
  /** Register the `agents workflows` command tree (list, view, add, remove). */
15
15
  export function registerWorkflowsCommands(program) {
@@ -92,18 +92,25 @@ Examples:
92
92
  workflowsCmd
93
93
  .command('add [source]')
94
94
  .description('Install workflows from a source (GitHub, local) or pick from central storage')
95
- .option('-a, --agents <list>', 'Targets: claude, claude@2.1.138')
95
+ .option('-a, --agents <list>', 'Targets: claude, claude@2.1.138, claude@all, all')
96
+ .option('--names <list>', 'Workflow names from the source (comma-separated)')
96
97
  .option('-y, --yes', 'Skip confirmation prompts')
97
98
  .addHelpText('after', `
98
99
  Examples:
99
100
  # Install from GitHub
100
101
  agents workflows add gh:user/workflows
101
102
 
103
+ # Pluck specific workflows from a multi-workflow repo
104
+ agents workflows add gh:user/workflows --names rdev,plan
105
+
102
106
  # Install a local workflow directory (must contain WORKFLOW.md)
103
107
  agents workflows add ./rdev
104
108
 
105
109
  # Install and sync to a specific version
106
110
  agents workflows add gh:user/workflows --agents claude@2.1.138
111
+
112
+ # Install across every installed Claude version
113
+ agents workflows add gh:user/workflows --agents claude@all
107
114
  `)
108
115
  .action(async (source, options) => {
109
116
  try {
@@ -156,11 +163,23 @@ Examples:
156
163
  }
157
164
  spinner.succeed('Using local path');
158
165
  }
159
- const discovered = discoverWorkflowsFromRepo(localPath);
166
+ let discovered = discoverWorkflowsFromRepo(localPath);
160
167
  if (discovered.length === 0) {
161
168
  console.log(chalk.yellow('No workflows found (looking for WORKFLOW.md files)'));
162
169
  return;
163
170
  }
171
+ // --names filter: pluck specific workflows from a multi-workflow source.
172
+ const requestedNames = parseCommaSeparatedList(options.names);
173
+ if (requestedNames.length > 0) {
174
+ const discoveredNames = new Set(discovered.map((w) => w.name));
175
+ const missing = requestedNames.filter((n) => !discoveredNames.has(n));
176
+ if (missing.length > 0) {
177
+ console.log(chalk.red(`\nWorkflow(s) not found in source: ${missing.join(', ')}`));
178
+ console.log(chalk.gray(`Available: ${[...discoveredNames].join(', ')}`));
179
+ process.exit(1);
180
+ }
181
+ discovered = discovered.filter((w) => requestedNames.includes(w.name));
182
+ }
164
183
  console.log(chalk.bold(`\nFound ${discovered.length} workflow(s):`));
165
184
  for (const w of discovered) {
166
185
  console.log(`\n ${chalk.cyan(w.name)}: ${w.frontmatter.description || 'no description'}`);
@@ -191,13 +210,17 @@ Examples:
191
210
  let selectedAgents;
192
211
  let versionSelections;
193
212
  if (options.agents) {
194
- const result = resolveAgentVersionTargets(options.agents, WORKFLOW_CAPABLE_AGENTS);
213
+ const result = await resolveAgentTargetsAutoInstalling(options.agents, WORKFLOW_CAPABLE_AGENTS, { yes: options.yes });
214
+ if (!result) {
215
+ console.log(chalk.gray('Cancelled.'));
216
+ return;
217
+ }
195
218
  selectedAgents = result.selectedAgents;
196
219
  versionSelections = result.versionSelections;
197
220
  }
198
221
  else {
199
222
  const result = await promptAgentVersionSelection(WORKFLOW_CAPABLE_AGENTS, {
200
- skipPrompts: options.yes || !isInteractiveTerminal(),
223
+ skipPrompts: options.yes,
201
224
  });
202
225
  selectedAgents = result.selectedAgents;
203
226
  versionSelections = result.versionSelections;
package/dist/computer.js CHANGED
File without changes
package/dist/index.js CHANGED
@@ -59,6 +59,7 @@ import { registerPullCommand } from './commands/pull.js';
59
59
  import { registerRepoCommands } from './commands/repo.js';
60
60
  import { registerSetupCommand, runSetup } from './commands/setup.js';
61
61
  import { registerStatusCommand } from './commands/status.js';
62
+ import { registerFeedbackCommand } from './commands/feedback.js';
62
63
  import { registerViewCommand } from './commands/view.js';
63
64
  import { registerCommandsCommands } from './commands/commands.js';
64
65
  import { registerHooksCommands } from './commands/hooks.js';
@@ -66,6 +67,7 @@ import { registerSkillsCommands } from './commands/skills.js';
66
67
  import { registerRulesCommands } from './commands/rules.js';
67
68
  import { registerPermissionsCommands } from './commands/permissions.js';
68
69
  import { registerMcpCommands } from './commands/mcp.js';
70
+ import { registerCliCommands } from './commands/cli.js';
69
71
  import { registerVersionsCommands } from './commands/versions.js';
70
72
  import { registerImportCommand } from './commands/import.js';
71
73
  import { registerPackagesCommands } from './commands/packages.js';
@@ -88,6 +90,7 @@ import { registerBrowserCommand } from './commands/browser.js';
88
90
  import { registerComputerCommand } from './commands/computer.js';
89
91
  import { registerProfilesCommands } from './commands/profiles.js';
90
92
  import { registerSecretsCommands } from './commands/secrets.js';
93
+ import { registerHelperCommand } from './commands/helper.js';
91
94
  import { registerFactoryCommands } from './commands/factory.js';
92
95
  import { registerUsageCommand } from './commands/usage.js';
93
96
  import { registerAliasCommand } from './commands/alias.js';
@@ -120,11 +123,12 @@ Quick start:
120
123
  agents sessions Browse past sessions across all agents
121
124
 
122
125
  Agent versions:
123
- add <agent>[@version] Install an agent CLI (e.g. agents add codex)
126
+ add <agent>[@version] Install an agent CLI (e.g. agents add grok or agents add codex)
124
127
  import <agent> Adopt an existing global install (npm/homebrew) into agents-cli
125
- remove <agent>[@version] Uninstall a version
128
+ prune <agent>[@version] Uninstall a version
129
+ remove <agent>[@version] Alias for prune
126
130
  use <agent>@<version> Set the default version
127
- prune [target] Remove orphan resources and older duplicate version installs (targets: commands, sessions, runs, trash)
131
+ prune cleanup [target] Remove orphan resources and older duplicate version installs
128
132
  trash Inspect and restore soft-deleted version directories
129
133
  view [agent[@version]] List versions, or inspect one in detail
130
134
 
@@ -171,7 +175,7 @@ Automation tips:
171
175
  Pass explicit names/IDs Avoid pickers: agents sessions <id> --markdown
172
176
  Use --yes for defaults Auto-accept sync/default prompts on add/use/pull
173
177
  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
178
+ Use agent@version targets e.g. --agents grok@0.1.218,claude@2.1.79,codex@default
175
179
  Non-TTY shells apply defaults Omitted required selections fail with a plain hint
176
180
 
177
181
  Options:
@@ -361,7 +365,13 @@ async function promptUpgrade(latestVersion) {
361
365
  console.log();
362
366
  }
363
367
  }
364
- /** Fire-and-forget: refresh the registry cache in background. Never blocks the command. */
368
+ /**
369
+ * Background update check — fires once per 24h cache window.
370
+ * Network: GET registry.npmjs.org/@phnx-labs/agents-cli/latest.
371
+ * Disable: set AGENTS_CLI_DISABLE_AUTO_UPDATE=1 in shell rc.
372
+ *
373
+ * Fire-and-forget; never blocks the CLI's foreground operation.
374
+ */
365
375
  function refreshUpdateCacheInBackground() {
366
376
  fetch('https://registry.npmjs.org/@phnx-labs/agents-cli/latest', {
367
377
  signal: AbortSignal.timeout(2000),
@@ -501,6 +511,7 @@ async function maybeBootstrapShimIntegration(requestedCommand) {
501
511
  // Register all commands
502
512
  registerViewCommand(program);
503
513
  registerStatusCommand(program);
514
+ registerFeedbackCommand(program);
504
515
  registerCommandsCommands(program);
505
516
  registerHooksCommands(program);
506
517
  registerSkillsCommands(program);
@@ -529,6 +540,7 @@ program
529
540
  await program.parseAsync(['node', 'agents', ...args]);
530
541
  });
531
542
  registerMcpCommands(program);
543
+ registerCliCommands(program);
532
544
  registerSubagentsCommands(program);
533
545
  registerPluginsCommands(program);
534
546
  registerWorkflowsCommands(program);
@@ -556,6 +568,7 @@ program
556
568
  });
557
569
  registerProfilesCommands(program);
558
570
  registerSecretsCommands(program);
571
+ registerHelperCommand(program);
559
572
  registerBetaCommands(program);
560
573
  registerSyncCommand(program);
561
574
  registerRefreshRulesCommand(program);
@@ -726,7 +739,13 @@ if (firstRun) {
726
739
  // Every command requires the system repo to be cloned first. `setup` is the
727
740
  // only exemption — it's the command that does the cloning.
728
741
  const SETUP_EXEMPT_COMMANDS = new Set(['setup', 'help']);
729
- if (!firstRun && requestedCommand && !SETUP_EXEMPT_COMMANDS.has(requestedCommand)) {
742
+ // Help and version output are pure documentation — they must never gate on
743
+ // setup, otherwise `agents <cmd> --help` becomes useless on a fresh box.
744
+ const helpOrVersionRequested = passedArgs.some((arg) => arg === '--help' || arg === '-h' || arg === '--version' || arg === '-V');
745
+ if (!firstRun &&
746
+ requestedCommand &&
747
+ !SETUP_EXEMPT_COMMANDS.has(requestedCommand) &&
748
+ !helpOrVersionRequested) {
730
749
  const { ensureInitialized } = await import('./commands/setup.js');
731
750
  await ensureInitialized(program);
732
751
  }
@@ -771,5 +790,18 @@ catch (err) {
771
790
  if (err instanceof Error && err.name === 'ExitPromptError') {
772
791
  process.exit(130);
773
792
  }
793
+ // Browser-daemon-not-running and CDP-not-reachable surface as typed errors
794
+ // from src/lib/browser/. Don't dump a Node stacktrace for these — they are
795
+ // user-actionable, not engineering bugs. See issues #41 and #43.
796
+ if (err instanceof Error) {
797
+ const isBrowserDaemonNotRunning = err.name === 'BrowserDaemonNotRunningError';
798
+ const isBrowserCdpUnreachable = err.name === 'BrowserCdpConnectionError';
799
+ const isBrowserIpcDown = err.message.startsWith('IPC error:') &&
800
+ (err.message.includes('ECONNREFUSED') || err.message.includes('ENOENT'));
801
+ if (isBrowserDaemonNotRunning || isBrowserCdpUnreachable || isBrowserIpcDown) {
802
+ console.error(err.message);
803
+ process.exit(1);
804
+ }
805
+ }
774
806
  throw err;
775
807
  }
@@ -82,7 +82,12 @@ function buildClient(opts) {
82
82
  return {};
83
83
  },
84
84
  async requestPermission(params) {
85
- const optionId = mode === 'full'
85
+ // `skip` (formerly `full`) and `auto` blanket-approve; in `auto` the
86
+ // upstream model has its own classifier so we just say "allow once" and
87
+ // let it decide. `edit` says allow_once. `plan` should never reach here
88
+ // (canWrite gates writes earlier), but if it does we cancel.
89
+ const skipAll = mode === 'skip';
90
+ const optionId = skipAll
86
91
  ? (params.options.find(o => o.kind === 'allow_always')?.optionId
87
92
  ?? params.options[0]?.optionId)
88
93
  : params.options.find(o => o.kind === 'allow_once')?.optionId;
@@ -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).
@@ -156,6 +156,10 @@ interface McpConfigEntry {
156
156
  type?: string;
157
157
  url?: string;
158
158
  }
159
+ /**
160
+ * Get user-scoped MCP config path for an agent.
161
+ */
162
+ export declare function getUserMcpConfigPath(agentId: AgentId): string;
159
163
  /**
160
164
  * Get MCP config path for a specific HOME directory (used for version-managed agents).
161
165
  */