@phnx-labs/agents-cli 1.20.78 → 1.20.82

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 (176) hide show
  1. package/CHANGELOG.md +487 -0
  2. package/README.md +25 -25
  3. package/dist/bin/agents +0 -0
  4. package/dist/commands/activity.d.ts +9 -0
  5. package/dist/commands/activity.js +153 -24
  6. package/dist/commands/apply.js +11 -4
  7. package/dist/commands/cli.js +1 -1
  8. package/dist/commands/commands.d.ts +1 -1
  9. package/dist/commands/commands.js +7 -6
  10. package/dist/commands/defaults.js +3 -11
  11. package/dist/commands/doctor.d.ts +65 -0
  12. package/dist/commands/doctor.js +575 -31
  13. package/dist/commands/events.js +1 -1
  14. package/dist/commands/exec.js +24 -5
  15. package/dist/commands/feed.d.ts +4 -0
  16. package/dist/commands/feed.js +87 -11
  17. package/dist/commands/harness.js +2 -2
  18. package/dist/commands/hooks.js +1 -1
  19. package/dist/commands/import.js +8 -4
  20. package/dist/commands/logs.js +6 -0
  21. package/dist/commands/models.js +1 -1
  22. package/dist/commands/packages.js +6 -6
  23. package/dist/commands/permissions.js +1 -1
  24. package/dist/commands/profiles.js +1 -1
  25. package/dist/commands/pull.d.ts +1 -1
  26. package/dist/commands/pull.js +4 -4
  27. package/dist/commands/repo.d.ts +78 -0
  28. package/dist/commands/repo.js +187 -11
  29. package/dist/commands/routines.js +12 -2
  30. package/dist/commands/rules.js +13 -12
  31. package/dist/commands/secrets.d.ts +3 -1
  32. package/dist/commands/secrets.js +100 -32
  33. package/dist/commands/sessions-inject.d.ts +7 -4
  34. package/dist/commands/sessions-inject.js +18 -18
  35. package/dist/commands/sessions-picker.js +32 -3
  36. package/dist/commands/sessions-resume.d.ts +16 -0
  37. package/dist/commands/sessions-resume.js +14 -13
  38. package/dist/commands/sessions.d.ts +31 -0
  39. package/dist/commands/sessions.js +141 -16
  40. package/dist/commands/set.d.ts +15 -0
  41. package/dist/commands/set.js +79 -0
  42. package/dist/commands/setup-fleet.d.ts +21 -0
  43. package/dist/commands/setup-fleet.js +201 -0
  44. package/dist/commands/setup-secrets.d.ts +20 -0
  45. package/dist/commands/setup-secrets.js +223 -0
  46. package/dist/commands/setup.js +17 -3
  47. package/dist/commands/skills.js +1 -1
  48. package/dist/commands/sync.js +7 -2
  49. package/dist/commands/teams-picker.js +0 -1
  50. package/dist/commands/teams.d.ts +33 -1
  51. package/dist/commands/teams.js +208 -44
  52. package/dist/commands/versions.js +10 -5
  53. package/dist/commands/view.d.ts +1 -0
  54. package/dist/commands/view.js +11 -0
  55. package/dist/index.js +5 -8
  56. package/dist/lib/acp/harnesses.js +0 -7
  57. package/dist/lib/activity.d.ts +156 -0
  58. package/dist/lib/activity.js +282 -0
  59. package/dist/lib/agents.d.ts +9 -2
  60. package/dist/lib/agents.js +72 -63
  61. package/dist/lib/browser/service.js +3 -0
  62. package/dist/lib/browser/types.d.ts +7 -0
  63. package/dist/lib/capabilities.js +6 -2
  64. package/dist/lib/crabbox/lease.js +2 -2
  65. package/dist/lib/crabbox/setup-copy.d.ts +4 -4
  66. package/dist/lib/crabbox/setup-copy.js +4 -4
  67. package/dist/lib/daemon.d.ts +18 -0
  68. package/dist/lib/daemon.js +47 -6
  69. package/dist/lib/devices/fleet.d.ts +1 -1
  70. package/dist/lib/devices/fleet.js +1 -1
  71. package/dist/lib/doctor-diff.d.ts +20 -0
  72. package/dist/lib/doctor-diff.js +6 -1
  73. package/dist/lib/drift.d.ts +31 -11
  74. package/dist/lib/drift.js +58 -7
  75. package/dist/lib/events.d.ts +8 -1
  76. package/dist/lib/events.js +10 -1
  77. package/dist/lib/exec.js +35 -8
  78. package/dist/lib/feed-post.d.ts +28 -1
  79. package/dist/lib/feed-post.js +110 -2
  80. package/dist/lib/fleet/apply.js +8 -1
  81. package/dist/lib/fleet/auth-sync.d.ts +18 -1
  82. package/dist/lib/fleet/auth-sync.js +25 -11
  83. package/dist/lib/fleet/remote-login.js +5 -0
  84. package/dist/lib/git.d.ts +14 -14
  85. package/dist/lib/git.js +41 -44
  86. package/dist/lib/hooks.d.ts +42 -0
  87. package/dist/lib/hooks.js +137 -93
  88. package/dist/lib/hosts/dispatch.d.ts +28 -1
  89. package/dist/lib/hosts/dispatch.js +33 -5
  90. package/dist/lib/hosts/option.js +2 -2
  91. package/dist/lib/hosts/passthrough.d.ts +22 -1
  92. package/dist/lib/hosts/passthrough.js +238 -13
  93. package/dist/lib/hosts/run-target.d.ts +2 -0
  94. package/dist/lib/hosts/run-target.js +1 -0
  95. package/dist/lib/hq/floor.d.ts +1 -1
  96. package/dist/lib/hq/floor.js +6 -1
  97. package/dist/lib/mcp.js +3 -71
  98. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  99. package/dist/lib/menubar/notify-desktop.d.ts +15 -0
  100. package/dist/lib/menubar/notify-desktop.js +41 -5
  101. package/dist/lib/merged-resources.d.ts +11 -0
  102. package/dist/{commands/resources.js → lib/merged-resources.js} +11 -19
  103. package/dist/lib/permissions.d.ts +0 -32
  104. package/dist/lib/permissions.js +4 -157
  105. package/dist/lib/picker.js +1 -1
  106. package/dist/lib/platform/process.d.ts +16 -5
  107. package/dist/lib/platform/process.js +54 -0
  108. package/dist/lib/plugins.d.ts +0 -8
  109. package/dist/lib/plugins.js +4 -110
  110. package/dist/lib/project-resources.js +4 -1
  111. package/dist/lib/refresh.d.ts +5 -3
  112. package/dist/lib/refresh.js +7 -5
  113. package/dist/lib/resources/commands.js +4 -1
  114. package/dist/lib/resources/mcp.js +0 -4
  115. package/dist/lib/resources/permissions.d.ts +1 -1
  116. package/dist/lib/resources/permissions.js +1 -5
  117. package/dist/lib/resources/rules.js +4 -1
  118. package/dist/lib/resources/skills.js +4 -1
  119. package/dist/lib/resources/subagents.js +4 -0
  120. package/dist/lib/resources/types.d.ts +1 -1
  121. package/dist/lib/routines.d.ts +20 -0
  122. package/dist/lib/routines.js +6 -0
  123. package/dist/lib/run-defaults.d.ts +1 -0
  124. package/dist/lib/run-defaults.js +9 -0
  125. package/dist/lib/runner.d.ts +0 -12
  126. package/dist/lib/runner.js +49 -10
  127. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  128. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  129. package/dist/lib/secrets/agent.d.ts +16 -0
  130. package/dist/lib/secrets/agent.js +105 -3
  131. package/dist/lib/secrets/audit.d.ts +46 -0
  132. package/dist/lib/secrets/audit.js +56 -0
  133. package/dist/lib/secrets/bundles.d.ts +11 -8
  134. package/dist/lib/secrets/bundles.js +31 -23
  135. package/dist/lib/secrets/index.d.ts +2 -1
  136. package/dist/lib/secrets/index.js +8 -2
  137. package/dist/lib/secrets/remote.d.ts +75 -2
  138. package/dist/lib/secrets/remote.js +139 -5
  139. package/dist/lib/secrets/sync.js +3 -3
  140. package/dist/lib/session/active.d.ts +110 -26
  141. package/dist/lib/session/active.js +161 -50
  142. package/dist/lib/session/actor-sidecar.d.ts +23 -0
  143. package/dist/lib/session/actor-sidecar.js +101 -0
  144. package/dist/lib/session/db.d.ts +4 -1
  145. package/dist/lib/session/db.js +65 -8
  146. package/dist/lib/session/discover.d.ts +4 -0
  147. package/dist/lib/session/discover.js +13 -0
  148. package/dist/lib/session/hook-sessions.d.ts +9 -0
  149. package/dist/lib/session/hook-sessions.js +57 -8
  150. package/dist/lib/session/pid-registry.d.ts +13 -0
  151. package/dist/lib/session/render.d.ts +2 -0
  152. package/dist/lib/session/render.js +1 -1
  153. package/dist/lib/session/types.d.ts +16 -0
  154. package/dist/lib/staleness/detectors/commands.js +2 -2
  155. package/dist/lib/staleness/detectors/permissions.js +1 -45
  156. package/dist/lib/staleness/writers/commands.js +4 -5
  157. package/dist/lib/staleness/writers/hooks.js +1 -1
  158. package/dist/lib/startup/command-registry.d.ts +1 -2
  159. package/dist/lib/startup/command-registry.js +2 -4
  160. package/dist/lib/subagents-registry.js +5 -12
  161. package/dist/lib/subagents.d.ts +0 -10
  162. package/dist/lib/subagents.js +0 -12
  163. package/dist/lib/teams/agents.d.ts +0 -2
  164. package/dist/lib/teams/agents.js +10 -27
  165. package/dist/lib/teams/index.d.ts +1 -1
  166. package/dist/lib/teams/index.js +1 -1
  167. package/dist/lib/types.d.ts +17 -9
  168. package/dist/lib/usage.d.ts +28 -5
  169. package/dist/lib/usage.js +271 -8
  170. package/dist/lib/versions.js +7 -1
  171. package/dist/lib/watchdog/watchdog.d.ts +1 -1
  172. package/dist/lib/watchdog/watchdog.js +25 -13
  173. package/package.json +1 -2
  174. package/dist/commands/check.d.ts +0 -15
  175. package/dist/commands/check.js +0 -180
  176. package/dist/commands/resources.d.ts +0 -5
@@ -76,7 +76,7 @@ export function registerEventsCommand(program) {
76
76
  .description('Read the unified event stream (operational + agent activity)')
77
77
  .option('--module <name>', 'Only events from this group (e.g. teams, secrets, activity)')
78
78
  .option('--command <path>', 'Only this command path — prefix match (e.g. "teams create")')
79
- .option('--event <type>', 'Only this typed event (repeatable, e.g. secrets.get, pr.opened)', collect, [])
79
+ .option('--event <type>', 'Only this typed event (repeatable, e.g. secrets.get, secrets.unlocked, pr.opened)', collect, [])
80
80
  .option('--agent <name>', 'Only events tagged with this agent')
81
81
  .option('--since <time>', 'Only events newer than this (e.g. 2h, 7d, or ISO date)')
82
82
  .option('--audit', 'Operational events only (skip agent activity)')
@@ -11,7 +11,7 @@ import { setHelpSections } from '../lib/help.js';
11
11
  import { isInteractiveTerminal, isPromptCancelled, requireInteractiveSelection } from './utils.js';
12
12
  import { getUserAgentsDir } from '../lib/state.js';
13
13
  import { parseLoopInterval } from '../lib/loop.js';
14
- import { AGENTS } from '../lib/agents.js';
14
+ import { AGENTS, resolveAgentName, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
15
15
  import { recordDispatchedRun } from '../lib/audit/log.js';
16
16
  import { maybeShowStarNudge } from '../lib/star-nudge.js';
17
17
  import { warnUnpushedWork, shouldWarnUnpushed } from '../lib/warn-unpushed.js';
@@ -372,10 +372,10 @@ export function registerRunCommand(program) {
372
372
  .option('--no-tmux', 'Spawn the agent directly instead of wrapping it in the shared tmux session. Same effect as --raw / AGENTS_NO_TMUX=1. Use this to see the agent\'s full startup output when a launch is failing.')
373
373
  .option('--disable-tmux', 'Alias for --no-tmux.')
374
374
  .option('--timeout <duration>', 'Kill the agent after this duration (e.g., 30m, 1h, 2h30m)')
375
- .option('--fallback <agents>', 'Comma-separated agents to try on rate-limit failure. Each entry accepts an optional @version pin (e.g., codex@0.116.0,gemini). The primary runs first; if it exits with a rate-limit error, the next agent picks up via /continue handoff.')
375
+ .option('--fallback <agents>', 'Comma-separated agents to try on rate-limit failure. Each entry accepts an optional @version pin (e.g., codex@0.116.0,antigravity). The primary runs first; if it exits with a rate-limit error, the next agent picks up via /continue handoff.')
376
376
  .option('-b, --balanced', 'Shortcut for --strategy balanced. Ignored when @version is pinned.')
377
377
  .option('--strategy <strategy>', 'Version/account selection strategy: pinned | available | balanced. Defaults to run.<agent>.strategy, then balanced (spreads load across healthy accounts and skips any that are rate-limited). (Legacy `rotate` accepted as alias for `balanced`.)')
378
- .option('--acp', 'Route through the Agent Client Protocol instead of direct exec. Supported for gemini, claude (via @zed-industries/claude-code-acp adapter). Unified event stream; emits ndjson when --json.')
378
+ .option('--acp', 'Route through the Agent Client Protocol instead of direct exec. Supported for claude via @zed-industries/claude-code-acp adapter. Unified event stream; emits ndjson when --json.')
379
379
  .option('-y, --yes', 'Skip the interactive budget-confirm prompt (require_confirm_over). Never skips a hard budget block.', false)
380
380
  .option('--loop', 'Re-inject the prompt/entrypoint each iteration until a stop condition (issue #332). Guards (--max-iterations, --budget, --until) are enforced outside the agent. Writes a checkpoint after every iteration for --resume-checkpoint.')
381
381
  .option('--resume-checkpoint <file>', 'Resume a killed loop run from its checkpoint.json. Continues from the last completed iteration, reusing the same runId, session id, prompt, and loop config.')
@@ -464,7 +464,7 @@ export function registerRunCommand(program) {
464
464
  installed account for this run. Rows show identity, login state, plan,
465
465
  and available limits; unsafe accounts stay visible but disabled.
466
466
 
467
- Fallback: --fallback codex,gemini retries on rate-limit failure via /continue handoff. Each entry accepts @version.
467
+ Fallback: --fallback codex,antigravity retries on rate-limit failure via /continue handoff. Each entry accepts @version.
468
468
 
469
469
  Resume: --resume <id> continues a prior conversation (full or partial id; omit to pick interactively). claude/codex resume natively; others replay via a /continue first message. Add a prompt to continue headlessly.
470
470
 
@@ -502,6 +502,12 @@ export function registerRunCommand(program) {
502
502
  console.error(chalk.red(`Invalid account picker target: ${agentSpec}. Use agents run <agent>@.`));
503
503
  process.exit(1);
504
504
  }
505
+ // Hard-deprecated harnesses cannot be run — point the user at the successor.
506
+ const runBaseAgentId = resolveAgentName(normalizedAgentSpec.split('@')[0]);
507
+ if (runBaseAgentId && isAgentHardDeprecated(runBaseAgentId)) {
508
+ console.error(chalk.red(hardDeprecationError(runBaseAgentId)));
509
+ process.exit(1);
510
+ }
505
511
  // Account-picker conflict check runs BEFORE device=auto may set balanced,
506
512
  // so an implicit balanced preference never surfaces as a fake
507
513
  // "cannot be combined with --balanced" when the user only typed trailing @.
@@ -965,8 +971,19 @@ export function registerRunCommand(program) {
965
971
  // Working directory on the host: an explicit --remote-cwd is used
966
972
  // verbatim; --cwd/--project are made portable (a local-home absolute
967
973
  // becomes `~/…` so the remote shell re-roots it at ITS home).
974
+ //
975
+ // With neither flag, mirror the LOCAL cwd's home-relative path onto
976
+ // the host (deriveMirroredCwd). Otherwise every host run starts in the
977
+ // remote `$HOME` — launch an agent from a repo and it opens with no
978
+ // project, and you `cd` by hand every time. The same checkout at the
979
+ // same home-relative path on both boxes is the normal fleet layout, so
980
+ // the mirror usually hits; when the host lacks that directory the run
981
+ // falls back to the remote home rather than failing.
968
982
  const { toRemotePortable } = await import('../lib/project-root.js');
969
- const hostCwd = options.remoteCwd ?? (options.cwd ? toRemotePortable(options.cwd) : undefined);
983
+ const { deriveMirroredCwd } = await import('../lib/hosts/dispatch.js');
984
+ const explicitHostCwd = options.remoteCwd ?? (options.cwd ? toRemotePortable(options.cwd) : undefined);
985
+ const hostCwd = explicitHostCwd ?? deriveMirroredCwd(process.cwd());
986
+ const mirrorHostCwd = explicitHostCwd === undefined;
970
987
  const hostAddDirs = options.addDir.length > 0 ? options.addDir.map(toRemotePortable) : undefined;
971
988
  // `--resume [id]`: commander yields the string id, or `true` when the
972
989
  // flag is passed bare. A bare resume needs the interactive picker,
@@ -1104,6 +1121,7 @@ export function registerRunCommand(program) {
1104
1121
  yes: options.yes,
1105
1122
  acp: options.acp,
1106
1123
  remoteCwd: hostCwd,
1124
+ mirrorCwd: mirrorHostCwd,
1107
1125
  sessionId: hostSessionId,
1108
1126
  name: options.name,
1109
1127
  resume: resumeId,
@@ -1190,6 +1208,7 @@ export function registerRunCommand(program) {
1190
1208
  acp: options.acp,
1191
1209
  autoSecrets: options.autoSecrets,
1192
1210
  remoteCwd: hostCwd,
1211
+ mirrorCwd: mirrorHostCwd,
1193
1212
  name: options.name,
1194
1213
  resume: resumeId,
1195
1214
  sessionId: options.sessionId,
@@ -59,3 +59,7 @@ export declare function sessionHintsFromActive(sessions: Array<{
59
59
  };
60
60
  }>): SessionOutcomeHint[];
61
61
  export declare function registerFeedCommand(program: Command): void;
62
+ /** Feed view selector (RUSH-2015): decisions, progress, or both. */
63
+ export type FeedFilter = 'needs' | 'updates' | 'all';
64
+ /** Normalize a raw --filter value; unknown/empty falls back to the default. */
65
+ export declare function resolveFeedFilter(raw: string | undefined): FeedFilter;
@@ -1,6 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { ensureFeedPublishHook, listAskStats, listBlocks, recordNotified } from '../lib/feed.js';
3
- import { ensureActivityLogHook, readRecentActivity, formatActivityLine } from '../lib/activity.js';
3
+ import { ensureActivityLogHook, readRecentActivity, formatActivityLine, formatProgressUpdate } from '../lib/activity.js';
4
4
  import { postFeedStatus } from '../lib/feed-post.js';
5
5
  import { enrichBlocksFromSessions, groupBlocksByOutcome, isUnambiguousOutcomeAnswer, openBlocksForOutcome, stampBlockOutcomes, } from '../lib/feed-outcome.js';
6
6
  import { classifyBlock, filterBlocksForFeed, suppressionDigest, } from '../lib/ask-classifier.js';
@@ -219,6 +219,7 @@ export function registerFeedCommand(program) {
219
219
  .command('feed')
220
220
  .description('Open blocks (needs you) + agent status posts (feed post)')
221
221
  .option('--json', 'Output as JSON (each block stamped with its outcome + ask class)')
222
+ .option('--filter <view>', 'What to show: needs (default) · updates · all', 'needs')
222
223
  .option('--flat', 'List one block per agent instead of grouping by outcome')
223
224
  .option('--all', 'Include stalls/FYIs that policy would suppress (default: hide them)')
224
225
  .option('--local', 'Only this machine -- skip the cross-machine SSH fan-out')
@@ -232,11 +233,13 @@ export function registerFeedCommand(program) {
232
233
  .description('Post a status update to the fleet activity stream (for agents)')
233
234
  .argument('<text...>', 'What just happened — one short human line')
234
235
  .option('--session <id>', 'Session id escape hatch (default: auto from env / pid registry)')
236
+ .option('--attach <path-or-url...>', 'Attach an artifact (local file or URL); repeatable')
235
237
  .option('--json', 'Emit the written event as JSON')
236
238
  .addHelpText('after', `
237
239
  Examples:
238
240
  # Inside an agents-cli run (session identity is already in the env):
239
241
  agents feed post "CHANGELOG pushed; watching CI and mac-mini E2E"
242
+ agents feed post "cover render ready" --attach ./out/cover.png
240
243
  agents feed post "ready for review" --json
241
244
 
242
245
  # Outside a run, pass the session explicitly:
@@ -251,18 +254,20 @@ Domain facts (tickets, PRs) are not CLI flags — join them on the session at re
251
254
  // parent.opts().json — not the child. Read both.
252
255
  const flags = {
253
256
  session: opts?.session ?? cmd?.opts?.()?.session,
257
+ attach: opts?.attach ?? cmd?.opts?.()?.attach,
254
258
  json: Boolean(opts?.json ?? cmd?.opts?.()?.json ?? cmd?.parent?.opts?.()?.json),
255
259
  };
256
260
  try {
257
261
  const { event } = postFeedStatus({
258
262
  text: Array.isArray(textParts) ? textParts.join(' ') : String(textParts ?? ''),
259
263
  sessionId: flags.session,
264
+ attach: flags.attach,
260
265
  });
261
266
  if (flags.json) {
262
267
  console.log(JSON.stringify(event, null, 2));
263
268
  return;
264
269
  }
265
- console.log(formatActivityLine(event, { showHost: true }).trimStart());
270
+ console.log(formatProgressUpdate(event));
266
271
  }
267
272
  catch (err) {
268
273
  console.error(chalk.red(err.message));
@@ -273,6 +278,7 @@ Domain facts (tickets, PRs) are not CLI flags — join them on the session at re
273
278
  if (opts.device?.length)
274
279
  opts.host = [...(opts.host ?? []), ...opts.device];
275
280
  const self = machineId();
281
+ const filter = resolveFeedFilter(opts.filter);
276
282
  const includeLocal = shouldIncludeLocalFeed(opts.host, self);
277
283
  const setupWarnings = [];
278
284
  if (includeLocal) {
@@ -299,6 +305,22 @@ Domain facts (tickets, PRs) are not CLI flags — join them on the session at re
299
305
  }
300
306
  }
301
307
  }
308
+ // Updates view: deliberate progress posts only, over the local activity
309
+ // timeline (blocks are decisions, not announcements). Short-circuit the
310
+ // whole block pipeline — no remote fan-out, no dispatch policy.
311
+ if (filter === 'updates') {
312
+ for (const warning of setupWarnings) {
313
+ console.error(chalk.yellow(`Feed hook setup warning: ${warning}`));
314
+ }
315
+ if (opts.json) {
316
+ const updates = readRecentActivity({ sinceMs: Date.now() - 7 * 24 * 60 * 60 * 1000, limit: 100 })
317
+ .filter((e) => e.event === 'status.posted');
318
+ console.log(JSON.stringify(updates, null, 2));
319
+ return;
320
+ }
321
+ renderUpdatesView();
322
+ return;
323
+ }
302
324
  // Active sessions feed both the GC sweep and outcome enrichment (ticket/PR).
303
325
  let sessions = [];
304
326
  if (includeLocal) {
@@ -406,8 +428,14 @@ Domain facts (tickets, PRs) are not CLI flags — join them on the session at re
406
428
  }
407
429
  if (blocks.length === 0) {
408
430
  console.log(chalk.gray(digest ? 'No open blocks after stall suppression.' : 'No open blocks.'));
409
- if (includeLocal)
410
- renderActivityLane();
431
+ if (includeLocal) {
432
+ if (filter === 'all') {
433
+ console.log();
434
+ renderUpdatesView();
435
+ }
436
+ else
437
+ renderActivityLane();
438
+ }
411
439
  return;
412
440
  }
413
441
  // Shared fleet-comms masthead (same family as `agents mailboxes`).
@@ -430,22 +458,70 @@ Domain facts (tickets, PRs) are not CLI flags — join them on the session at re
430
458
  });
431
459
  for (const g of groups)
432
460
  renderOutcomeGroup(g, self);
433
- if (includeLocal)
434
- renderActivityLane();
461
+ if (includeLocal) {
462
+ if (filter === 'all') {
463
+ console.log();
464
+ renderUpdatesView();
465
+ }
466
+ else
467
+ renderActivityLane();
468
+ }
435
469
  });
436
470
  }
471
+ /** Normalize a raw --filter value; unknown/empty falls back to the default. */
472
+ export function resolveFeedFilter(raw) {
473
+ const v = (raw ?? '').trim().toLowerCase();
474
+ if (v === 'updates' || v === 'update')
475
+ return 'updates';
476
+ if (v === 'all')
477
+ return 'all';
478
+ return 'needs';
479
+ }
480
+ /**
481
+ * Render one activity event in the lane: deliberate progress posts
482
+ * (`status.posted`) use the rich multi-line {@link formatProgressUpdate}; hook
483
+ * milestones (PR, commit, …) keep the compact {@link formatActivityLine}.
484
+ */
485
+ function renderActivityEntry(ev) {
486
+ if (ev.event === 'status.posted') {
487
+ console.log(formatProgressUpdate(ev));
488
+ }
489
+ else {
490
+ console.log(formatActivityLine(ev, { showHost: true }));
491
+ }
492
+ }
493
+ /**
494
+ * Render the **Updates** view: deliberate progress posts only (`status.posted`),
495
+ * recency-ordered, with rich identity chips. Pure `file.edited` / git-hook noise
496
+ * is excluded so operators see announcements, not tool churn.
497
+ */
498
+ function renderUpdatesView(limit = 30) {
499
+ const updates = readRecentActivity({ sinceMs: Date.now() - 7 * 24 * 60 * 60 * 1000, limit })
500
+ .filter((e) => e.event === 'status.posted');
501
+ console.log(masthead({ title: 'updates', accent: 'cyan', host: machineId(), right: `${updates.length} post${updates.length === 1 ? '' : 's'}` }));
502
+ console.log();
503
+ if (updates.length === 0) {
504
+ console.log(chalk.gray(' No progress updates yet. Agents post them with `agents feed post "…"`.'));
505
+ return;
506
+ }
507
+ for (const ev of updates) {
508
+ console.log(formatProgressUpdate(ev));
509
+ console.log();
510
+ }
511
+ }
437
512
  /**
438
513
  * Print a compact "recent activity" lane under the feed: the last few milestone
439
- * events (plans, PRs, worktrees, sub-agents) from the append-only activity logs.
440
- * Read-only tail of the logs -- no transcript re-parsing. Silent when empty.
514
+ * events (plans, PRs, worktrees, sub-agents, progress posts) from the append-only
515
+ * activity logs. Read-only tail of the logs -- no transcript re-parsing. Silent
516
+ * when empty.
441
517
  */
442
- function renderActivityLane() {
443
- const events = readRecentActivity({ sinceMs: Date.now() - 24 * 60 * 60 * 1000, limit: 6 })
518
+ function renderActivityLane(limit = 6) {
519
+ const events = readRecentActivity({ sinceMs: Date.now() - 24 * 60 * 60 * 1000, limit })
444
520
  .filter((e) => e.tier === 'milestone');
445
521
  if (events.length === 0)
446
522
  return;
447
523
  console.log(chalk.bold('\n recent activity'));
448
524
  for (const ev of events)
449
- console.log(formatActivityLine(ev, { showHost: true }));
525
+ renderActivityEntry(ev);
450
526
  console.log(chalk.gray(' → agents activity for the full stream'));
451
527
  }
@@ -26,7 +26,7 @@ export function registerHarnessCommands(program) {
26
26
  .alias('harnesses')
27
27
  .description('Custom harnesses — name a (host CLI + model) combo and run it like a native agent type.')
28
28
  .addHelpText('after', `
29
- A custom harness pins a host CLI (opencode, claude, codex, grok, gemini, ...) to a
29
+ A custom harness pins a host CLI (opencode, claude, codex, grok, antigravity, ...) to a
30
30
  model and gives it a name. 'agents run <name>' then behaves like a native agent
31
31
  type, and 'agents repo push user' syncs it to every device.
32
32
 
@@ -49,7 +49,7 @@ Examples:
49
49
  cmd
50
50
  .command('add <name>')
51
51
  .description('Create a custom harness from a host + model (or apply a built-in preset).')
52
- .option('--host <agent>', 'Host CLI to run under (opencode, claude, codex, grok, gemini, ...) — pair with --model')
52
+ .option('--host <agent>', 'Host CLI to run under (opencode, claude, codex, grok, antigravity, ...) — pair with --model')
53
53
  .option('--model <id>', 'Model id to pin on the host (e.g., meta/muse-spark-1.1) — pair with --host')
54
54
  .option('--base-url <url>', 'Custom endpoint base URL (claude/codex hosts)')
55
55
  .option('--auth-provider <provider>', 'Attach a keychain-backed API key under this provider (for private endpoints)')
@@ -221,7 +221,7 @@ When to use:
221
221
  hooksCmd
222
222
  .command('add [source]')
223
223
  .description('Install hooks from a source (GitHub, local) or pick from central storage')
224
- .option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or gemini@default')
224
+ .option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or antigravity@default')
225
225
  .option('--names <list>', 'Hook names from ~/.agents/hooks/ (comma-separated)')
226
226
  .option('-y, --yes', 'Skip all prompts')
227
227
  .addHelpText('after', `
@@ -29,8 +29,8 @@ import * as fs from 'fs';
29
29
  import * as os from 'os';
30
30
  import * as path from 'path';
31
31
  import { confirm } from '@inquirer/prompts';
32
- import { ALL_AGENT_IDS } from '../lib/agents.js';
33
- import { AGENTS, getCliPath, getCliVersion, agentLabel, resolveAgentName } from '../lib/agents.js';
32
+ import { MANAGED_AGENT_IDS } from '../lib/agents.js';
33
+ import { AGENTS, getCliPath, getCliVersion, agentLabel, resolveAgentName, isAgentHardDeprecated, hardDeprecationError } from '../lib/agents.js';
34
34
  import { getVersionDir } from '../lib/versions.js';
35
35
  import { finalizeImport, importAgentBinary, importAgentConfig, importInstallScriptBinary, seedIsolatedConfigFromLocal, isValidImportVersion, resolvePackageDirFromBinary, } from '../lib/import.js';
36
36
  import { isPromptCancelled, isInteractiveTerminal } from './utils.js';
@@ -38,7 +38,11 @@ async function runImport(agentArg, opts) {
38
38
  const agentId = resolveAgentName(agentArg);
39
39
  if (!agentId) {
40
40
  console.error(chalk.red(`Unknown agent: ${agentArg}`));
41
- console.error(chalk.gray(`Known agents: ${ALL_AGENT_IDS.join(', ')}`));
41
+ console.error(chalk.gray(`Known agents: ${MANAGED_AGENT_IDS.join(', ')}`));
42
+ process.exit(1);
43
+ }
44
+ if (isAgentHardDeprecated(agentId)) {
45
+ console.error(chalk.red(hardDeprecationError(agentId)));
42
46
  process.exit(1);
43
47
  }
44
48
  const agent = AGENTS[agentId];
@@ -340,7 +344,7 @@ When to use:
340
344
  under agents-cli management without reinstalling. Creates a symlink farm
341
345
  pointing at the existing install — nothing is copied or moved (except the
342
346
  agent's config dir, which is moved into the version's home). Works for both
343
- npm-style packages (claude, codex, gemini, opencode, openclaw) and
347
+ npm-style packages (claude, codex, opencode, openclaw) and
344
348
  installScript-based agents (grok, antigravity, cursor, kiro, goose).
345
349
  `)
346
350
  .action((...args) => withIsolationBoundary(() => runImport(...args)));
@@ -358,6 +358,12 @@ async function runStats(opts) {
358
358
  console.log(` ${k.padEnd(30)} ${v}`);
359
359
  }
360
360
  }
361
+ if (Object.keys(s.byActor).length) {
362
+ console.log(chalk.bold('\n By actor:'));
363
+ for (const [k, v] of Object.entries(s.byActor).sort((a, b) => b[1] - a[1])) {
364
+ console.log(` ${k.padEnd(30)} ${v}`);
365
+ }
366
+ }
361
367
  console.log();
362
368
  }
363
369
  /** Register the top-level `agents logs` command. */
@@ -13,7 +13,7 @@ import { listInstalledVersions, getGlobalDefault, resolveVersion, resolveVersion
13
13
  import { getModelCatalog, locateModelSource } from '../lib/models.js';
14
14
  import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/width.js';
15
15
  import { wrapJoined } from './inspect.js';
16
- const MODEL_CAPABLE_AGENTS = ['claude', 'codex', 'gemini', 'opencode', 'cursor', 'openclaw', 'antigravity', 'kimi'];
16
+ const MODEL_CAPABLE_AGENTS = ['claude', 'codex', 'opencode', 'cursor', 'openclaw', 'antigravity', 'kimi'];
17
17
  /**
18
18
  * Agents that don't necessarily install under ~/.agents/versions (cursor ships
19
19
  * via a curl script). For these, fall back to the PATH binary and synthesize
@@ -10,7 +10,7 @@ import * as path from 'path';
10
10
  import chalk from 'chalk';
11
11
  import ora from 'ora';
12
12
  import { getAgentsDir, getEnabledExtraRepos } from '../lib/state.js';
13
- import { AGENTS, ALL_AGENT_IDS, getAllCliStates, agentLabel, } from '../lib/agents.js';
13
+ import { AGENTS, getAllCliStates, agentLabel, } from '../lib/agents.js';
14
14
  import { capableAgents } from '../lib/capabilities.js';
15
15
  import { DEFAULT_REGISTRIES } from '../lib/types.js';
16
16
  import { getRegistries, setRegistry, removeRegistry, search as searchRegistries, resolvePackage, validatedNpmSpec, validatedPyPISpec, buildSkillIndex, verifySkillIntegrity, parseOwnerRepoFromRemote, } from '../lib/registry.js';
@@ -419,7 +419,7 @@ the sha256 in the index and abort on mismatch.
419
419
  console.log(chalk.gray('\n Register + search + install:'));
420
420
  console.log(` ${chalk.green(`agents registry add skill ${registryName} ${rawUrl}`)}`);
421
421
  console.log(` ${chalk.green(`agents search ${index.skills[0].name} --type skill`)}`);
422
- console.log(` ${chalk.green(`agents install skill:${index.skills[0].name} --agents claude,codex,gemini`)}`);
422
+ console.log(` ${chalk.green(`agents install skill:${index.skills[0].name} --agents claude,codex,cursor`)}`);
423
423
  });
424
424
  // ==========================================================================
425
425
  // INSTALL COMMAND (unified package installation)
@@ -427,7 +427,7 @@ the sha256 in the index and abort on mismatch.
427
427
  program
428
428
  .command('install <identifier>')
429
429
  .description('Install a package by registry name (mcp:notion), GitHub URL (gh:user/repo), or skill identifier')
430
- .option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or gemini@default')
430
+ .option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or cursor@default')
431
431
  .option('--types <list>', 'When source is a repo: comma-separated resource types to install (skills,workflows,commands,hooks,permissions,subagents,mcp)')
432
432
  .option('--names <list>', 'When source is a repo: comma-separated resource names within the selected types')
433
433
  .option('-y, --yes', 'Auto-install any missing agent versions without prompting')
@@ -624,10 +624,10 @@ When to use:
624
624
  console.log(` ${count} ${kind}`);
625
625
  }
626
626
  const gitCliStates = await getAllCliStates();
627
- const installedAgents = ALL_AGENT_IDS.filter((id) => gitCliStates[id]?.installed || listInstalledVersions(id).length > 0);
627
+ const installedAgents = capableAgents('commands').filter((id) => gitCliStates[id]?.installed || listInstalledVersions(id).length > 0);
628
628
  let targets;
629
629
  if (options.agents) {
630
- const resolved = await resolveInstalledAgentTargetsAutoInstalling(options.agents, ALL_AGENT_IDS, { yes: options.yes });
630
+ const resolved = await resolveInstalledAgentTargetsAutoInstalling(options.agents, capableAgents('commands'), { yes: options.yes });
631
631
  if (!resolved) {
632
632
  console.log(chalk.gray('Cancelled.'));
633
633
  return;
@@ -635,7 +635,7 @@ When to use:
635
635
  targets = resolved;
636
636
  }
637
637
  else {
638
- targets = resolveConfiguredAgentTargets(installedAgents, undefined, ALL_AGENT_IDS);
638
+ targets = resolveConfiguredAgentTargets(installedAgents, undefined, capableAgents('commands'));
639
639
  }
640
640
  if (targets.selectedAgents.length === 0) {
641
641
  console.log(chalk.yellow('\nNo agents selected.'));
@@ -208,7 +208,7 @@ When to use:
208
208
  permissionsCmd
209
209
  .command('add [source]')
210
210
  .description('Import permissions from an agent config, YAML, or GitHub, then apply to versions')
211
- .option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or gemini@default')
211
+ .option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or antigravity@default')
212
212
  .option('--names <list>', 'Permission set names from ~/.agents/permissions/ (comma-separated)')
213
213
  .option('--all', 'Apply to all installed versions instead of just defaults')
214
214
  .option('--replace', 'Replace managed permission block instead of merging (drops rules no longer in the set)')
@@ -272,7 +272,7 @@ export function registerProfilesCommands(program) {
272
272
  .command('profiles')
273
273
  .description('Named bundles of (host CLI, endpoint, model, auth) — run Kimi/DeepSeek/Qwen/etc through Claude Code without a proxy.')
274
274
  .addHelpText('after', `
275
- A profile pins a host CLI (claude, codex, gemini, ...) to a non-default endpoint
275
+ A profile pins a host CLI (claude, codex, antigravity, ...) to a non-default endpoint
276
276
  and model, with a keychain-backed API key. Running 'agents run <profile>' spawns
277
277
  the host CLI with the right env vars — no plaintext tokens, no local proxy.
278
278
 
@@ -6,7 +6,7 @@
6
6
  * etc.). It is replaced by the explicit split:
7
7
  *
8
8
  * - `agents repo pull <alias>` — git fetch+ff on system / user / extra
9
- * - `agents repo refresh [agent]` — re-materialize installed version homes
9
+ * - `agents sync [--local] [agent]` — re-materialize installed version homes
10
10
  * - `agents setup` — first-time bootstrap
11
11
  *
12
12
  * The command is kept registered so old muscle-memory invocations get a clear
@@ -6,7 +6,7 @@
6
6
  * etc.). It is replaced by the explicit split:
7
7
  *
8
8
  * - `agents repo pull <alias>` — git fetch+ff on system / user / extra
9
- * - `agents repo refresh [agent]` — re-materialize installed version homes
9
+ * - `agents sync [--local] [agent]` — re-materialize installed version homes
10
10
  * - `agents setup` — first-time bootstrap
11
11
  *
12
12
  * The command is kept registered so old muscle-memory invocations get a clear
@@ -15,20 +15,20 @@
15
15
  import { setHelpSections } from '../lib/help.js';
16
16
  const REDIRECT = 'agents-cli: "agents pull" was removed.\n' +
17
17
  ' Git pull a repo: agents repo pull <alias> (system | user | <extra>)\n' +
18
- ' Re-materialize: agents repo refresh [agent] (claude | codex | ...)\n' +
18
+ ' Re-materialize: agents sync --local (all agents; or: agents sync <agent>)\n' +
19
19
  ' First-time setup: agents setup\n\n';
20
20
  /** Register the deprecated `agents pull` command as a hard-error redirect. */
21
21
  export function registerPullCommand(program) {
22
22
  const pullCmd = program
23
23
  .command('pull [agent]')
24
- .description('Removed. See `agents repo pull` + `agents repo refresh`.')
24
+ .description('Removed. See `agents repo pull` + `agents sync`.')
25
25
  .option('-y, --yes', '(no-op)')
26
26
  .option('--skip-clis', '(no-op)');
27
27
  setHelpSections(pullCmd, {
28
28
  notes: `
29
29
  Removed. Equivalents:
30
30
  agents repo pull <alias> git pull (system | user | extra)
31
- agents repo refresh [agent] re-materialize version homes
31
+ agents sync --local re-materialize version homes (or: agents sync <agent>)
32
32
  agents setup first-time bootstrap
33
33
  `,
34
34
  });
@@ -69,10 +69,88 @@ export declare function formatResourceDelta(entries: {
69
69
  action: ChangeAction;
70
70
  file: string;
71
71
  }[], maxParts?: number): string;
72
+ /** One side of a divergence: the resource delta plus the raw commit count (fallback). */
73
+ interface RepoDivergence {
74
+ delta: ResourceDelta;
75
+ commits: number;
76
+ }
77
+ /**
78
+ * Structured status for one repo. `raw` is a free-form trailer for special cases
79
+ * (missing repo, no git remote, error) that don't fit the columns; otherwise the
80
+ * fields feed whichever layout (table / cards) the terminal width selects.
81
+ */
82
+ export interface RepoRow {
83
+ alias: string;
84
+ raw?: string;
85
+ branch?: string;
86
+ tracking?: boolean;
87
+ pull?: RepoDivergence;
88
+ push?: RepoDivergence;
89
+ clean?: boolean;
90
+ local?: ResourceDelta;
91
+ url?: string;
92
+ commit?: string;
93
+ }
72
94
  /** `owner/repo` from a git URL, for the compact card header. Falls back to the raw URL. */
73
95
  export declare function repoSlug(url: string): string;
74
96
  /** Pack colored phrases into `, `-joined lines no wider than `width`. */
75
97
  export declare function wrapPhrases(parts: string[], width: number): string[];
98
+ /** Options shared by `agents repo list` and the `agents repo status` alias. */
99
+ export interface RepoStatusOptions {
100
+ verbose?: boolean;
101
+ json?: boolean;
102
+ /** Fan out across every reachable fleet device (`--devices-all`/`--hosts-all`). */
103
+ devicesAll?: boolean;
104
+ hostsAll?: boolean;
105
+ /** `all`, or a comma-separated device list (`--devices`/`--hosts`). */
106
+ devices?: string;
107
+ hosts?: string;
108
+ }
109
+ /**
110
+ * Per-device repo status — the unit of the `--devices-all` fleet fan-out. One
111
+ * entry per device: its parsed repo rows when reachable, or `reachable: false`
112
+ * when the peer could not be queried (kept in the model so the table can mark it).
113
+ */
114
+ export interface DeviceRepoStatus {
115
+ device: string;
116
+ reachable: boolean;
117
+ rows?: RepoRow[];
118
+ }
119
+ /**
120
+ * Recursion guard for the fleet fan-out, passed as an env var (not a CLI flag)
121
+ * so an OLDER remote `agents` that predates this feature ignores it harmlessly
122
+ * instead of erroring on an unknown option. A peer new enough to fan out reads
123
+ * it and answers only for itself. Mirrors `NO_FANOUT_ENV` in remote-active.ts.
124
+ */
125
+ export declare const NO_REPO_FANOUT_ENV = "AGENTS_REPO_LOCAL";
126
+ /**
127
+ * Parse one peer's `repo status --json` stdout (a RepoRow[]) into a single
128
+ * device entry tagged with `machine`. Defensive against version skew: non-JSON
129
+ * or a non-array yields no entry, and non-object rows are dropped rather than
130
+ * throwing. Exported for unit testing without a live tailnet.
131
+ */
132
+ export declare function parseRemoteRepoRows(stdout: string, machine: string): DeviceRepoStatus[];
133
+ /**
134
+ * Render the fleet repo-status table: one row per (device, repo), with the
135
+ * device name shown once per group. SYNC and CHANGES reuse the same compact
136
+ * cells as the local wide table (`↓N (…) ↑N` / `clean` / `~N edits`), and an
137
+ * unreachable device collapses to a single `unreachable` marker row. Pure — no
138
+ * I/O — so the aggregation/formatting is unit-testable without live devices.
139
+ */
140
+ export declare function renderDeviceStatusRows(results: DeviceRepoStatus[]): string[];
141
+ /** Where a `repo status` run should report: this machine only, or the fleet. */
142
+ type DeviceIntent = {
143
+ all: true;
144
+ } | {
145
+ hosts: string[];
146
+ };
147
+ /**
148
+ * Read the device-scope flags into an intent, or null for the default local-only
149
+ * run. `--devices-all`/`--hosts-all` (and `--devices all`/`--hosts all`) sweep
150
+ * every reachable peer; `--devices box1,box2` targets an explicit list. A peer
151
+ * carrying {@link NO_REPO_FANOUT_ENV} never fans out again (recursion guard).
152
+ */
153
+ export declare function resolveDeviceIntent(opts: RepoStatusOptions): DeviceIntent | null;
76
154
  /** Register the `agents repos` command tree (`repo` is a convenience alias). */
77
155
  export declare function registerRepoCommands(program: Command): void;
78
156
  export {};