@phnx-labs/agents-cli 1.18.6 → 1.19.1

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 (104) hide show
  1. package/CHANGELOG.md +13 -2
  2. package/README.md +22 -20
  3. package/dist/commands/browser.js +25 -2
  4. package/dist/commands/cloud.js +3 -3
  5. package/dist/commands/computer.d.ts +6 -0
  6. package/dist/commands/computer.js +477 -0
  7. package/dist/commands/doctor.js +19 -17
  8. package/dist/commands/exec.js +37 -59
  9. package/dist/commands/factory.js +12 -5
  10. package/dist/commands/import.js +6 -1
  11. package/dist/commands/mcp.js +9 -4
  12. package/dist/commands/packages.d.ts +3 -0
  13. package/dist/commands/packages.js +20 -12
  14. package/dist/commands/permissions.d.ts +2 -0
  15. package/dist/commands/permissions.js +20 -1
  16. package/dist/commands/plugins.d.ts +2 -0
  17. package/dist/commands/plugins.js +23 -4
  18. package/dist/commands/profiles.js +1 -1
  19. package/dist/commands/pty.js +126 -112
  20. package/dist/commands/pull.js +29 -25
  21. package/dist/commands/repo.js +24 -26
  22. package/dist/commands/routines.js +29 -26
  23. package/dist/commands/secrets.js +66 -73
  24. package/dist/commands/sessions-tail.js +21 -22
  25. package/dist/commands/sessions.js +36 -68
  26. package/dist/commands/setup.js +20 -24
  27. package/dist/commands/teams.js +30 -39
  28. package/dist/commands/versions.js +60 -68
  29. package/dist/commands/worktree.d.ts +20 -0
  30. package/dist/commands/worktree.js +242 -0
  31. package/dist/computer.d.ts +2 -0
  32. package/dist/computer.js +7 -0
  33. package/dist/index.js +70 -26
  34. package/dist/lib/agents.d.ts +4 -1
  35. package/dist/lib/agents.js +23 -5
  36. package/dist/lib/browser/cdp.d.ts +15 -1
  37. package/dist/lib/browser/cdp.js +77 -8
  38. package/dist/lib/browser/chrome.js +17 -24
  39. package/dist/lib/browser/drivers/ssh.d.ts +1 -0
  40. package/dist/lib/browser/drivers/ssh.js +20 -8
  41. package/dist/lib/browser/ipc.js +38 -5
  42. package/dist/lib/browser/profiles.js +34 -2
  43. package/dist/lib/browser/runtime-state.d.ts +1 -2
  44. package/dist/lib/browser/runtime-state.js +11 -3
  45. package/dist/lib/browser/service.d.ts +5 -0
  46. package/dist/lib/browser/service.js +32 -4
  47. package/dist/lib/browser/types.d.ts +1 -1
  48. package/dist/lib/browser/upload.d.ts +2 -0
  49. package/dist/lib/browser/upload.js +34 -0
  50. package/dist/lib/cloud/rush.d.ts +2 -1
  51. package/dist/lib/cloud/rush.js +28 -9
  52. package/dist/lib/computer-rpc.d.ts +24 -0
  53. package/dist/lib/computer-rpc.js +263 -0
  54. package/dist/lib/daemon.js +7 -7
  55. package/dist/lib/exec.d.ts +2 -1
  56. package/dist/lib/exec.js +3 -2
  57. package/dist/lib/fs-atomic.d.ts +18 -0
  58. package/dist/lib/fs-atomic.js +76 -0
  59. package/dist/lib/git.js +2 -4
  60. package/dist/lib/help.d.ts +15 -0
  61. package/dist/lib/help.js +41 -0
  62. package/dist/lib/hooks/match.d.ts +1 -0
  63. package/dist/lib/hooks/match.js +57 -12
  64. package/dist/lib/hooks.d.ts +1 -0
  65. package/dist/lib/hooks.js +27 -10
  66. package/dist/lib/import.d.ts +1 -0
  67. package/dist/lib/import.js +7 -0
  68. package/dist/lib/manifest.js +27 -1
  69. package/dist/lib/mcp.d.ts +14 -0
  70. package/dist/lib/mcp.js +79 -14
  71. package/dist/lib/migrate.js +3 -3
  72. package/dist/lib/models.js +3 -1
  73. package/dist/lib/permissions.d.ts +5 -0
  74. package/dist/lib/permissions.js +35 -0
  75. package/dist/lib/plugin-marketplace.d.ts +3 -1
  76. package/dist/lib/plugin-marketplace.js +36 -1
  77. package/dist/lib/plugins.d.ts +19 -1
  78. package/dist/lib/plugins.js +99 -8
  79. package/dist/lib/redact.d.ts +4 -0
  80. package/dist/lib/redact.js +18 -0
  81. package/dist/lib/registry.d.ts +2 -0
  82. package/dist/lib/registry.js +15 -0
  83. package/dist/lib/sandbox.js +15 -5
  84. package/dist/lib/secrets/bundles.d.ts +7 -12
  85. package/dist/lib/secrets/bundles.js +45 -29
  86. package/dist/lib/secrets/index.js +4 -4
  87. package/dist/lib/session/cloud.d.ts +2 -0
  88. package/dist/lib/session/cloud.js +34 -6
  89. package/dist/lib/session/parse.js +7 -2
  90. package/dist/lib/session/render.d.ts +4 -1
  91. package/dist/lib/session/render.js +81 -35
  92. package/dist/lib/shims.d.ts +5 -2
  93. package/dist/lib/shims.js +29 -7
  94. package/dist/lib/state.d.ts +5 -5
  95. package/dist/lib/state.js +43 -13
  96. package/dist/lib/teams/agents.js +1 -1
  97. package/dist/lib/types.d.ts +4 -3
  98. package/dist/lib/types.js +0 -2
  99. package/dist/lib/versions.js +65 -40
  100. package/dist/lib/workflows.d.ts +7 -0
  101. package/dist/lib/workflows.js +42 -1
  102. package/npm-shrinkwrap.json +3162 -0
  103. package/package.json +32 -26
  104. package/scripts/postinstall.js +8 -2
@@ -10,7 +10,7 @@ import * as fs from 'fs';
10
10
  import { bundleExists, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, migrateLegacyBundles, parseDotenv, readBundle, renameBundle, rotateBundleSecret, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, } from '../lib/secrets/bundles.js';
11
11
  import { deleteKeychainToken, getKeychainToken, hasKeychainToken, secretsKeychainItem, setKeychainToken, } from '../lib/secrets/index.js';
12
12
  import { assertOpAvailable, createPasswordItem, deleteItemByTitle, extractSecrets, itemExistsByTitle, listItems, listVaults, } from '../lib/onepassword.js';
13
- import { registerCommandGroups } from '../lib/help.js';
13
+ import { registerCommandGroups, setHelpSections } from '../lib/help.js';
14
14
  import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
15
15
  /** Prompt the user for a secret value with masked input. Requires an interactive TTY. */
16
16
  async function promptForSecret(message) {
@@ -314,86 +314,45 @@ function countExpiringSoon(meta) {
314
314
  export function registerSecretsCommands(program) {
315
315
  const cmd = program
316
316
  .command('secrets')
317
- .description('Named bundles of env variables backed by macOS Keychain (iCloud-synced by default). Inject into agents via `agents run --secrets <name>`.')
318
- .hook('preAction', () => { migrateLegacyBundles(); })
319
- .addHelpText('after', `
320
- Workflow:
321
- Bundles are containers; secrets are the variables inside them. Create a
322
- bundle once, add secrets to it, then inject the whole bundle into any agent
323
- run with --secrets <name>. Keychain-backed values never touch disk in
324
- plaintext.
325
-
326
- New bundles store values in the iCloud-synced keychain by default so they
327
- appear automatically on your other Macs (same iCloud account, iCloud
328
- Keychain enabled). Pass --no-icloud-sync at create time to keep values
329
- device-local instead.
330
-
331
- Examples:
332
- # Create a bundle for production credentials (iCloud-synced by default)
333
- agents secrets create prod --description "Production keys for the api stack"
334
-
335
- # Create a bundle that never leaves this Mac
336
- agents secrets create local-only --no-icloud-sync
337
-
338
- # Rename a bundle (moves metadata + every keychain value)
339
- agents secrets rename prod production
340
-
341
- # Edit the description of an existing bundle
342
- agents secrets describe prod "Production keys for the api stack"
343
- agents secrets describe prod --clear
344
-
345
- # Add a keychain-backed secret (prompts for the value)
346
- agents secrets add prod STRIPE_API_KEY
347
-
348
- # Add a literal (non-sensitive) value
349
- agents secrets add prod LOG_LEVEL --value info
317
+ .description('Named bundles of env variables backed by macOS Keychain (iCloud-synced by default). Inject into agents via `agents run --secrets <name>`.');
318
+ setHelpSections(cmd, {
319
+ examples: `
320
+ # Create a bundle
321
+ agents secrets create prod --description "Production keys for the api stack"
350
322
 
351
- # Add with metadata
352
- agents secrets add prod STRIPE_API_KEY --type api-key --expires 2027-01-15 --note "Live key, owner: payments-team"
323
+ # Add a keychain-backed secret (prompts for the value)
324
+ agents secrets add prod STRIPE_API_KEY
353
325
 
354
- # Rotate a key (replaces the value, preserves metadata unless overridden)
355
- agents secrets rotate prod STRIPE_API_KEY
356
-
357
- # Import an entire .env file straight into keychain
358
- agents secrets import prod --from .env.prod
359
-
360
- # Import secrets from a 1Password vault
361
- agents secrets import prod --from-1password --vault "Rush Prod"
362
-
363
- # Push a bundle back to 1Password (vault migration, backup)
364
- agents secrets export prod --to-1password --vault "Rush Prod"
365
- agents secrets export prod --to-1password --vault "Rush Prod" --force
366
-
367
- # See what's in a bundle (values masked)
368
- agents secrets view prod
326
+ # Add a non-sensitive literal
327
+ agents secrets add prod LOG_LEVEL --value info
369
328
 
370
- # Reveal the real values in an interactive shell
371
- agents secrets view prod --reveal
329
+ # Inject the bundle into an agent run
330
+ agents run claude "deploy the worker" --secrets prod
372
331
 
373
- # Inject the bundle into an agent run
374
- agents run claude "deploy the worker" --secrets prod
332
+ # See what's in the bundle (values masked)
333
+ agents secrets view prod
375
334
 
376
- # Eval the bundle into your current shell
377
- eval "$(agents secrets export prod --plaintext)"
335
+ # Eval the bundle into your current shell
336
+ eval "$(agents secrets export prod --plaintext)"
378
337
 
379
- # Run a command with secrets injected
380
- agents secrets exec prod -- ./deploy.sh
381
- agents secrets exec hetzner.com -- crabbox list
338
+ # Run a one-off command with secrets injected
339
+ agents secrets exec prod -- ./deploy.sh
340
+ `,
341
+ notes: `
342
+ Bundles are containers; secrets are the variables inside them. Keychain values
343
+ never touch disk in plaintext.
382
344
 
383
- # Remove one key (purges the keychain item by default)
384
- agents secrets remove prod STRIPE_API_KEY
345
+ iCloud sync: new bundles use the iCloud-synced keychain by default so they
346
+ appear on other Macs (same iCloud account, iCloud Keychain enabled). Pass
347
+ --no-icloud-sync at create time to keep values device-local instead.
385
348
 
386
- # Delete the whole bundle and purge every keychain item it owned
387
- agents secrets delete prod
388
-
389
- # Generate a random password (32 chars, all character classes)
390
- agents secrets generate
391
-
392
- # Generate a 16-char password, or a PIN, or hex
393
- agents secrets generate 16
394
- agents secrets generate 8 --pin
395
- agents secrets generate 32 --hex
396
- `);
349
+ See also:
350
+ agents secrets rotate <bundle> <key> rotate value, preserve metadata
351
+ agents secrets import <bundle> --from .env bulk import from .env
352
+ agents secrets import <bundle> --from-1password --vault <name>
353
+ agents secrets generate [length] generate a random password / PIN / hex
354
+ `,
355
+ });
397
356
  registerCommandGroups(cmd, [
398
357
  { title: 'Bundle commands', names: ['list', 'view', 'create', 'rename', 'describe', 'delete'] },
399
358
  { title: 'Secret commands', names: ['add', 'rotate', 'remove', 'import', 'export'] },
@@ -796,6 +755,40 @@ Examples:
796
755
  process.exit(1);
797
756
  }
798
757
  });
758
+ cmd
759
+ .command('migrate')
760
+ .description('Interactively migrate legacy YAML bundles into Keychain')
761
+ .action(async () => {
762
+ try {
763
+ if (!isInteractiveTerminal()) {
764
+ console.error(chalk.red('Refusing to migrate legacy secrets without an interactive confirmation prompt.'));
765
+ process.exit(1);
766
+ }
767
+ const { confirm } = await import('@inquirer/prompts');
768
+ const migrated = await migrateLegacyBundles(async (candidate) => {
769
+ console.log(chalk.bold(`Legacy bundle '${candidate.name}'`));
770
+ console.log(chalk.gray(candidate.file));
771
+ for (const key of candidate.keys) {
772
+ console.log(` ${key}`);
773
+ }
774
+ return await confirm({
775
+ message: `Migrate legacy bundle '${candidate.name}' into Keychain?`,
776
+ default: false,
777
+ });
778
+ });
779
+ if (migrated === 0) {
780
+ console.log(chalk.gray('No legacy bundles migrated.'));
781
+ return;
782
+ }
783
+ console.log(chalk.green(`Migrated ${migrated} legacy bundle${migrated === 1 ? '' : 's'} into keychain.`));
784
+ }
785
+ catch (err) {
786
+ if (isPromptCancelled(err))
787
+ return;
788
+ console.error(chalk.red(err.message));
789
+ process.exit(1);
790
+ }
791
+ });
799
792
  cmd
800
793
  .command('import [bundle]')
801
794
  .description('Import keys from a .env file or a 1Password vault into a bundle. By default every key is stored in keychain.')
@@ -10,6 +10,7 @@ import * as fsp from 'fs/promises';
10
10
  import * as path from 'path';
11
11
  import chalk from 'chalk';
12
12
  import { discoverSessions, resolveSessionById } from '../lib/session/discover.js';
13
+ import { setHelpSections } from '../lib/help.js';
13
14
  const TAIL_SUPPORTED = ['claude', 'codex'];
14
15
  /**
15
16
  * Tail a file: emit each newline-terminated line via onLine as it's written.
@@ -202,34 +203,32 @@ async function runTail(sessionId, options) {
202
203
  }
203
204
  /** Attach the `tail` subcommand to an existing `sessions` command. */
204
205
  export function registerSessionsTailCommand(sessionsCmd) {
205
- sessionsCmd
206
+ const tailCmd = sessionsCmd
206
207
  .command('tail [sessionId]')
207
- .description('Live-tail a session file, streaming new JSONL events as they are written. Long-running: press Ctrl+C to stop. Claude and Codex only.')
208
+ .description('Stream JSONL events from a session file as they are written, one event per line. Long-running: Ctrl+C to stop. Claude and Codex only.')
208
209
  .option('--latest', 'Tail the most recent tailable session (claude or codex)')
209
210
  .option('--from-start', 'Emit the full file first, then follow (default: start at EOF)')
210
- .option('--json', 'Raw JSONL passthrough (default)')
211
- .addHelpText('after', `
212
- This command runs until interrupted (Ctrl+C). Each line printed to stdout
213
- is a raw JSONL event from the session file parse with jq or similar.
211
+ .option('--json', 'Raw JSONL passthrough (default)');
212
+ setHelpSections(tailCmd, {
213
+ examples: `
214
+ # Follow the most recent active Claude or Codex session
215
+ agents sessions tail --latest
214
216
 
215
- Examples:
216
- # Follow the most recent active Claude or Codex session
217
- agents sessions tail --latest
217
+ # Follow a specific session by short or full ID
218
+ agents sessions tail a1b2c3d4
218
219
 
219
- # Follow a specific session by short or full ID
220
- agents sessions tail a1b2c3d4
220
+ # Replay from the beginning, then follow
221
+ agents sessions tail a1b2c3d4 --from-start
221
222
 
222
- # Replay from the beginning, then follow
223
- agents sessions tail a1b2c3d4 --from-start
224
-
225
- # Pipe through jq to extract just user messages
226
- agents sessions tail --latest | jq 'select(.type == "user")'
227
-
228
- Only Claude and Codex sessions are supported — they append JSONL one event
229
- per line, which makes live-tailing safe. Gemini, OpenCode, and OpenClaw
230
- use formats that rewrite the file or store state elsewhere.
231
- `)
232
- .action(async (sessionId, options) => {
223
+ # Pipe through jq to extract just user messages
224
+ agents sessions tail --latest | jq 'select(.type == "user")'
225
+ `,
226
+ notes: `
227
+ - Only Claude and Codex sessions are tailable they append JSONL one event per line.
228
+ - Gemini, OpenCode, and OpenClaw use formats that rewrite the file or store state elsewhere.
229
+ `,
230
+ });
231
+ tailCmd.action(async (sessionId, options) => {
233
232
  await runTail(sessionId, options);
234
233
  });
235
234
  }
@@ -25,6 +25,7 @@ import { colorAgent, resolveAgentName } from '../lib/agents.js';
25
25
  import { resolveVersion, resolveVersionAliasLoose } from '../lib/versions.js';
26
26
  import { isInteractiveTerminal, isPromptCancelled } from './utils.js';
27
27
  import { sessionPicker } from './sessions-picker.js';
28
+ import { setHelpSections } from '../lib/help.js';
28
29
  import { registerSessionsTailCommand } from './sessions-tail.js';
29
30
  const SESSION_AGENT_FILTER_HELP = `Filter by agent, e.g. claude, codex, claude@2.0.65`;
30
31
  const CLAUDE_RESUME_MATCH_WINDOW_MS = 10 * 60_000;
@@ -297,7 +298,7 @@ async function sessionsAction(query, options) {
297
298
  // When the user explicitly asks to render (via mode flag), resolve the
298
299
  // query globally so sessions outside the default cwd/30d window are found.
299
300
  if (wantsRender && searchQuery) {
300
- await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts });
301
+ await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts, noRedact: options.noRedact });
301
302
  return;
302
303
  }
303
304
  // Interactive picker loads a deep pool but shows only recent sessions
@@ -347,11 +348,11 @@ async function sessionsAction(query, options) {
347
348
  if (searchQuery) {
348
349
  const idMatches = resolveSessionById(sessions, searchQuery);
349
350
  if (idMatches.length === 1) {
350
- await renderSession(idMatches[0], mode, filterOpts);
351
+ await renderSession(idMatches[0], mode, filterOpts, options);
351
352
  return;
352
353
  }
353
354
  if (idMatches.length === 0 && looksLikeSessionId(searchQuery)) {
354
- await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts });
355
+ await renderOneSession(searchQuery, mode, { agent: options.agent, project: options.project, filter: filterOpts, noRedact: options.noRedact });
355
356
  return;
356
357
  }
357
358
  }
@@ -472,7 +473,7 @@ function resolveViewMode(options, filters) {
472
473
  return 'markdown';
473
474
  return 'summary';
474
475
  }
475
- async function renderSession(session, mode, filters) {
476
+ async function renderSession(session, mode, filters, options = {}) {
476
477
  // OpenCode stores sessions in SQLite; filePath is "db_path#session_id"
477
478
  const realPath = session.filePath.split('#')[0];
478
479
  if (!fs.existsSync(realPath)) {
@@ -518,7 +519,7 @@ async function renderSession(session, mode, filters) {
518
519
  chalk.gray(` ${formatRelativeTime(session.timestamp)}`) +
519
520
  (session.account ? chalk.gray(` (${session.account})`) : ''));
520
521
  console.log(chalk.gray('─'.repeat(60)));
521
- process.stdout.write(renderMarkdown(renderConversationMarkdown(events)));
522
+ process.stdout.write(renderMarkdown(renderConversationMarkdown(events, { redact: options.noRedact !== true })));
522
523
  return;
523
524
  }
524
525
  // json — no header, raw events only (pipeable)
@@ -731,7 +732,7 @@ async function runCloudSessions(query, options) {
731
732
  const cachedSpinner = options.json ? null : ora('Fetching session...').start();
732
733
  let cachedPath;
733
734
  try {
734
- cachedPath = await ensureCloudSessionCached(meta.id, meta.filePath);
735
+ cachedPath = await ensureCloudSessionCached(meta.id);
735
736
  }
736
737
  catch (err) {
737
738
  cachedSpinner?.stop();
@@ -740,7 +741,7 @@ async function runCloudSessions(query, options) {
740
741
  }
741
742
  cachedSpinner?.stop();
742
743
  // Ensure the SessionMeta points at the local cache path for renderSession.
743
- await renderSession({ ...meta, filePath: cachedPath }, mode, filterOpts);
744
+ await renderSession({ ...meta, filePath: cachedPath }, mode, filterOpts, options);
744
745
  }
745
746
  function parseAgentFilter(agentName) {
746
747
  if (!agentName)
@@ -986,7 +987,7 @@ async function renderOneSession(query, mode, scope) {
986
987
  throw new Error('Session resolution failed');
987
988
  }
988
989
  spinner.stop();
989
- await renderSession(session, mode, scope.filter);
990
+ await renderSession(session, mode, scope.filter, { noRedact: scope.noRedact });
990
991
  }
991
992
  catch (err) {
992
993
  if (isPromptCancelled(err))
@@ -1011,6 +1012,7 @@ export function registerSessionsCommands(program) {
1011
1012
  .option('--until <time>', 'Only sessions older than this (ISO timestamp)')
1012
1013
  .option('-n, --limit <n>', 'Maximum number of sessions to return', '50')
1013
1014
  .option('--markdown', 'Render the session as markdown (user, assistant, thinking, tool calls)')
1015
+ .option('--no-redact', 'Disable default secret redaction in markdown session output')
1014
1016
  .option('--json', 'Output JSON (session list when browsing, event array when rendering one session)')
1015
1017
  .option('--include <roles>', 'Only include these roles (comma-separated): user, assistant, thinking, tools')
1016
1018
  .option('--exclude <roles>', 'Exclude these roles (comma-separated): user, assistant, thinking, tools')
@@ -1019,70 +1021,36 @@ export function registerSessionsCommands(program) {
1019
1021
  .option('--artifacts', 'List all files written or edited during a session')
1020
1022
  .option('--artifact <name>', 'Read a specific artifact by filename or path (outputs to stdout)')
1021
1023
  .option('--active', 'Show only sessions running right now across terminals, teams, cloud, and headless agents')
1022
- .option('--cloud', 'Source sessions from Rush Cloud (captured runs) instead of local disk')
1023
- .addHelpText('after', `
1024
- Examples:
1025
- # Interactive picker: browse and search recent sessions (TTY only)
1026
- agents sessions
1024
+ .option('--cloud', 'Source sessions from Rush Cloud (captured runs) instead of local disk');
1025
+ setHelpSections(sessionsCmd, {
1026
+ examples: `
1027
+ # Search prior sessions in this project by topic, file path, or command
1028
+ agents sessions "add auth middleware"
1027
1029
 
1028
- # List sessions from current project (last 30 days, piped output shows table)
1029
- agents sessions | head -20
1030
+ # Read a session as markdown (user + assistant + thinking + tools)
1031
+ agents sessions a1b2c3d4 --markdown
1030
1032
 
1031
- # Search sessions by text (topic, file paths, commands)
1032
- agents sessions "add auth middleware"
1033
+ # Just the user turns useful for recalling intent
1034
+ agents sessions a1b2c3d4 --include user
1033
1035
 
1034
- # Filter by project across all directories
1035
- agents sessions --project agents-cli --all
1036
+ # Show only what's running right now (terminals, teams, cloud, headless)
1037
+ agents sessions --active
1036
1038
 
1037
- # Filter by agent and time window
1038
- agents sessions --agent claude --since 7d
1039
+ # Search across every directory, not just this project
1040
+ agents sessions "topic" --all
1039
1041
 
1040
- # Filter sessions in a specific directory
1041
- agents sessions ~/src/my-project
1042
-
1043
- # Default summary view for one session
1044
- agents sessions a1b2c3d4
1045
-
1046
- # Full conversation (user + assistant + thinking + tools) as markdown
1047
- agents sessions a1b2c3d4 --markdown
1048
-
1049
- # Same conversation as structured JSON events
1050
- agents sessions a1b2c3d4 --json
1051
-
1052
- # Only user messages (filter flags auto-select markdown)
1053
- agents sessions a1b2c3d4 --include user
1054
-
1055
- # Everything except thinking, as markdown
1056
- agents sessions a1b2c3d4 --exclude thinking --markdown
1057
-
1058
- # Last 3 turns as markdown
1059
- agents sessions a1b2c3d4 --last 3
1060
-
1061
- # First 10 turns, user messages only, as JSON
1062
- agents sessions a1b2c3d4 --first 10 --include user --json
1063
-
1064
- # Export all recent sessions as JSON for analysis
1065
- agents sessions --since 30d --limit 200 --json > sessions.json
1066
-
1067
- # Include team-spawned sessions in results
1068
- agents sessions --teams
1069
-
1070
- # Show only live sessions across terminals, teams, cloud, and headless agents
1071
- agents sessions --active
1072
- agents sessions --active --json
1073
-
1074
- # List captured cloud-run sessions (claude/codex/rush) via Rush Cloud
1075
- agents sessions --cloud
1076
- agents sessions --cloud <execution_id>
1077
- agents sessions --cloud <execution_id> --markdown
1078
- agents sessions --cloud <execution_id> --include user,assistant --last 3
1079
-
1080
- Notes:
1081
- - --include and --exclude are mutually exclusive.
1082
- - --first and --last are mutually exclusive.
1083
- - A filter flag without --markdown/--json defaults to --markdown output.
1084
- `)
1085
- .action(async (query, options) => {
1042
+ # Export for analysis
1043
+ agents sessions --since 30d --limit 200 --json > sessions.json
1044
+ `,
1045
+ notes: `
1046
+ - --include and --exclude are mutually exclusive.
1047
+ - --first and --last are mutually exclusive.
1048
+ - A filter flag (--include/--exclude/--first/--last) without --markdown/--json defaults to --markdown output.
1049
+ - --cloud sources from Rush Cloud captured runs instead of local disk.
1050
+ - Without --teams, team-spawned sessions are hidden by default.
1051
+ `,
1052
+ });
1053
+ sessionsCmd.action(async (query, options) => {
1086
1054
  await sessionsAction(query, options);
1087
1055
  });
1088
1056
  registerSessionsTailCommand(sessionsCmd);
@@ -1272,7 +1240,7 @@ function formatAbsoluteTime(isoTimestamp) {
1272
1240
  return `${months[d.getMonth()]} ${d.getDate()} ${hh}:${mm}`;
1273
1241
  }
1274
1242
  function padRight(s, width) {
1275
- return s.length >= width ? s + ' ' : s + ' '.repeat(width - s.length);
1243
+ return s.length >= width ? s : s + ' '.repeat(width - s.length);
1276
1244
  }
1277
1245
  function truncate(s, max) {
1278
1246
  return s.length > max ? s.slice(0, max - 1) + '.' : s;
@@ -17,6 +17,7 @@ import { isPromptCancelled, isInteractiveTerminal } from './utils.js';
17
17
  import { AGENTS, getUnmanagedAgentInstalls, countSessionFiles, agentLabel } from '../lib/agents.js';
18
18
  import { setGlobalDefault } from '../lib/versions.js';
19
19
  import { ensureShimCurrent, switchHomeFileSymlinks, isShimsInPath, addShimsToPath, getPathSetupInstructions } from '../lib/shims.js';
20
+ import { setHelpSections } from '../lib/help.js';
20
21
  const HOME = os.homedir();
21
22
  /**
22
23
  * Import an existing unmanaged agent installation into agents-cli.
@@ -191,33 +192,28 @@ export async function ensureInitialized(program) {
191
192
  }
192
193
  /** Register the `agents setup` command. */
193
194
  export function registerSetupCommand(program) {
194
- program
195
+ const setupCmd = program
195
196
  .command('setup')
196
- .description('Set up agents-cli for the first time. Clones a config repo and installs agent CLIs.')
197
- .option('-f, --force', 'Re-run setup even if ~/.agents-system/ already exists (use with caution)')
198
- .addHelpText('after', `
199
- Examples:
200
- # First-time setup (clones the system repo into ~/.agents-system/)
201
- agents setup
197
+ .description('First-time setup. Clones a config repo and installs agent CLIs.')
198
+ .option('-f, --force', 'Re-run setup even if ~/.agents-system/ already exists (use with caution)');
199
+ setHelpSections(setupCmd, {
200
+ examples: `
201
+ # First-time setup (clones the system repo into ~/.agents-system/)
202
+ agents setup
202
203
 
203
- # Re-run setup after corruption
204
- agents setup --force
204
+ # Re-run after corruption or to repair ~/.agents-system/
205
+ agents setup --force
206
+ `,
207
+ notes: `
208
+ What it does:
209
+ 1. Clones the system repo into ~/.agents-system/
210
+ 2. Installs agent CLIs based on agents.yaml in that repo
211
+ 3. Syncs commands, skills, hooks, and MCP servers to each version
205
212
 
206
- When to use:
207
- - First time running agents-cli: this is your starting point
208
- - Onboarding a new machine: restore the system repo and installed CLIs
209
- - Repairing ~/.agents-system/ after accidental deletion or corruption
210
-
211
- What it does:
212
- 1. Clones the system repo into ~/.agents-system/
213
- 2. Installs agent CLIs based on agents.yaml in that repo
214
- 3. Syncs commands, skills, hooks, and MCP servers to each version
215
-
216
- Non-interactive alternative:
217
- Skip 'setup' and run:
218
- agents pull
219
- `)
220
- .action(async (options) => {
213
+ Non-interactive alternative: agents pull
214
+ `,
215
+ });
216
+ setupCmd.action(async (options) => {
221
217
  try {
222
218
  await runSetup(program, options);
223
219
  }
@@ -6,6 +6,7 @@ import { resolveProvider } from '../lib/cloud/registry.js';
6
6
  import { runSupervisor } from '../lib/teams/supervisor.js';
7
7
  import { handleSpawn, handleStatus, handleStop, handleTasks, } from '../lib/teams/api.js';
8
8
  import { createTeam, ensureTeam, getTeam, loadTeams, removeTeam, teamExists, } from '../lib/teams/registry.js';
9
+ import { setHelpSections } from '../lib/help.js';
9
10
  import { createWorktree, isGitRepo, hasUncommittedChanges, removeWorktree, } from '../lib/teams/worktree.js';
10
11
  import { isVersionInstalled, resolveVersionAlias, resolveVersionAliasLoose } from '../lib/versions.js';
11
12
  import { discoverSessions, parseTimeFilter, resolveSessionById } from '../lib/session/discover.js';
@@ -513,53 +514,43 @@ async function pickTeamOr(mgr, command) {
513
514
  export function registerTeamsCommands(program) {
514
515
  const teams = program
515
516
  .command('teams')
516
- .description('Organize AI coding agents into teams that collaborate on a shared task')
517
- .addHelpText('after', `
518
- A team is a named group of agents working together on a shared task. Each teammate
519
- runs in the background; you use 'status' to check in on progress. Use --after to
520
- create DAG-style dependencies (one teammate waits for another to finish first).
517
+ .description('Organize AI coding agents into teams that work in parallel on a shared task.');
518
+ setHelpSections(teams, {
519
+ examples: `
520
+ # Create a team for a coordinated task
521
+ agents teams create pricing-page
521
522
 
522
- Teammate sessions appear in 'agents sessions --teams' with a [team/name · mode] tag.
523
+ # Add a teammate name them so you can refer to them later
524
+ agents teams add pricing-page claude "Rewrite /v2/pricing endpoint" --name backend
523
525
 
524
- Examples:
525
- # Spin up a team to ship the new pricing page end-to-end
526
- agents teams create pricing-page
526
+ # Parallel work — frontend stubs API while backend lands
527
+ agents teams add pricing-page codex "Build /pricing route with three-tier layout" --name frontend
527
528
 
528
- # Backend first: Claude rewrites the billing endpoint
529
- agents teams add pricing-page claude "Rewrite /v2/pricing to return tiered plans from billing.plans table" --name backend
529
+ # DAG dependency QA waits for backend AND frontend to finish
530
+ agents teams add pricing-page claude "Run Playwright suite, fix flakes" --name qa --after backend,frontend
530
531
 
531
- # Frontend can start in parallel it stubs the API while backend lands
532
- agents teams add pricing-page codex "Build the new /pricing route in apps/web with the three-tier layout" --name frontend
532
+ # Start everyone (respects --after dependencies) and watch live
533
+ agents teams start pricing-page --watch
533
534
 
534
- # QA waits for both to finish before running e2e
535
- agents teams add pricing-page claude "Run the full Playwright suite, fix any flakes, paste failing screenshots" --name qa --after backend,frontend
535
+ # Delta-poll status without rereading everything
536
+ agents teams status pricing-page --since 2026-04-24T09:00:00-07:00
536
537
 
537
- # Drain the DAG: backend + frontend launch now, qa picks up when they're done
538
- agents teams start pricing-page --watch
538
+ # Wind everyone down when shipped
539
+ agents teams disband pricing-page
540
+ `,
541
+ notes: `
542
+ A team is a named group of agents working in the background on a shared task.
543
+ Teammate sessions show in 'agents sessions --teams' tagged [team/name · mode].
539
544
 
540
- # Check in without re-reading everything (delta poll)
541
- agents teams status pricing-page --since 2026-04-24T09:00:00-07:00
545
+ Teammate syntax:
546
+ 'claude' the default Claude version on this machine
547
+ 'claude@2.1.112' a specific installed version (see 'agents view')
542
548
 
543
- # Pull the live log of one teammate
544
- agents teams logs frontend
545
-
546
- # A teammate is stuck — stop them, then remove if needed
547
- agents teams stop pricing-page frontend
548
- agents teams remove pricing-page frontend
549
-
550
- # Ship done — wind everyone down
551
- agents teams disband pricing-page
552
-
553
- Short aliases:
554
- teams c = create teams a = add teams s = status
555
- teams rm = remove teams d = disband teams ls = list
556
-
557
- Teammate syntax (same as the rest of agents-cli):
558
- 'claude' -> the default Claude version on this machine
559
- 'claude@2.1.112' -> a specific installed version (see 'agents view')
560
-
561
- Name teammates with --name alice to refer to them as 'alice' instead of a UUID.
562
- `);
549
+ Short aliases:
550
+ teams c = create teams a = add teams s = status
551
+ teams rm = remove teams d = disband teams ls = list
552
+ `,
553
+ });
563
554
  // list
564
555
  teams
565
556
  .command('list [query]')