@phnx-labs/agents-cli 1.22.37 → 1.22.39

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 (65) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +8 -8
  3. package/dist/bin/agents +0 -0
  4. package/dist/bootstrap.js +3 -2
  5. package/dist/commands/artifacts-setup.d.ts +53 -0
  6. package/dist/commands/{setup-share.js → artifacts-setup.js} +59 -13
  7. package/dist/commands/artifacts.d.ts +18 -0
  8. package/dist/commands/artifacts.js +58 -0
  9. package/dist/commands/browser.js +2 -0
  10. package/dist/commands/config.js +31 -1
  11. package/dist/commands/exec.js +2 -2
  12. package/dist/commands/models.js +67 -0
  13. package/dist/commands/setup.js +5 -5
  14. package/dist/commands/share.d.ts +20 -7
  15. package/dist/commands/share.js +74 -75
  16. package/dist/commands/ssh.js +156 -8
  17. package/dist/lib/browser/hygiene.d.ts +90 -0
  18. package/dist/lib/browser/hygiene.js +146 -0
  19. package/dist/lib/browser/ipc.js +12 -0
  20. package/dist/lib/browser/service.d.ts +75 -1
  21. package/dist/lib/browser/service.js +201 -11
  22. package/dist/lib/browser/types.d.ts +44 -1
  23. package/dist/lib/config-keys.d.ts +11 -3
  24. package/dist/lib/config-keys.js +22 -3
  25. package/dist/lib/config-machine-keys.js +1 -0
  26. package/dist/lib/device-config.d.ts +34 -0
  27. package/dist/lib/device-config.js +96 -0
  28. package/dist/lib/devices/pool.d.ts +56 -0
  29. package/dist/lib/devices/pool.js +85 -0
  30. package/dist/lib/exec.d.ts +4 -1
  31. package/dist/lib/exec.js +8 -2
  32. package/dist/lib/git.d.ts +1 -1
  33. package/dist/lib/git.js +1 -1
  34. package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
  35. package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
  36. package/dist/lib/migrate.d.ts +36 -0
  37. package/dist/lib/migrate.js +107 -0
  38. package/dist/lib/routines.d.ts +3 -1
  39. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  40. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  41. package/dist/lib/share/analytics.js +1 -1
  42. package/dist/lib/share/capture.d.ts +1 -1
  43. package/dist/lib/share/capture.js +3 -3
  44. package/dist/lib/share/config.d.ts +2 -2
  45. package/dist/lib/share/config.js +5 -5
  46. package/dist/lib/share/delete.js +3 -3
  47. package/dist/lib/share/provision.js +3 -3
  48. package/dist/lib/share/publish.d.ts +1 -1
  49. package/dist/lib/share/publish.js +3 -3
  50. package/dist/lib/share/worker-template.d.ts +12 -1
  51. package/dist/lib/share/worker-template.js +13 -2
  52. package/dist/lib/smart-launch.d.ts +33 -3
  53. package/dist/lib/smart-launch.js +61 -6
  54. package/dist/lib/startup/command-registry.d.ts +10 -2
  55. package/dist/lib/startup/command-registry.js +16 -7
  56. package/dist/lib/tmux/orphan-reap.d.ts +15 -19
  57. package/dist/lib/tmux/orphan-reap.js +15 -21
  58. package/dist/lib/tmux/session.js +4 -3
  59. package/dist/lib/triggers/handlers.js +10 -0
  60. package/dist/lib/triggers/webhook.js +10 -0
  61. package/dist/lib/types.d.ts +2 -2
  62. package/package.json +1 -1
  63. package/dist/commands/set.d.ts +0 -15
  64. package/dist/commands/set.js +0 -79
  65. package/dist/commands/setup-share.d.ts +0 -17
@@ -50,7 +50,6 @@ export declare const loadOpen: ModuleLoader;
50
50
  export declare const loadReconnect: ModuleLoader;
51
51
  export declare const loadFork: ModuleLoader;
52
52
  export declare const loadConfig: ModuleLoader;
53
- export declare const loadSet: ModuleLoader;
54
53
  export declare const loadModels: ModuleLoader;
55
54
  export declare const loadModes: ModuleLoader;
56
55
  export declare const loadPrune: ModuleLoader;
@@ -100,7 +99,7 @@ export declare const loadSend: ModuleLoader;
100
99
  export declare const loadFeed: ModuleLoader;
101
100
  export declare const loadMailboxes: ModuleLoader;
102
101
  export declare const loadServe: ModuleLoader;
103
- export declare const loadShare: ModuleLoader;
102
+ export declare const loadArtifacts: ModuleLoader;
104
103
  export declare const loadAudit: ModuleLoader;
105
104
  export declare const loadWebhooks: ModuleLoader;
106
105
  export declare const loadHumans: ModuleLoader;
@@ -143,6 +142,15 @@ export declare const COMMAND_LOADERS: Record<string, ModuleLoader[]>;
143
142
  * registered command tree so a new command can never drift out of it.
144
143
  */
145
144
  export declare const KNOWN_TOP_LEVEL_COMMANDS: ReadonlySet<string>;
145
+ /**
146
+ * Former top-level names that must NOT auto-correct (edit-distance 1) into a
147
+ * live command. Without this a pruned surface silently misroutes: the typed
148
+ * name is gone, the spellchecker finds a neighbour, and the CLI runs something
149
+ * the user never asked for instead of saying the command is gone.
150
+ *
151
+ * `set` moved under `agents models`/`agents config` (RUSH-2579); `share` moved
152
+ * under `agents artifacts share` (RUSH-2580).
153
+ */
146
154
  export declare const RETIRED_TOP_LEVEL_COMMANDS: ReadonlySet<string>;
147
155
  /** Whether `name` is a top-level command this CLI registers. See {@link KNOWN_TOP_LEVEL_COMMANDS}. */
148
156
  export declare function isKnownTopLevelCommand(name: string): boolean;
@@ -52,7 +52,6 @@ export const loadOpen = async () => (await import('../../commands/open.js')).reg
52
52
  export const loadReconnect = async () => (await import('../../commands/reconnect.js')).registerReconnectCommand;
53
53
  export const loadFork = async () => (await import('../../commands/fork.js')).registerForkCommand;
54
54
  export const loadConfig = async () => (await import('../../commands/config.js')).registerConfigCommand;
55
- export const loadSet = async () => (await import('../../commands/set.js')).registerSetCommand;
56
55
  export const loadModels = async () => (await import('../../commands/models.js')).registerModelsCommand;
57
56
  export const loadModes = async () => (await import('../../commands/modes.js')).registerModesCommand;
58
57
  export const loadPrune = async () => (await import('../../commands/prune.js')).registerPruneCommand;
@@ -103,7 +102,9 @@ export const loadSend = async () => (await import('../../commands/send.js')).reg
103
102
  export const loadFeed = async () => (await import('../../commands/feed.js')).registerFeedCommand;
104
103
  export const loadMailboxes = async () => (await import('../../commands/mailboxes.js')).registerMailboxesCommand;
105
104
  export const loadServe = async () => (await import('../../commands/serve.js')).registerServeCommand;
106
- export const loadShare = async () => (await import('../../commands/share.js')).registerShareCommands;
105
+ // Registers the `artifacts` group (with `share` + `setup` under it) AND the
106
+ // top-level `unshare` alias — see commands/artifacts.ts.
107
+ export const loadArtifacts = async () => (await import('../../commands/artifacts.js')).registerArtifactsCommands;
107
108
  export const loadAudit = async () => (await import('../../commands/audit.js')).registerAuditCommands;
108
109
  export const loadWebhooks = async () => (await import('../../commands/webhook.js')).registerWebhooksCommand;
109
110
  export const loadHumans = async () => (await import('../../commands/humans.js')).registerHumansCommands;
@@ -180,7 +181,6 @@ export const COMMAND_LOADERS = {
180
181
  reconnect: [loadReconnect],
181
182
  fork: [loadFork],
182
183
  config: [loadConfig],
183
- set: [loadSet],
184
184
  models: [loadModels],
185
185
  modes: [loadModes],
186
186
  trash: [loadTrash],
@@ -246,10 +246,10 @@ export const COMMAND_LOADERS = {
246
246
  mailboxes: [loadMailboxes],
247
247
  mailbox: [loadMailboxes],
248
248
  serve: [loadServe],
249
- share: [loadShare],
250
- // `unshare` is a top-level convenience alias of `share delete` (see
249
+ artifacts: [loadArtifacts],
250
+ // `unshare` is a top-level convenience alias of `artifacts share delete` (see
251
251
  // commands/share.ts) — same module, registered as its own program.command().
252
- unshare: [loadShare],
252
+ unshare: [loadArtifacts],
253
253
  audit: [loadAudit],
254
254
  webhooks: [loadWebhooks],
255
255
  humans: [loadHumans],
@@ -288,7 +288,16 @@ export const KNOWN_TOP_LEVEL_COMMANDS = new Set([
288
288
  ...Object.keys(COMMAND_LOADERS),
289
289
  ...INLINE_COMMAND_NAMES,
290
290
  ]);
291
- export const RETIRED_TOP_LEVEL_COMMANDS = new Set(['webhook']);
291
+ /**
292
+ * Former top-level names that must NOT auto-correct (edit-distance 1) into a
293
+ * live command. Without this a pruned surface silently misroutes: the typed
294
+ * name is gone, the spellchecker finds a neighbour, and the CLI runs something
295
+ * the user never asked for instead of saying the command is gone.
296
+ *
297
+ * `set` moved under `agents models`/`agents config` (RUSH-2579); `share` moved
298
+ * under `agents artifacts share` (RUSH-2580).
299
+ */
300
+ export const RETIRED_TOP_LEVEL_COMMANDS = new Set(['webhook', 'set', 'share']);
292
301
  /** Whether `name` is a top-level command this CLI registers. See {@link KNOWN_TOP_LEVEL_COMMANDS}. */
293
302
  export function isKnownTopLevelCommand(name) {
294
303
  return KNOWN_TOP_LEVEL_COMMANDS.has(name);
@@ -58,12 +58,10 @@
58
58
  * - the reaping process itself, its ancestors, pid 1, and every long-lived
59
59
  * agents-cli service ({@link isProtectedAgentsService}) are never candidates.
60
60
  * - an UNRELIABLE read of tmux's session state (the query threw, timed out, or
61
- * tmux itself never answered) disables tier 1 for that sweep entirely — an
62
- * absent map entry proves a session is gone only when the map is known-good.
63
- * Distinguishing "tmux answered: no such session" (a completed, if nonzero,
64
- * exit genuinely nothing there) from "tmux did not answer" (a thrown
65
- * error/timeout — unknown) is exactly this: a completed process is a real
66
- * answer, a rejected promise is not one at all.
61
+ * tmux itself never answered) disables tier 1 for that sweep entirely;
62
+ * - an absent session entry is unknown, even after a reliable read. A tmux
63
+ * server restart can produce the same empty snapshot while marked agents
64
+ * remain alive, so tier 1 requires a present owner with positive death proof.
67
65
  * - tier 2 is anchored on the actual claude EXECUTABLE (argv[0]'s basename),
68
66
  * never a substring match anywhere in the command line, and excludes any
69
67
  * process still structurally part of a LIVE pane leaf's process tree right
@@ -121,7 +119,7 @@ export interface PaneOwner {
121
119
  attached: boolean;
122
120
  }
123
121
  /** Why a process was selected, for human output and tests. */
124
- export type OrphanReason = 'tmux-session-gone' | 'tmux-agent-exited' | 'detached-helper';
122
+ export type OrphanReason = 'tmux-agent-exited' | 'detached-helper';
125
123
  export interface OrphanCandidate {
126
124
  pid: number;
127
125
  args: string;
@@ -170,16 +168,14 @@ export declare function isProtectedAgentsService(args: string): boolean;
170
168
  /**
171
169
  * Pure. Select the processes whose owning agent is provably gone.
172
170
  *
173
- * `owners` maps a tmux session name to its liveness; a name ABSENT from the map
174
- * is a session tmux no longer has, which is the strongest orphan signal there is
175
- * but ONLY when `owners` is itself known-complete. `opts.ownersReliable`
176
- * (default `true`, so existing direct callers/tests keep their prior meaning)
177
- * says whether the caller actually got a real answer from tmux for every
178
- * socket it queried. When it is `false` a query threw, timed out, or tmux
179
- * itself never answered tier 1 is skipped entirely for this call: an absent
180
- * map entry proves nothing when the map itself might be missing entries for
181
- * sessions tmux never got asked about (RUSH-2521 review — a flaky tick must
182
- * never fall back to "treat every marked process as orphaned").
171
+ * `owners` maps a tmux session name to its liveness. An ABSENT name is never
172
+ * proof that the marked process is orphaned: the tmux server can restart or its
173
+ * socket can disappear while the pane's process tree is still alive. That exact
174
+ * state made the daemon classify every live agent as `tmux-session-gone` and
175
+ * SIGTERM it on startup (RUSH-2603). Tier 1 therefore requires a PRESENT owner
176
+ * whose pane process is confirmed dead and has no attached client. A reliable
177
+ * empty map means "nothing proven dead", not "every marked process is dead".
178
+ * `opts.ownersReliable` still disables tier 1 when any query failed to answer.
183
179
  */
184
180
  export declare function selectOrphanProcesses(procs: AgentProcess[], owners: Map<string, PaneOwner>, opts: {
185
181
  protectedPids: Set<number>;
@@ -281,8 +277,8 @@ export declare function readPaneOwners(socket: string): Promise<PaneOwnersRead>;
281
277
  * Reap every helper process whose owning agent has exited.
282
278
  *
283
279
  * Called from the daemon's periodic tick and from `agents sessions reap`. Panes
284
- * are read BEFORE processes so a session that disappears mid-scan is treated as
285
- * gone (reapable) rather than as a live owner.
280
+ * are read before processes to take one ownership snapshot. A session absent
281
+ * from that snapshot is unknown and is never sufficient proof for tier 1.
286
282
  *
287
283
  * `opts.pids` is the test-only process-table scope described on
288
284
  * {@link readAgentProcesses} — never set by production callers.
@@ -58,12 +58,10 @@
58
58
  * - the reaping process itself, its ancestors, pid 1, and every long-lived
59
59
  * agents-cli service ({@link isProtectedAgentsService}) are never candidates.
60
60
  * - an UNRELIABLE read of tmux's session state (the query threw, timed out, or
61
- * tmux itself never answered) disables tier 1 for that sweep entirely — an
62
- * absent map entry proves a session is gone only when the map is known-good.
63
- * Distinguishing "tmux answered: no such session" (a completed, if nonzero,
64
- * exit genuinely nothing there) from "tmux did not answer" (a thrown
65
- * error/timeout — unknown) is exactly this: a completed process is a real
66
- * answer, a rejected promise is not one at all.
61
+ * tmux itself never answered) disables tier 1 for that sweep entirely;
62
+ * - an absent session entry is unknown, even after a reliable read. A tmux
63
+ * server restart can produce the same empty snapshot while marked agents
64
+ * remain alive, so tier 1 requires a present owner with positive death proof.
67
65
  * - tier 2 is anchored on the actual claude EXECUTABLE (argv[0]'s basename),
68
66
  * never a substring match anywhere in the command line, and excludes any
69
67
  * process still structurally part of a LIVE pane leaf's process tree right
@@ -193,16 +191,14 @@ function livePid(pid) {
193
191
  /**
194
192
  * Pure. Select the processes whose owning agent is provably gone.
195
193
  *
196
- * `owners` maps a tmux session name to its liveness; a name ABSENT from the map
197
- * is a session tmux no longer has, which is the strongest orphan signal there is
198
- * but ONLY when `owners` is itself known-complete. `opts.ownersReliable`
199
- * (default `true`, so existing direct callers/tests keep their prior meaning)
200
- * says whether the caller actually got a real answer from tmux for every
201
- * socket it queried. When it is `false` a query threw, timed out, or tmux
202
- * itself never answered tier 1 is skipped entirely for this call: an absent
203
- * map entry proves nothing when the map itself might be missing entries for
204
- * sessions tmux never got asked about (RUSH-2521 review — a flaky tick must
205
- * never fall back to "treat every marked process as orphaned").
194
+ * `owners` maps a tmux session name to its liveness. An ABSENT name is never
195
+ * proof that the marked process is orphaned: the tmux server can restart or its
196
+ * socket can disappear while the pane's process tree is still alive. That exact
197
+ * state made the daemon classify every live agent as `tmux-session-gone` and
198
+ * SIGTERM it on startup (RUSH-2603). Tier 1 therefore requires a PRESENT owner
199
+ * whose pane process is confirmed dead and has no attached client. A reliable
200
+ * empty map means "nothing proven dead", not "every marked process is dead".
201
+ * `opts.ownersReliable` still disables tier 1 when any query failed to answer.
206
202
  */
207
203
  export function selectOrphanProcesses(procs, owners, opts) {
208
204
  const isAlive = opts.isAlive ?? livePid;
@@ -256,10 +252,8 @@ export function selectOrphanProcesses(procs, owners, opts) {
256
252
  if (!p.tmuxSession || !eligible(p))
257
253
  continue;
258
254
  const owner = owners.get(p.tmuxSession);
259
- if (!owner) {
260
- push(p, 'tmux-session-gone');
255
+ if (!owner)
261
256
  continue;
262
- }
263
257
  // An attached client or a live agent pane process is a hard exclusion.
264
258
  if (owner.attached || owner.agentAlive)
265
259
  continue;
@@ -553,8 +547,8 @@ function describe(c) {
553
547
  * Reap every helper process whose owning agent has exited.
554
548
  *
555
549
  * Called from the daemon's periodic tick and from `agents sessions reap`. Panes
556
- * are read BEFORE processes so a session that disappears mid-scan is treated as
557
- * gone (reapable) rather than as a live owner.
550
+ * are read before processes to take one ownership snapshot. A session absent
551
+ * from that snapshot is unknown and is never sufficient proof for tier 1.
558
552
  *
559
553
  * `opts.pids` is the test-only process-table scope described on
560
554
  * {@link readAgentProcesses} — never set by production callers.
@@ -211,9 +211,10 @@ export async function killAll(socket) {
211
211
  export async function reapDeadTmuxPanes(socket, opts = {}) {
212
212
  const sock = socket ?? getDefaultSocketPath();
213
213
  const result = { reaped: 0, sessions: [], details: [], processes: 0, processDetails: [], warnings: [] };
214
- // The process sweep runs even with no server on this socket: a torn-down
215
- // server (`killAll` unlinks the socket) is the strongest orphan signal there
216
- // is, and skipping it here would strand exactly those leftovers forever.
214
+ // The process sweep may run with no server, but an absent session is not
215
+ // evidence that a still-live marked process is orphaned. Tier 1 only acts on
216
+ // a present pane owner confirmed dead; tier 2 independently verifies its
217
+ // declared spawner pid (RUSH-2603).
217
218
  // `opts.pids` is a test-only process-table scope (see `readAgentProcesses`)
218
219
  // — production callers never set it.
219
220
  const { reapOrphanAgentProcesses } = await import('./orphan-reap.js');
@@ -226,6 +226,16 @@ export function handlerMatchesWebhook(handler, webhook) {
226
226
  const current = data?.state?.name;
227
227
  if (current !== handler.stateTo)
228
228
  return false;
229
+ // RUSH-2539: `stateTo` is a TRANSITION predicate, not a current-state one.
230
+ // Linear carries the prior value of each changed field in `updatedFrom`, so a
231
+ // real state change has `updatedFrom.state` (this codebase's shape) or
232
+ // `updatedFrom.stateId` (Linear's scalar). With neither, this Issue/update
233
+ // touched something else (label, assignee, description) while the issue merely
234
+ // still sits in `stateTo` — matching there re-fires on every later edit
235
+ // (RUSH-1459 accumulated 11 duplicate plan comments).
236
+ const updatedTo = webhook.payload.updatedFrom;
237
+ if (!updatedTo || (updatedTo.state === undefined && updatedTo.stateId === undefined))
238
+ return false;
229
239
  }
230
240
  if (handler.stateFrom) {
231
241
  const updatedFrom = webhook.payload.updatedFrom;
@@ -155,6 +155,16 @@ function linearTriggerMatches(trigger, webhook) {
155
155
  const current = data?.state?.name;
156
156
  if (current !== trigger.stateTo)
157
157
  return false;
158
+ // RUSH-2539: `stateTo` is a TRANSITION predicate, not a current-state one.
159
+ // Linear carries the prior value of each changed field in `updatedFrom`, so a
160
+ // real state change has `updatedFrom.state` (this codebase's shape) or
161
+ // `updatedFrom.stateId` (Linear's scalar). With neither, this Issue/update
162
+ // touched something else while the issue merely still sits in `stateTo` —
163
+ // matching there re-fires on every later edit (RUSH-1459 got 11 duplicate
164
+ // plan comments).
165
+ const updatedTo = webhook.payload.updatedFrom;
166
+ if (!updatedTo || (updatedTo.state === undefined && updatedTo.stateId === undefined))
167
+ return false;
158
168
  }
159
169
  if (trigger.stateFrom) {
160
170
  const updatedFrom = webhook.payload.updatedFrom;
@@ -995,7 +995,7 @@ export interface Meta {
995
995
  * Full shape in `lib/fleet/types.ts` (FleetManifest).
996
996
  */
997
997
  fleet?: import('./fleet/types.js').FleetManifest;
998
- /** `agents share` endpoint (Cloudflare R2 + Worker). Set by `agents share
998
+ /** Artifact share endpoint (Cloudflare R2 + Worker). Set by `agents artifacts
999
999
  * setup`/`join`; syncs fleet-wide via `agents repo push/pull`. The write token
1000
1000
  * lives in the `share` secrets bundle, not here. */
1001
1001
  share?: {
@@ -1007,7 +1007,7 @@ export interface Meta {
1007
1007
  /** Cloudflare Web Analytics token injected into published HTML pages. */
1008
1008
  analyticsToken?: string;
1009
1009
  /** sha256 of the Worker script deployed at the last provision/update, so
1010
- * `agents share status` can tell current vs outdated vs unknown (a config
1010
+ * `agents artifacts share status` can tell current vs outdated vs unknown (a config
1011
1011
  * from before this field existed has no hash — always "unknown"). */
1012
1012
  templateHash?: string;
1013
1013
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@phnx-labs/agents-cli",
3
- "version": "1.22.37",
3
+ "version": "1.22.39",
4
4
  "description": "One CLI for all your AI coding agents - versions, config, cloud dispatch, sessions, and teams (now with first-class Grok Build CLI support)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,15 +0,0 @@
1
- /**
2
- * `agents set` — ergonomic top-level setter for per-agent/version run defaults.
3
- *
4
- * `agents set claude@2.1.220 --model opus-5` pins the default model (and/or
5
- * mode) that `agents run` uses for that agent version. It reads and writes the
6
- * same store as `agents config set run.<agent@version>.*` (agents.yaml -> run.defaults), so the
7
- * two stay consistent — `set` is just the short front door.
8
- *
9
- * agents set # list every configured default
10
- * agents set claude@2.1.220 # show the default for one version
11
- * agents set claude@2.1.220 --model opus-5
12
- * agents set 'claude:*' --mode auto --model opus
13
- */
14
- import type { Command } from 'commander';
15
- export declare function registerSetCommand(program: Command): void;
@@ -1,79 +0,0 @@
1
- /**
2
- * `agents set` — ergonomic top-level setter for per-agent/version run defaults.
3
- *
4
- * `agents set claude@2.1.220 --model opus-5` pins the default model (and/or
5
- * mode) that `agents run` uses for that agent version. It reads and writes the
6
- * same store as `agents config set run.<agent@version>.*` (agents.yaml -> run.defaults), so the
7
- * two stay consistent — `set` is just the short front door.
8
- *
9
- * agents set # list every configured default
10
- * agents set claude@2.1.220 # show the default for one version
11
- * agents set claude@2.1.220 --model opus-5
12
- * agents set 'claude:*' --mode auto --model opus
13
- */
14
- import chalk from 'chalk';
15
- import { setHelpSections } from '../lib/help.js';
16
- import { formatRunDefaultEntry, listRunDefaults, parseRunDefaultSelector, setRunDefault, } from '../lib/run-defaults.js';
17
- export function registerSetCommand(program) {
18
- const set = program
19
- .command('set [selector]')
20
- .description('Set the default model/mode an agent version uses for `agents run`')
21
- .option('--model <model>', 'Default model or model alias, forwarded via --model')
22
- .option('--mode <mode>', "Default mode: plan, edit, auto, skip. 'full' accepted as alias for skip.")
23
- .action((selector, options) => {
24
- try {
25
- const hasFlags = options.model !== undefined || options.mode !== undefined;
26
- if (!selector) {
27
- if (hasFlags) {
28
- throw new Error('Selector is required when passing --model/--mode. Example: agents set claude@2.1.220 --model opus-5');
29
- }
30
- const entries = listRunDefaults();
31
- if (entries.length === 0) {
32
- console.log(chalk.gray('No agent defaults configured.'));
33
- console.log(chalk.gray('Set one with: agents set claude@2.1.220 --model opus-5'));
34
- return;
35
- }
36
- console.log(chalk.bold('Agent Defaults\n'));
37
- for (const entry of entries) {
38
- console.log(` ${formatRunDefaultEntry(entry)}`);
39
- }
40
- return;
41
- }
42
- if (!hasFlags) {
43
- const parsed = parseRunDefaultSelector(selector);
44
- const entry = listRunDefaults().find((e) => e.selector === parsed.selector);
45
- if (!entry || (!entry.defaults.mode && !entry.defaults.model)) {
46
- console.log(chalk.gray(`No default set for ${parsed.selector}.`));
47
- console.log(chalk.gray(`Set one with: agents set ${selector} --model <model>`));
48
- return;
49
- }
50
- console.log(` ${formatRunDefaultEntry(entry)}`);
51
- return;
52
- }
53
- const entry = setRunDefault(selector, {
54
- ...(options.mode !== undefined ? { mode: options.mode } : {}),
55
- ...(options.model !== undefined ? { model: options.model } : {}),
56
- });
57
- console.log(chalk.green('Set default:'));
58
- console.log(` ${formatRunDefaultEntry(entry)}`);
59
- }
60
- catch (err) {
61
- console.error(chalk.red(err.message));
62
- process.exit(1);
63
- }
64
- });
65
- setHelpSections(set, {
66
- examples: `
67
- agents set claude@2.1.220 --model opus-5
68
- agents set 'claude:*' --mode auto --model opus
69
- agents set claude@2.1.220
70
- agents set
71
- `,
72
- notes: `
73
- Selectors use <agent>@<version> or <agent>:<version>; * matches all versions.
74
- Exact selectors override wildcard selectors field by field.
75
- Writes the same store as 'agents config set run.<agent@version>.*'. Explicit flags on
76
- 'agents run' always win over configured defaults.
77
- `,
78
- });
79
- }
@@ -1,17 +0,0 @@
1
- /**
2
- * `agents setup share` — interactive wizard to configure the `agents share`
3
- * endpoint (Cloudflare R2 + Worker). A friendly front door over the existing
4
- * `agents share setup` (provision) and `agents share join` flows, reusing their
5
- * exact logic so there is a single source of truth for provisioning.
6
- *
7
- * Idempotent: re-running shows the current endpoint and offers to reconfigure.
8
- */
9
- import type { Command } from 'commander';
10
- /**
11
- * Interactive share setup. Returns true if the user configured (or already had)
12
- * an endpoint, false if they skipped. Never throws on user cancel — callers
13
- * (the `agents setup` hub) rely on that to keep the fresh-machine flow going.
14
- */
15
- export declare function runShareWizard(): Promise<boolean>;
16
- /** Register `agents setup share` under the parent `setup` command. */
17
- export declare function registerSetupShareCommand(setupCmd: Command): void;