@phnx-labs/agents-cli 1.22.43 → 1.22.44

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 (164) hide show
  1. package/CHANGELOG.md +77 -0
  2. package/README.md +14 -12
  3. package/dist/bootstrap.js +8 -6
  4. package/dist/cli/command-registry.d.ts +0 -5
  5. package/dist/cli/command-registry.js +1 -17
  6. package/dist/commands/apply.d.ts +7 -10
  7. package/dist/commands/apply.js +15 -35
  8. package/dist/commands/artifacts.js +4 -5
  9. package/dist/commands/audit.d.ts +6 -6
  10. package/dist/commands/audit.js +12 -12
  11. package/dist/commands/auth.js +7 -3
  12. package/dist/commands/beta.d.ts +7 -1
  13. package/dist/commands/beta.js +16 -9
  14. package/dist/commands/commands.js +1 -1
  15. package/dist/commands/doctor.js +1 -4
  16. package/dist/commands/events.d.ts +2 -2
  17. package/dist/commands/events.js +6 -5
  18. package/dist/commands/exec.d.ts +1 -1
  19. package/dist/commands/exec.js +8 -4
  20. package/dist/commands/factory.d.ts +1 -1
  21. package/dist/commands/fleet-capture.d.ts +1 -1
  22. package/dist/commands/fleet-capture.js +4 -5
  23. package/dist/commands/harness.js +1 -49
  24. package/dist/commands/hooks.js +1 -1
  25. package/dist/commands/insights.d.ts +1 -1
  26. package/dist/commands/insights.js +3 -2
  27. package/dist/commands/menubar.js +58 -1
  28. package/dist/commands/org.d.ts +5 -0
  29. package/dist/commands/org.js +82 -29
  30. package/dist/commands/output.js +2 -2
  31. package/dist/commands/prune.js +5 -4
  32. package/dist/commands/routines.test-fixture.d.ts +86 -0
  33. package/dist/commands/routines.test-fixture.js +345 -0
  34. package/dist/commands/sessions.test-fixture.d.ts +24 -0
  35. package/dist/commands/sessions.test-fixture.js +224 -0
  36. package/dist/commands/setup.js +5 -1
  37. package/dist/commands/share.d.ts +9 -10
  38. package/dist/commands/share.js +68 -50
  39. package/dist/commands/skills.js +1 -1
  40. package/dist/commands/ssh.js +15 -62
  41. package/dist/commands/subagents.js +1 -1
  42. package/dist/commands/view.js +5 -2
  43. package/dist/lib/analytics/mix-commands.d.ts +6 -28
  44. package/dist/lib/analytics/mix-commands.js +14 -40
  45. package/dist/lib/audit/log.d.ts +2 -2
  46. package/dist/lib/audit/log.js +2 -2
  47. package/dist/lib/beta.js +1 -1
  48. package/dist/lib/browser/domain-skills.d.ts +26 -6
  49. package/dist/lib/browser/domain-skills.js +81 -43
  50. package/dist/lib/daemon/daemon.js +11 -0
  51. package/dist/lib/daemon/daemon.test-fixture.d.ts +24 -0
  52. package/dist/lib/daemon/daemon.test-fixture.js +71 -0
  53. package/dist/lib/daemon-ticks.d.ts +38 -1
  54. package/dist/lib/daemon-ticks.js +52 -5
  55. package/dist/lib/device-config.js +1 -2
  56. package/dist/lib/devices/discovery-policy.d.ts +3 -3
  57. package/dist/lib/devices/discovery-policy.js +3 -3
  58. package/dist/lib/devices/fleet.d.ts +4 -8
  59. package/dist/lib/devices/fleet.js +3 -15
  60. package/dist/lib/devices/health-report.js +3 -3
  61. package/dist/lib/devices/pool.d.ts +0 -6
  62. package/dist/lib/devices/pool.js +0 -6
  63. package/dist/lib/devices/registry.d.ts +0 -15
  64. package/dist/lib/devices/registry.js +0 -9
  65. package/dist/lib/devices/stats-cache.d.ts +12 -0
  66. package/dist/lib/devices/stats-cache.js +24 -5
  67. package/dist/lib/fleet/capture.d.ts +1 -1
  68. package/dist/lib/fleet/manifest.js +1 -1
  69. package/dist/lib/fleet/remote-login.d.ts +2 -2
  70. package/dist/lib/fleet/remote-login.js +3 -4
  71. package/dist/lib/hosts/dispatch.d.ts +2 -0
  72. package/dist/lib/hosts/dispatch.js +8 -1
  73. package/dist/lib/hosts/passthrough.js +0 -1
  74. package/dist/lib/hosts/providers/devices.js +1 -13
  75. package/dist/lib/hosts/ready.d.ts +12 -3
  76. package/dist/lib/hosts/ready.js +27 -12
  77. package/dist/lib/hosts/registry.d.ts +4 -5
  78. package/dist/lib/hosts/registry.js +3 -7
  79. package/dist/lib/installations/versions.js +21 -2
  80. package/dist/lib/menubar/MenubarHelper.app/Contents/Info.plist +1 -5
  81. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  82. package/dist/lib/menubar/MenubarHelper.app/Contents/_CodeSignature/CodeResources +2 -15
  83. package/dist/lib/menubar/install-menubar.d.ts +109 -0
  84. package/dist/lib/menubar/install-menubar.js +199 -4
  85. package/dist/lib/prix-account.d.ts +2 -1
  86. package/dist/lib/prix-account.js +2 -1
  87. package/dist/lib/remote-agents-json.js +1 -7
  88. package/dist/lib/routines-placement.d.ts +1 -1
  89. package/dist/lib/routines-placement.js +1 -1
  90. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  91. package/dist/lib/secrets/Agents CLI.app/Contents/Info.plist +0 -2
  92. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  93. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -13
  94. package/dist/lib/session/active.d.ts +4 -16
  95. package/dist/lib/session/active.js +2 -6
  96. package/dist/lib/session/db.d.ts +13 -98
  97. package/dist/lib/session/db.js +4 -11
  98. package/dist/lib/session/discover.d.ts +5 -81
  99. package/dist/lib/session/discover.js +5 -5
  100. package/dist/lib/session/remote/remote-bundle.d.ts +12 -0
  101. package/dist/lib/session/remote/remote-bundle.js +61 -0
  102. package/dist/lib/session/remote/remote-list.d.ts +148 -0
  103. package/dist/lib/session/remote/remote-list.js +607 -0
  104. package/dist/lib/session/remote/remote.d.ts +132 -0
  105. package/dist/lib/session/remote/remote.js +314 -0
  106. package/dist/lib/session/remote/watch.d.ts +101 -0
  107. package/dist/lib/session/remote/watch.js +241 -0
  108. package/dist/lib/session/remote-bundle.d.ts +1 -12
  109. package/dist/lib/session/remote-bundle.js +3 -61
  110. package/dist/lib/session/remote-list.d.ts +1 -148
  111. package/dist/lib/session/remote-list.js +3 -607
  112. package/dist/lib/session/remote.d.ts +1 -132
  113. package/dist/lib/session/remote.js +3 -314
  114. package/dist/lib/session/watch.d.ts +1 -101
  115. package/dist/lib/session/watch.js +3 -242
  116. package/dist/lib/share/config.d.ts +12 -2
  117. package/dist/lib/share/config.js +47 -11
  118. package/dist/lib/share/delete.js +1 -1
  119. package/dist/lib/share/publish.js +1 -1
  120. package/dist/lib/share/worker-template.js +2 -2
  121. package/dist/lib/smart-launch.d.ts +4 -4
  122. package/dist/lib/smart-launch.js +8 -18
  123. package/dist/lib/staleness/index.d.ts +3 -1
  124. package/dist/lib/staleness/index.js +21 -1
  125. package/dist/lib/staleness/types.d.ts +11 -0
  126. package/dist/lib/staleness/writers/commands.js +13 -4
  127. package/dist/lib/staleness/writers/hooks.js +3 -1
  128. package/dist/lib/staleness/writers/mcp.d.ts +0 -7
  129. package/dist/lib/staleness/writers/mcp.js +16 -1
  130. package/dist/lib/staleness/writers/rules.js +1 -1
  131. package/dist/lib/staleness/writers/skills.js +3 -1
  132. package/dist/lib/staleness/writers/subagents.d.ts +0 -9
  133. package/dist/lib/staleness/writers/subagents.js +18 -1
  134. package/dist/lib/staleness/writers/types.d.ts +10 -0
  135. package/dist/lib/startup/command-registry.d.ts +7 -1
  136. package/dist/lib/startup/command-registry.js +17 -5
  137. package/dist/lib/startup/root-command.d.ts +18 -1
  138. package/dist/lib/startup/root-command.js +18 -1
  139. package/dist/lib/state.js +7 -0
  140. package/dist/lib/teams/placement-probe.js +27 -12
  141. package/dist/lib/teams/scheduler.d.ts +3 -1
  142. package/dist/lib/types.d.ts +1 -1
  143. package/dist/lib/view-types.d.ts +3 -0
  144. package/package.json +2 -3
  145. package/dist/bin/agents +0 -0
  146. package/dist/commands/serve.d.ts +0 -10
  147. package/dist/commands/serve.js +0 -68
  148. package/dist/commands/trends.d.ts +0 -10
  149. package/dist/commands/trends.js +0 -12
  150. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  151. package/dist/lib/menubar/MenubarHelper.app/Contents/Resources/AppIcon.icns +0 -0
  152. package/dist/lib/secrets/Agents CLI.app/Contents/Resources/AppIcon.icns +0 -0
  153. package/dist/lib/serve/control.d.ts +0 -95
  154. package/dist/lib/serve/control.js +0 -260
  155. package/dist/lib/serve/data.d.ts +0 -81
  156. package/dist/lib/serve/data.js +0 -91
  157. package/dist/lib/serve/page.d.ts +0 -7
  158. package/dist/lib/serve/page.js +0 -140
  159. package/dist/lib/serve/server.d.ts +0 -80
  160. package/dist/lib/serve/server.js +0 -145
  161. package/dist/lib/serve/stream.d.ts +0 -43
  162. package/dist/lib/serve/stream.js +0 -116
  163. package/dist/lib/serve/token.d.ts +0 -35
  164. package/dist/lib/serve/token.js +0 -85
@@ -1,5 +1,6 @@
1
1
  import chalk from 'chalk';
2
2
  import { ALL_BETA_FEATURES, getBetaConfigLocation, getEnabledBetaFeatures, setBetaEnabled, } from '../lib/beta.js';
3
+ import { setHelpSections } from '../lib/help.js';
3
4
  const BETA_DESCRIPTIONS = {
4
5
  factory: 'Cloud-based agent dispatch via Rush Factory',
5
6
  };
@@ -20,16 +21,22 @@ function parseFeatures(values) {
20
21
  }
21
22
  return values.filter((v) => valid.has(v));
22
23
  }
23
- export function registerBetaCommands(program) {
24
- const beta = program
24
+ /** Register `agents setup beta` under the parent `setup` command. */
25
+ export function registerBetaCommands(setupCmd) {
26
+ const beta = setupCmd
25
27
  .command('beta')
26
- .description('Enable or disable preview features like factory.')
27
- .addHelpText('after', `
28
- Examples:
29
- agents beta list
30
- agents beta enable factory
31
- agents beta disable factory
32
- `);
28
+ .description('Enable or disable preview features like factory.');
29
+ setHelpSections(beta, {
30
+ examples: `
31
+ agents setup beta list
32
+ agents setup beta enable factory
33
+ agents setup beta disable factory
34
+ `,
35
+ notes: `
36
+ Writes the enabled set to ~/.agents/agents.yaml (beta.enabled). Top-level
37
+ \`agents beta\` is gone — use this group.
38
+ `,
39
+ });
33
40
  beta
34
41
  .command('list')
35
42
  .description('Show available beta features and whether they are enabled.')
@@ -392,7 +392,7 @@ Examples:
392
392
  }
393
393
  else {
394
394
  console.log(chalk.green(`\nRemoved ${removed} command(s) from version homes.`));
395
- console.log(chalk.gray('Central source unchanged. Commands will re-sync on next agent launch.'));
395
+ console.log(chalk.gray("Central source unchanged. Restore any target with 'agents sync <agent>@<version> --yes'."));
396
396
  }
397
397
  });
398
398
  // `commands prune` moved to the top-level `agents prune cleanup` command.
@@ -2,7 +2,7 @@ import { IsolationBoundaryError } from '../lib/installations/shims.js';
2
2
  import { explainIsolationBoundary } from '../lib/isolation-boundary-report.js';
3
3
  import { addHostOption } from '../lib/hosts/option.js';
4
4
  import { buildRemoteAgentsInvocation } from '../lib/hosts/remote-cmd.js';
5
- import { loadDevices, isControlDevice } from '../lib/devices/registry.js';
5
+ import { loadDevices } from '../lib/devices/registry.js';
6
6
  import { fanOutDevices, planFleetTargets, remoteFleetTargets } from '../lib/devices/fleet.js';
7
7
  import { enterDoctorOverviewGate, invalidateDoctorOverviewCache, writeDoctorOverviewCache } from '../lib/devices/doctor-overview-cache.js';
8
8
  import { fleetDialTarget } from '../lib/devices/connect.js';
@@ -183,9 +183,6 @@ async function resolveFleetTargets(opts) {
183
183
  // Normalize names so zion/ZION/zion.local all match machineId() and we never
184
184
  // self-SSH the local box during fleet probes (RUSH-2114).
185
185
  .filter((d) => normalizeHost(d.name) !== localName)
186
- // Control devices (a cockpit) never run agents — skip them in the fleet
187
- // fan-out (an explicit --device <name> still resolves above).
188
- .filter((d) => !isControlDevice(d))
189
186
  .map((d) => ({
190
187
  name: d.name,
191
188
  sshTarget: d.name,
@@ -7,7 +7,7 @@
7
7
  * (per-session activity: plans, PRs, worktrees, sub-agents, artifacts). Run-dispatch
8
8
  * outcomes land here as `run.dispatched` (readable via `--include runs`).
9
9
  *
10
- * `agents audit` and `agents logs` are thin aliases of this command.
10
+ * `agents events audit` and `agents logs` are thin aliases of this command.
11
11
  *
12
12
  * Filter with sessions-style `--include` / `--exclude` families (ops, activity,
13
13
  * commands, runs, security) plus field filters (`--module`, `--event`, …).
@@ -49,7 +49,7 @@ export interface EventsOptions {
49
49
  }
50
50
  /** Add the one canonical event-reader option surface to a command or alias. */
51
51
  export declare function addEventsReadOptions(command: Command, includeAuditFlag?: boolean): Command;
52
- /** Canonical reader used by `agents events`, `agents audit`, and `agents logs`. */
52
+ /** Canonical reader used by `agents events`, `agents events audit`, and `agents logs`. */
53
53
  export declare function runEventsCommand(options: EventsOptions, forceAudit?: boolean): Promise<void>;
54
54
  /** Shared by `events stats` and the `logs stats` alias. */
55
55
  export declare function runEventsStats(opts: {
@@ -7,7 +7,7 @@
7
7
  * (per-session activity: plans, PRs, worktrees, sub-agents, artifacts). Run-dispatch
8
8
  * outcomes land here as `run.dispatched` (readable via `--include runs`).
9
9
  *
10
- * `agents audit` and `agents logs` are thin aliases of this command.
10
+ * `agents events audit` and `agents logs` are thin aliases of this command.
11
11
  *
12
12
  * Filter with sessions-style `--include` / `--exclude` families (ops, activity,
13
13
  * commands, runs, security) plus field filters (`--module`, `--event`, …).
@@ -19,6 +19,7 @@ import { readUnifiedEvents } from '../lib/event-stream.js';
19
19
  import { parseFamilyList, EVENT_FAMILIES } from '../lib/event-families.js';
20
20
  import { ingestBatch } from '../lib/events-ingest.js';
21
21
  import { setHelpSections } from '../lib/help.js';
22
+ import { registerAuditCommands } from './audit.js';
22
23
  /**
23
24
  * Resolve `--limit` into a record cap. `0` means "no cap" — without it there is
24
25
  * no way to read the whole stream, and any aggregation (group-by failure, count
@@ -215,7 +216,7 @@ export function addEventsReadOptions(command, includeAuditFlag = true) {
215
216
  }
216
217
  return command;
217
218
  }
218
- /** Canonical reader used by `agents events`, `agents audit`, and `agents logs`. */
219
+ /** Canonical reader used by `agents events`, `agents events audit`, and `agents logs`. */
219
220
  export async function runEventsCommand(options, forceAudit = false) {
220
221
  if (options.follow) {
221
222
  await followLog();
@@ -356,9 +357,8 @@ Examples:
356
357
  agents events -f Live tail (operational)
357
358
  agents events stats
358
359
  agents events rotate --days 7
359
-
360
- agents audit Alias of: events --include runs
361
- agents logs Alias of: events`)
360
+ agents events audit Alias of: events --include runs
361
+ agents logs Run log viewer (also aliases events audit/stats/rotate)`)
362
362
  .action((_options, command) => runEventsCommand(command.optsWithGlobals()));
363
363
  // `events` both reads (its own action, above) and writes (this subcommand) —
364
364
  // the same shape as `feed` / `feed post`.
@@ -375,6 +375,7 @@ Examples:
375
375
  .option('--days <n>', 'Retention period in days (default 7)', '7')
376
376
  .option('--max-mb <n>', 'Total event storage ceiling in MiB (default 50)', '50')
377
377
  .action((opts) => runEventsRotate(opts));
378
+ registerAuditCommands(events);
378
379
  }
379
380
  /** Shared by `events rotate` and the `logs rotate` alias. */
380
381
  export function runEventsRotate(opts) {
@@ -15,7 +15,6 @@ export interface RunAccountPickerRequest {
15
15
  }
16
16
  /** Distinguish a terminal account-picker marker from an explicit @version pin. */
17
17
  export declare function parseRunAccountPickerRequest(agentSpec: string): RunAccountPickerRequest;
18
- /** Return every option whose routing semantics conflict with a local account choice. */
19
18
  /**
20
19
  * The `--device` alias family — the flags that mean "dispatch this run to another
21
20
  * machine over SSH". `--device` is canonical; `--on`/`--computer` are hidden
@@ -30,6 +29,7 @@ export declare function hostTargetGiven(options: {
30
29
  on?: string;
31
30
  computer?: string;
32
31
  }): string[];
32
+ /** Return every option whose selection semantics conflict with an account choice. */
33
33
  export declare function runAccountPickerConflicts(options: {
34
34
  resume?: string | boolean;
35
35
  strategy?: string;
@@ -36,7 +36,6 @@ export function parseRunAccountPickerRequest(agentSpec) {
36
36
  valid: !requested || (!!normalizedAgentSpec && !normalizedAgentSpec.includes('@')),
37
37
  };
38
38
  }
39
- /** Return every option whose routing semantics conflict with a local account choice. */
40
39
  /**
41
40
  * The `--device` alias family — the flags that mean "dispatch this run to another
42
41
  * machine over SSH". `--device` is canonical; `--on`/`--computer` are hidden
@@ -48,6 +47,7 @@ export function parseRunAccountPickerRequest(agentSpec) {
48
47
  export function hostTargetGiven(options) {
49
48
  return [options.host, options.device, options.on, options.computer].filter((v) => !!v);
50
49
  }
50
+ /** Return every option whose selection semantics conflict with an account choice. */
51
51
  export function runAccountPickerConflicts(options) {
52
52
  const conflicts = [];
53
53
  if (options.resume !== undefined)
@@ -60,8 +60,6 @@ export function runAccountPickerConflicts(options) {
60
60
  conflicts.push('--lease');
61
61
  if (options.box)
62
62
  conflicts.push('--box');
63
- if (hostTargetGiven(options).length)
64
- conflicts.push('--device');
65
63
  return conflicts;
66
64
  }
67
65
  /** Type guard that narrows a string to a known AgentId. */
@@ -845,7 +843,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
845
843
  const conflicts = runAccountPickerConflicts(options);
846
844
  if (conflicts.length > 0) {
847
845
  console.error(chalk.red(`Account selection with ${agentSpec} cannot be combined with ${conflicts.join(', ')}. ` +
848
- 'Pick the account locally, or use an explicit agent@version target.'));
846
+ 'Remove the conflicting selector, or use an explicit agent@version target.'));
849
847
  process.exit(1);
850
848
  }
851
849
  }
@@ -1450,6 +1448,11 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
1450
1448
  process.exit(1);
1451
1449
  }
1452
1450
  const interactiveHost = options.interactive === true || (prompt === undefined && options.headless !== true);
1451
+ if (accountPickerRequested && !interactiveHost) {
1452
+ console.error(chalk.red(`Account selection with ${agentSpec} requires an interactive host run. ` +
1453
+ `Use agents run ${runAgent}@ --device ${host.name} --interactive.`));
1454
+ process.exit(1);
1455
+ }
1453
1456
  if (interactiveHost) {
1454
1457
  // Interactive host run: forward the local TTY over SSH and let the
1455
1458
  // remote agent start its normal interactive UI (tmux on the host).
@@ -1496,6 +1499,7 @@ agents run auto --device yosemite-s0 "fix the flaky test" # pin the device
1496
1499
  const exitCode = await runInteractiveOnHost(host, {
1497
1500
  agent: runAgent,
1498
1501
  version: resumeId ? undefined : runVersion,
1502
+ accountPicker: accountPickerRequested,
1499
1503
  strategy: resumeId ? undefined : runStrategy,
1500
1504
  account: resumeId ? undefined : options.account,
1501
1505
  fallback: options.fallback,
@@ -2,7 +2,7 @@
2
2
  * Software Factory CLI — submits Linear issues to a remote orchestrator.
3
3
  *
4
4
  * Requires FACTORY_FLOOR_URL pointing at a Factory-compatible endpoint.
5
- * Beta-gated; enable with `agents beta enable factory`.
5
+ * Beta-gated; enable with `agents setup beta enable factory`.
6
6
  */
7
7
  import type { Command } from 'commander';
8
8
  export declare function registerFactoryCommands(program: Command): void;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `agents fleet capture` (alias surface of `agents devices capture`) — snapshot
3
3
  * the live environment into the `fleet:` block of `agents.yaml` so a fresh
4
- * machine can reconstruct it with `agents apply`.
4
+ * machine can reconstruct it with `agents fleet apply`.
5
5
  *
6
6
  * Local-read + local-write, zero SSH. Records device NAMES only (never IPs or
7
7
  * usernames — those are re-resolved live from Tailscale at apply-time), plus the
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * `agents fleet capture` (alias surface of `agents devices capture`) — snapshot
3
3
  * the live environment into the `fleet:` block of `agents.yaml` so a fresh
4
- * machine can reconstruct it with `agents apply`.
4
+ * machine can reconstruct it with `agents fleet apply`.
5
5
  *
6
6
  * Local-read + local-write, zero SSH. Records device NAMES only (never IPs or
7
7
  * usernames — those are re-resolved live from Tailscale at apply-time), plus the
@@ -12,7 +12,7 @@
12
12
  import * as fs from 'fs';
13
13
  import chalk from 'chalk';
14
14
  import * as yaml from 'yaml';
15
- import { loadDevices, isControlDevice } from '../lib/devices/registry.js';
15
+ import { loadDevices } from '../lib/devices/registry.js';
16
16
  import { readMeta, updateMeta, getDevicePinsPath } from '../lib/state.js';
17
17
  import { machineId } from '../lib/machine-id.js';
18
18
  import { listBundles } from '../lib/secrets/bundles.js';
@@ -39,10 +39,9 @@ function agentsFromPins(names) {
39
39
  }
40
40
  async function runCapture(opts) {
41
41
  const meta = readMeta();
42
- // Roster: registered, non-control device names only.
42
+ // Roster: every registered device name.
43
43
  const registry = await loadDevices();
44
44
  let names = Object.values(registry)
45
- .filter((d) => !isControlDevice(d))
46
45
  .map((d) => d.name)
47
46
  .sort();
48
47
  if (opts.device) {
@@ -83,7 +82,7 @@ async function runCapture(opts) {
83
82
  console.log(chalk.green('Captured fleet profile') +
84
83
  chalk.gray(` — ${deviceCount} device(s), ${defaults.agents?.length ?? 0} agent(s), ` +
85
84
  `${inputs.secretsBundles?.length ?? 0} secret bundle(s), ${inputs.routines?.length ?? 0} routine(s).`));
86
- console.log(chalk.gray(' Wrote agents.yaml → fleet:. Push it (`agents repo push`) and run `agents apply` on any machine.'));
85
+ console.log(chalk.gray(' Wrote agents.yaml → fleet:. Push it (`agents repo push`) and run `agents fleet apply` on any machine.'));
87
86
  }
88
87
  /** Attach `capture` to the `devices`/`fleet` command tree. */
89
88
  export function registerFleetCaptureCommand(devicesCmd) {
@@ -17,8 +17,6 @@ import { listProfiles, readProfile, writeProfile, deleteProfile, profileExists,
17
17
  import { listPresets, getPreset } from '../lib/profiles-presets.js';
18
18
  import { AGENTS, ALL_AGENT_IDS, resolveAgentName } from '../lib/agents.js';
19
19
  import { findAccount } from '../lib/account-registry.js';
20
- import { keychainItemName, setKeychainToken, deleteKeychainToken } from '../lib/secrets/profiles.js';
21
- import { readStdinSync } from '../lib/format.js';
22
20
  import { runWizardSteps, createSteps, editSteps, defaultWizardIO, } from './harness-wizard.js';
23
21
  /** Short capability summary for a native harness — its supported run modes. */
24
22
  function nativeModes(id) {
@@ -567,7 +565,7 @@ Examples:
567
565
  cmd
568
566
  .command('remove <name>')
569
567
  .alias('rm')
570
- .description('Delete a custom harness (keychain token is kept use `harness logout <provider>` to remove).')
568
+ .description('Delete a custom harness (credentials stay on `agents accounts`; this only drops the named pin).')
571
569
  .action((name) => {
572
570
  const existed = deleteProfile(name);
573
571
  if (!existed) {
@@ -576,50 +574,4 @@ Examples:
576
574
  }
577
575
  console.log(chalk.green(`Harness '${name}' removed.`));
578
576
  });
579
- cmd
580
- .command('login <provider>')
581
- .description('Store or rotate the API key for a provider (e.g., openrouter). Shared across harnesses using that provider.')
582
- .option('--key-stdin', 'Read API key from stdin')
583
- .action(async (provider, opts) => {
584
- try {
585
- const item = keychainItemName(provider);
586
- let token;
587
- if (opts.keyStdin) {
588
- token = readStdinSync();
589
- if (!token)
590
- throw new Error('No key received on stdin.');
591
- }
592
- else {
593
- if (!isInteractiveTerminal()) {
594
- throw new Error('A secret is required but the shell is not interactive. Pipe the key via stdin (--key-stdin).');
595
- }
596
- const { password } = await import('@inquirer/prompts');
597
- token = await password({ message: `Enter API key for ${provider}`, mask: true });
598
- }
599
- setKeychainToken(item, token);
600
- console.log(chalk.green(`Stored in keychain: ${item}`));
601
- }
602
- catch (err) {
603
- console.error(chalk.red(err.message));
604
- process.exit(1);
605
- }
606
- });
607
- cmd
608
- .command('logout <provider>')
609
- .description('Remove a stored provider key from keychain')
610
- .action((provider) => {
611
- try {
612
- const item = keychainItemName(provider);
613
- const existed = deleteKeychainToken(item);
614
- if (!existed) {
615
- console.error(chalk.yellow(`No keychain item '${item}' to remove.`));
616
- process.exit(1);
617
- }
618
- console.log(chalk.green(`Removed keychain item: ${item}`));
619
- }
620
- catch (err) {
621
- console.error(chalk.red(err.message));
622
- process.exit(1);
623
- }
624
- });
625
577
  }
@@ -514,7 +514,7 @@ Examples:
514
514
  }
515
515
  else {
516
516
  console.log(chalk.green(`\nRemoved ${removed} hook(s) from version homes.`));
517
- console.log(chalk.gray('Central source unchanged. Hooks will re-sync on next agent launch.'));
517
+ console.log(chalk.gray("Central source unchanged. Restore any target with 'agents sync <agent>@<version> --yes'."));
518
518
  }
519
519
  });
520
520
  // `hooks prune` moved to the top-level `agents prune cleanup` command.
@@ -31,7 +31,7 @@
31
31
  * the coaching prose by piping the AGGREGATE (never raw transcripts) through a headless
32
32
  * `claude -p`.
33
33
  *
34
- * `agents trends` is a thin deprecated alias of the mix tree only (see commands/trends.ts).
34
+ * Former top-level `agents trends` is `agents insights mix` / `agents insights trends`.
35
35
  */
36
36
  import type { Command } from 'commander';
37
37
  export declare function registerInsightsCommand(program: Command): void;
@@ -31,7 +31,7 @@
31
31
  * the coaching prose by piping the AGGREGATE (never raw transcripts) through a headless
32
32
  * `claude -p`.
33
33
  *
34
- * `agents trends` is a thin deprecated alias of the mix tree only (see commands/trends.ts).
34
+ * Former top-level `agents trends` is `agents insights mix` / `agents insights trends`.
35
35
  */
36
36
  import * as fs from 'fs';
37
37
  import chalk from 'chalk';
@@ -651,7 +651,8 @@ function configureInsightsCommand(cmd) {
651
651
  \`agents insights mix\` — cheap counters from sessions.db + usage.db
652
652
  Latency is \`agents perf\` (not mix). Quota is \`agents usage\`. Spend is
653
653
  \`agents insights cost\`; shipped output is \`agents insights output\`. Skill/slash popularity
654
- is \`agents sessions stats\`. \`agents trends\` is a deprecated alias of the mix tree.
654
+ is \`agents sessions stats\`. Former top-level \`agents trends\` is \`agents insights mix\`
655
+ (also \`agents insights trends\`).
655
656
 
656
657
  The behavioural report parses in-scope transcripts once and caches facets; later runs
657
658
  re-read only files that changed. \`--refresh\` forces a full re-read.
@@ -8,7 +8,7 @@
8
8
  */
9
9
  import chalk from 'chalk';
10
10
  import { setHelpSections } from '../lib/help.js';
11
- import { enableMenubarService, disableMenubarService, getMenubarStatus, runMenubarSetup, } from '../lib/menubar/install-menubar.js';
11
+ import { enableMenubarService, disableMenubarService, getMenubarStatus, runMenubarSetup, buildMenubarDoctorReport, } from '../lib/menubar/install-menubar.js';
12
12
  function notMac() {
13
13
  if (process.platform !== 'darwin') {
14
14
  console.log(chalk.yellow('AGI Menu is macOS only.'));
@@ -77,6 +77,32 @@ function printSetupResult(r) {
77
77
  console.log(chalk.red('AGI Menu not fully configured.') + chalk.gray(' See the failed step above.'));
78
78
  }
79
79
  }
80
+ function printDoctorReport(r) {
81
+ console.log(chalk.bold('AGI Menu doctor\n'));
82
+ console.log(` install path ${r.installPath ? chalk.gray(r.installPath) : chalk.red('not installed')}`);
83
+ console.log(` installed version ${r.installedVersion ? chalk.gray(r.installedVersion) : chalk.gray('unknown')}`);
84
+ console.log(` CLI version ${chalk.gray(r.currentVersion)}${r.versionMatches ? '' : chalk.yellow(' (mismatch — `agents menubar setup` updates it)')}`);
85
+ const identity = r.signingIdentity === 'developer-id'
86
+ ? chalk.green('Developer ID (update-stable)')
87
+ : r.signingIdentity === 'ad-hoc'
88
+ ? chalk.red('ad-hoc (unstable — Accessibility grant breaks on every update)')
89
+ : chalk.gray('unknown (nothing installed)');
90
+ console.log(` signing identity ${identity}`);
91
+ console.log(` running ${r.running ? chalk.green('yes') : chalk.gray('no')}`);
92
+ if (r.staleRunningProcess.length > 0) {
93
+ for (const p of r.staleRunningProcess) {
94
+ const mark = p.stale
95
+ ? chalk.red('started BEFORE the on-disk bundle — running the OLD binary')
96
+ : chalk.green('matches the on-disk bundle');
97
+ console.log(` pid ${String(p.pid).padEnd(10)} ${mark}`);
98
+ }
99
+ }
100
+ if (r.accessibilityHintNeeded) {
101
+ console.log(chalk.yellow('\n Accessibility grant may not be trusted for the current binary.'));
102
+ console.log(chalk.gray(' Run `agents menubar setup` to reinstall + restart the helper, then grant access:'));
103
+ console.log(chalk.gray(' open "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility"'));
104
+ }
105
+ }
80
106
  export function registerMenubarCommands(program) {
81
107
  const menubar = program
82
108
  .command('menubar')
@@ -184,6 +210,37 @@ export function registerMenubarCommands(program) {
184
210
  }
185
211
  printStatus(s);
186
212
  });
213
+ const doctor = menubar
214
+ .command('doctor')
215
+ .description('Diagnose AGI Menu: install path, version skew, signing identity, stale process')
216
+ .option('--json', 'Emit machine-readable JSON')
217
+ .action((options) => {
218
+ const r = buildMenubarDoctorReport();
219
+ if (options.json) {
220
+ process.stdout.write(JSON.stringify(r) + '\n');
221
+ return;
222
+ }
223
+ if (r.platform !== 'darwin') {
224
+ console.log(chalk.yellow('AGI Menu is macOS only.'));
225
+ return;
226
+ }
227
+ printDoctorReport(r);
228
+ });
229
+ setHelpSections(doctor, {
230
+ examples: `
231
+ # Why did Accessibility ask again after an update?
232
+ agents menubar doctor
233
+ `,
234
+ notes: `
235
+ Read-only — never installs, restarts, or resets anything. Reports the
236
+ installed bundle's signing identity (ad-hoc signing breaks the
237
+ Accessibility grant on every update; Developer ID is stable across
238
+ updates) and whether a live helper pid started before the on-disk
239
+ bundle's last write — the sign that a running process is still the
240
+ binary an update just replaced. \`agents menubar setup\` is the fix for
241
+ anything this flags.
242
+ `,
243
+ });
187
244
  // Bare `agents menubar` -> status.
188
245
  menubar.action(() => {
189
246
  const s = getMenubarStatus();
@@ -3,4 +3,9 @@ import type { Command } from 'commander';
3
3
  export declare function resolveInviteRole(raw: string | undefined): 'admin' | 'member';
4
4
  /** Validate a role argument before any network call — same rule the backend enforces server-side. */
5
5
  export declare function parseRole(raw: string): 'admin' | 'member';
6
+ /** One-line deprecation for the leftover top-level `agents org` spelling. */
7
+ export declare function printOrgDeprecation(): void;
8
+ /** Canonical home: `agents auth space …`. */
9
+ export declare function registerAuthSpaceCommand(auth: Command): void;
10
+ /** Leftover top-level spelling — same tree, deprecation on every path. */
6
11
  export declare function registerOrgCommand(program: Command): void;
@@ -41,7 +41,7 @@ async function runList(o) {
41
41
  return;
42
42
  }
43
43
  if (!spaces.length) {
44
- console.log(chalk.gray("No spaces. Create one with 'agents org create <name>'."));
44
+ console.log(chalk.gray("No spaces. Create one with 'agents auth space create <name>'."));
45
45
  return;
46
46
  }
47
47
  for (const space of spaces)
@@ -133,43 +133,96 @@ async function runLeave(spaceArg, o) {
133
133
  }
134
134
  console.log(chalk.green(`Left '${space.name}'.`));
135
135
  }
136
- export function registerOrgCommand(program) {
137
- const org = program.command('org').description('Create and manage a team (a Rush "space") you can share with `agents auth login` collaborators');
138
- org.command('create <name>').description('Create a space (free tier: 1 owned space)')
136
+ /** One-line deprecation for the leftover top-level `agents org` spelling. */
137
+ export function printOrgDeprecation() {
138
+ console.error(chalk.yellow('agents org is deprecated use `agents auth space`.'));
139
+ }
140
+ /** Shared space CRUD tree — canonical under `auth space`, leftover under `org`. */
141
+ function attachSpaceCommands(parent, opts) {
142
+ const before = () => {
143
+ if (opts.deprecated)
144
+ printOrgDeprecation();
145
+ };
146
+ const jsonFlag = (o, command) => !!(o.json || command.optsWithGlobals().json);
147
+ parent.command('create <name>').description('Create a space (free tier: 1 owned space)')
139
148
  .option('--slug <slug>', 'Override the derived slug')
140
149
  .option('--description <text>', 'Optional description')
141
150
  .option('--json', 'Machine-readable output')
142
- .action((name, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runCreate(name, { ...o, json }), { json }); });
143
- org.command('list').description('List spaces you own or belong to').option('--json', 'Machine-readable output')
144
- .action((o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runList({ json }), { json }); });
145
- org.command('view [space]').description('Show one space (defaults to your only space)').option('--json', 'Machine-readable output')
146
- .action((space, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runView(space, { json }), { json }); });
147
- org.command('invite <email>').description('Invite (or directly add) a member')
151
+ .action((name, o, command) => {
152
+ before();
153
+ const json = jsonFlag(o, command);
154
+ return runOrDie(() => runCreate(name, { ...o, json }), { json });
155
+ });
156
+ parent.command('list').description('List spaces you own or belong to').option('--json', 'Machine-readable output')
157
+ .action((o, command) => {
158
+ before();
159
+ const json = jsonFlag(o, command);
160
+ return runOrDie(() => runList({ json }), { json });
161
+ });
162
+ parent.command('view [space]').description('Show one space (defaults to your only space)').option('--json', 'Machine-readable output')
163
+ .action((space, o, command) => {
164
+ before();
165
+ const json = jsonFlag(o, command);
166
+ return runOrDie(() => runView(space, { json }), { json });
167
+ });
168
+ parent.command('invite <email>').description('Invite (or directly add) a member')
148
169
  .option('--role <role>', 'admin | member', 'member')
149
170
  .option('--space <id-or-slug>', 'Space to invite into (defaults to your only space)')
150
171
  .option('--json', 'Machine-readable output')
151
- .action((email, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runInvite(email, { ...o, json }), { json }); });
152
- org.command('members [space]').description('List a space\'s members').option('--json', 'Machine-readable output')
153
- .action((space, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runMembers(space, { json }), { json }); });
154
- org.command('role <email> <role>').description('Change a member\'s role (owner-only for admin)')
172
+ .action((email, o, command) => {
173
+ before();
174
+ const json = jsonFlag(o, command);
175
+ return runOrDie(() => runInvite(email, { ...o, json }), { json });
176
+ });
177
+ parent.command('members [space]').description("List a space's members").option('--json', 'Machine-readable output')
178
+ .action((space, o, command) => {
179
+ before();
180
+ const json = jsonFlag(o, command);
181
+ return runOrDie(() => runMembers(space, { json }), { json });
182
+ });
183
+ parent.command('role <email> <role>').description("Change a member's role (owner-only for admin)")
155
184
  .option('--space <id-or-slug>', 'Space to change (defaults to your only space)')
156
185
  .option('--json', 'Machine-readable output')
157
- .action((email, role, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runRole(email, role, { ...o, json }), { json }); });
158
- org.command('remove <email>').description('Remove a member from a space')
186
+ .action((email, role, o, command) => {
187
+ before();
188
+ const json = jsonFlag(o, command);
189
+ return runOrDie(() => runRole(email, role, { ...o, json }), { json });
190
+ });
191
+ parent.command('remove <email>').description('Remove a member from a space')
159
192
  .option('--space <id-or-slug>', 'Space to remove from (defaults to your only space)')
160
193
  .option('--json', 'Machine-readable output')
161
- .action((email, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runRemove(email, { ...o, json }), { json }); });
162
- org.command('leave [space]').description('Leave a space you do not own').option('--json', 'Machine-readable output')
163
- .action((space, o, command) => { const json = !!(o.json || command.optsWithGlobals().json); return runOrDie(() => runLeave(space, { json }), { json }); });
164
- setHelpSections(org, {
165
- examples: `agents org create acme-team
166
- agents org list
167
- agents org view acme-team
168
- agents org invite dev@example.com --role admin
169
- agents org members
170
- agents org role dev@example.com admin
171
- agents org remove dev@example.com
172
- agents org leave acme-team`,
173
- notes: 'Maps to the Rush backend\'s /api/v1/spaces (the free-tier team primitive), not /api/v1/orgs. Free tier: 1 owned space, 3 members per space. Every command needs `agents auth login` (or a `rush login` session) first. `--space` is only needed once you belong to more than one space.',
194
+ .action((email, o, command) => {
195
+ before();
196
+ const json = jsonFlag(o, command);
197
+ return runOrDie(() => runRemove(email, { ...o, json }), { json });
198
+ });
199
+ parent.command('leave [space]').description('Leave a space you do not own').option('--json', 'Machine-readable output')
200
+ .action((space, o, command) => {
201
+ before();
202
+ const json = jsonFlag(o, command);
203
+ return runOrDie(() => runLeave(space, { json }), { json });
204
+ });
205
+ setHelpSections(parent, {
206
+ examples: `${opts.prefix} create acme-team
207
+ ${opts.prefix} list
208
+ ${opts.prefix} view acme-team
209
+ ${opts.prefix} invite dev@example.com --role admin
210
+ ${opts.prefix} members
211
+ ${opts.prefix} role dev@example.com admin
212
+ ${opts.prefix} remove dev@example.com
213
+ ${opts.prefix} leave acme-team`,
214
+ notes: opts.deprecated
215
+ ? 'Deprecated alias of `agents auth space`. Maps to the Rush backend\'s /api/v1/spaces (the free-tier team primitive), not /api/v1/orgs. Free tier: 1 owned space, 3 members per space. Every command needs `agents auth login` (or a `rush login` session) first. `--space` is only needed once you belong to more than one space.'
216
+ : 'Maps to the Rush backend\'s /api/v1/spaces (the free-tier team primitive), not /api/v1/orgs. Free tier: 1 owned space, 3 members per space. Every command needs `agents auth login` (or a `rush login` session) first. `--space` is only needed once you belong to more than one space. The leftover `agents org` spelling still works and prints a deprecation line.',
174
217
  });
175
218
  }
219
+ /** Canonical home: `agents auth space …`. */
220
+ export function registerAuthSpaceCommand(auth) {
221
+ const space = auth.command('space').description('Create and manage a Prix space (invite collaborators)');
222
+ attachSpaceCommands(space, { prefix: 'agents auth space' });
223
+ }
224
+ /** Leftover top-level spelling — same tree, deprecation on every path. */
225
+ export function registerOrgCommand(program) {
226
+ const org = program.command('org').description('Deprecated alias of `agents auth space` — create and manage a Prix space');
227
+ attachSpaceCommands(org, { prefix: 'agents org', deprecated: true });
228
+ }
@@ -10,7 +10,7 @@ import { formatUsd, PRICING_VERSION } from '../lib/pricing/index.js';
10
10
  import { formatDuration } from '../lib/session/render.js';
11
11
  import { terminalWidth, truncateToWidth, padToWidth } from '../lib/session/width.js';
12
12
  import { collectGitOutput } from '../lib/output/git-output.js';
13
- import { loadDevices, isControlDevice } from '../lib/devices/registry.js';
13
+ import { loadDevices } from '../lib/devices/registry.js';
14
14
  import { machineId } from '../lib/session/sync/config.js';
15
15
  import { stripClixml } from '../lib/hosts/remote-cmd.js';
16
16
  const execFileAsync = promisify(execFile);
@@ -189,7 +189,7 @@ async function outputAction(options) {
189
189
  const self = machineId();
190
190
  const registry = await loadDevices();
191
191
  const remotes = Object.values(registry)
192
- .filter(d => d.tailscale?.online && d.name !== self && !isControlDevice(d))
192
+ .filter(d => d.tailscale?.online && d.name !== self)
193
193
  .map(d => d.name);
194
194
  if (!options.json)
195
195
  console.error(chalk.gray(`Folding in ${remotes.length} online device${remotes.length !== 1 ? 's' : ''}…`));
@@ -12,10 +12,11 @@
12
12
  * agents-cli.
13
13
  * - Runs: routine execution logs, keeping only the last N per job.
14
14
  *
15
- * Sync (additive: copy missing/changed files into version homes) is no longer
16
- * a user-facing verb `syncResourcesToVersion` runs at agent launch and
17
- * applies adds/updates automatically. Pruning, however, is destructive, so it
18
- * stays explicit.
15
+ * Sync (additive: copy missing/changed files into version homes) is the job
16
+ * of `agents sync` / `agents refresh` the shim's launch hook
17
+ * (`agents sync --launch`) is project-scoped only and deliberately skips
18
+ * version-home reconciliation. Pruning, however, is destructive, so it stays
19
+ * explicit.
19
20
  *
20
21
  * Default scope: each agent's currently-pinned default version for orphan
21
22
  * cleanup, plus the standard cross-agent version-dedup pass. Pass `--all`