@phnx-labs/agents-cli 1.19.2 → 1.20.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (156) hide show
  1. package/CHANGELOG.md +140 -0
  2. package/README.md +72 -12
  3. package/dist/browser.js +0 -0
  4. package/dist/commands/browser.js +88 -16
  5. package/dist/commands/cli.d.ts +14 -0
  6. package/dist/commands/cli.js +244 -0
  7. package/dist/commands/cloud.js +1 -1
  8. package/dist/commands/commands.js +27 -10
  9. package/dist/commands/computer.js +18 -1
  10. package/dist/commands/doctor.d.ts +1 -1
  11. package/dist/commands/doctor.js +2 -2
  12. package/dist/commands/exec.js +38 -18
  13. package/dist/commands/factory.d.ts +3 -14
  14. package/dist/commands/factory.js +3 -3
  15. package/dist/commands/feedback.d.ts +7 -0
  16. package/dist/commands/feedback.js +89 -0
  17. package/dist/commands/helper.d.ts +12 -0
  18. package/dist/commands/helper.js +87 -0
  19. package/dist/commands/hooks.js +89 -10
  20. package/dist/commands/mcp.js +166 -10
  21. package/dist/commands/packages.js +196 -27
  22. package/dist/commands/permissions.js +21 -6
  23. package/dist/commands/plugins.js +11 -4
  24. package/dist/commands/profiles.d.ts +8 -0
  25. package/dist/commands/profiles.js +118 -5
  26. package/dist/commands/prune.js +39 -160
  27. package/dist/commands/pull.js +58 -5
  28. package/dist/commands/routines.js +107 -14
  29. package/dist/commands/rules.js +8 -4
  30. package/dist/commands/secrets-migrate.d.ts +24 -0
  31. package/dist/commands/secrets-migrate.js +198 -0
  32. package/dist/commands/secrets-sync.d.ts +11 -0
  33. package/dist/commands/secrets-sync.js +155 -0
  34. package/dist/commands/secrets.js +79 -46
  35. package/dist/commands/sessions.d.ts +28 -0
  36. package/dist/commands/sessions.js +98 -33
  37. package/dist/commands/setup.d.ts +1 -0
  38. package/dist/commands/setup.js +37 -28
  39. package/dist/commands/skills.js +25 -8
  40. package/dist/commands/subagents.js +69 -49
  41. package/dist/commands/teams.js +61 -10
  42. package/dist/commands/utils.d.ts +33 -0
  43. package/dist/commands/utils.js +139 -0
  44. package/dist/commands/versions.d.ts +4 -3
  45. package/dist/commands/versions.js +134 -130
  46. package/dist/commands/view.d.ts +6 -0
  47. package/dist/commands/view.js +175 -19
  48. package/dist/commands/workflows.js +29 -6
  49. package/dist/computer.js +0 -0
  50. package/dist/index.js +38 -6
  51. package/dist/lib/acp/client.js +6 -1
  52. package/dist/lib/acp/harnesses.js +8 -0
  53. package/dist/lib/agents.d.ts +4 -0
  54. package/dist/lib/agents.js +125 -34
  55. package/dist/lib/auto-pull-worker.js +18 -1
  56. package/dist/lib/browser/cdp.d.ts +8 -1
  57. package/dist/lib/browser/cdp.js +40 -3
  58. package/dist/lib/browser/chrome.d.ts +13 -0
  59. package/dist/lib/browser/chrome.js +46 -3
  60. package/dist/lib/browser/domain-skills.d.ts +51 -0
  61. package/dist/lib/browser/domain-skills.js +157 -0
  62. package/dist/lib/browser/drivers/local.js +45 -4
  63. package/dist/lib/browser/drivers/ssh.js +2 -2
  64. package/dist/lib/browser/ipc.d.ts +8 -1
  65. package/dist/lib/browser/ipc.js +37 -28
  66. package/dist/lib/browser/profiles.d.ts +16 -3
  67. package/dist/lib/browser/profiles.js +44 -4
  68. package/dist/lib/browser/service.d.ts +3 -0
  69. package/dist/lib/browser/service.js +40 -5
  70. package/dist/lib/browser/types.d.ts +11 -4
  71. package/dist/lib/cli-resources.d.ts +137 -0
  72. package/dist/lib/cli-resources.js +477 -0
  73. package/dist/lib/cloud/factory.d.ts +1 -1
  74. package/dist/lib/cloud/factory.js +1 -1
  75. package/dist/lib/cloud/rush.js +5 -5
  76. package/dist/lib/command-skills.js +0 -2
  77. package/dist/lib/computer-rpc.d.ts +3 -0
  78. package/dist/lib/computer-rpc.js +53 -0
  79. package/dist/lib/daemon.js +20 -0
  80. package/dist/lib/events.d.ts +16 -2
  81. package/dist/lib/events.js +33 -2
  82. package/dist/lib/exec.d.ts +42 -13
  83. package/dist/lib/exec.js +127 -33
  84. package/dist/lib/help.js +11 -5
  85. package/dist/lib/hooks/cache.d.ts +38 -0
  86. package/dist/lib/hooks/cache.js +242 -0
  87. package/dist/lib/hooks/profile.d.ts +33 -0
  88. package/dist/lib/hooks/profile.js +129 -0
  89. package/dist/lib/hooks.d.ts +0 -10
  90. package/dist/lib/hooks.js +246 -11
  91. package/dist/lib/mcp.d.ts +15 -0
  92. package/dist/lib/mcp.js +46 -0
  93. package/dist/lib/migrate.js +1 -1
  94. package/dist/lib/overdue.d.ts +26 -0
  95. package/dist/lib/overdue.js +101 -0
  96. package/dist/lib/permissions.d.ts +13 -0
  97. package/dist/lib/permissions.js +55 -1
  98. package/dist/lib/plugin-marketplace.js +1 -1
  99. package/dist/lib/plugins.js +15 -1
  100. package/dist/lib/profiles-presets.d.ts +26 -0
  101. package/dist/lib/profiles-presets.js +216 -0
  102. package/dist/lib/profiles.d.ts +34 -0
  103. package/dist/lib/profiles.js +112 -1
  104. package/dist/lib/resources/mcp.js +37 -0
  105. package/dist/lib/resources.d.ts +1 -1
  106. package/dist/lib/rotate.js +10 -4
  107. package/dist/lib/routines-format.d.ts +47 -0
  108. package/dist/lib/routines-format.js +194 -0
  109. package/dist/lib/routines.d.ts +8 -2
  110. package/dist/lib/routines.js +34 -14
  111. package/dist/lib/runner.js +83 -15
  112. package/dist/lib/scheduler.js +8 -1
  113. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  114. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  115. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
  116. package/dist/lib/secrets/bundles.d.ts +34 -17
  117. package/dist/lib/secrets/bundles.js +210 -36
  118. package/dist/lib/secrets/index.d.ts +49 -30
  119. package/dist/lib/secrets/index.js +126 -115
  120. package/dist/lib/secrets/install-helper.d.ts +45 -0
  121. package/dist/lib/secrets/install-helper.js +165 -0
  122. package/dist/lib/secrets/linux.js +4 -4
  123. package/dist/lib/secrets/sync.d.ts +56 -0
  124. package/dist/lib/secrets/sync.js +180 -0
  125. package/dist/lib/session/active.d.ts +8 -0
  126. package/dist/lib/session/active.js +3 -2
  127. package/dist/lib/session/db.d.ts +0 -4
  128. package/dist/lib/session/db.js +0 -26
  129. package/dist/lib/session/parse.d.ts +1 -0
  130. package/dist/lib/session/parse.js +44 -0
  131. package/dist/lib/session/render.js +4 -4
  132. package/dist/lib/session/types.d.ts +2 -2
  133. package/dist/lib/session/types.js +1 -1
  134. package/dist/lib/shims.d.ts +5 -2
  135. package/dist/lib/shims.js +70 -38
  136. package/dist/lib/state.d.ts +14 -2
  137. package/dist/lib/state.js +51 -20
  138. package/dist/lib/teams/agents.d.ts +5 -4
  139. package/dist/lib/teams/agents.js +48 -22
  140. package/dist/lib/teams/api.d.ts +2 -1
  141. package/dist/lib/teams/api.js +4 -3
  142. package/dist/lib/teams/parsers.d.ts +1 -1
  143. package/dist/lib/teams/parsers.js +153 -3
  144. package/dist/lib/teams/summarizer.js +18 -2
  145. package/dist/lib/teams/worktree.js +14 -3
  146. package/dist/lib/types.d.ts +63 -4
  147. package/dist/lib/types.js +8 -3
  148. package/dist/lib/usage.d.ts +27 -2
  149. package/dist/lib/usage.js +100 -17
  150. package/dist/lib/versions.d.ts +45 -3
  151. package/dist/lib/versions.js +455 -60
  152. package/package.json +15 -14
  153. package/scripts/install-helper.js +97 -0
  154. package/scripts/postinstall.js +16 -0
  155. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
  156. package/npm-shrinkwrap.json +0 -3162
@@ -0,0 +1,244 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import chalk from 'chalk';
4
+ import { confirm } from '@inquirer/prompts';
5
+ import { listCliManifests, listCliStatus, resolveCliManifest, installCli, describeMethod, describeCheck, selectInstallMethod, isCliInstalled, } from '../lib/cli-resources.js';
6
+ import { getUserAgentsDir } from '../lib/state.js';
7
+ import { isPromptCancelled } from './utils.js';
8
+ function userCliDir() {
9
+ return path.join(getUserAgentsDir(), 'cli');
10
+ }
11
+ /** Render the status table — one row per declared CLI. */
12
+ function printStatus(rows) {
13
+ if (rows.length === 0) {
14
+ console.log(chalk.gray('No CLIs declared.'));
15
+ console.log(chalk.gray(`Create one with: agents cli add <name>`));
16
+ return;
17
+ }
18
+ const nameWidth = Math.max(4, ...rows.map((r) => r.manifest.name.length));
19
+ for (const row of rows) {
20
+ const status = row.installed
21
+ ? chalk.green('installed')
22
+ : chalk.yellow('missing');
23
+ const name = row.manifest.name.padEnd(nameWidth);
24
+ const source = chalk.gray(`[${row.manifest.source}]`);
25
+ const desc = row.manifest.description ? ' ' + chalk.gray(row.manifest.description) : '';
26
+ console.log(` ${name} ${status} ${source}${desc}`);
27
+ }
28
+ }
29
+ export function registerCliCommands(program) {
30
+ const cliCmd = program
31
+ .command('cli')
32
+ .description('Declare and install host CLI binaries (gh, higgsfield, glab, ...)')
33
+ .addHelpText('after', `
34
+ CLI manifests live in <repo>/cli/<name>.yaml and declare how to install a
35
+ binary on the host. On a fresh machine, 'agents cli install' runs the first
36
+ compatible method (npm > brew > script > binary) for every declared entry.
37
+
38
+ Examples:
39
+ # See which declared CLIs are installed on this host
40
+ agents cli list
41
+
42
+ # Install everything that's missing
43
+ agents cli install
44
+
45
+ # Install one
46
+ agents cli install higgsfield
47
+
48
+ # Show the manifest detail
49
+ agents cli view higgsfield
50
+
51
+ # Exit 0 if all declared CLIs are installed (use in CI / setup scripts)
52
+ agents cli check
53
+
54
+ When to use:
55
+ - After 'agents pull' on a new machine, to materialize host binaries
56
+ - In a team setup: commit cli/ entries so teammates get the same toolchain
57
+ `);
58
+ cliCmd
59
+ .command('list')
60
+ .description('Show all declared CLIs and whether each is installed on this host')
61
+ .action(() => {
62
+ const { statuses, errors } = listCliStatus(process.cwd());
63
+ printStatus(statuses);
64
+ for (const err of errors) {
65
+ console.log(chalk.red(` parse error: ${err.file}: ${err.reason}`));
66
+ }
67
+ });
68
+ cliCmd
69
+ .command('check')
70
+ .description('Exit 0 if every declared CLI is installed, 1 otherwise')
71
+ .action(() => {
72
+ const { statuses, errors } = listCliStatus(process.cwd());
73
+ const missing = statuses.filter((s) => !s.installed);
74
+ if (errors.length > 0) {
75
+ for (const err of errors) {
76
+ console.error(chalk.red(`parse error: ${err.file}: ${err.reason}`));
77
+ }
78
+ process.exit(1);
79
+ }
80
+ if (missing.length === 0) {
81
+ console.log(chalk.green(`All ${statuses.length} declared CLI(s) installed.`));
82
+ return;
83
+ }
84
+ console.log(chalk.yellow(`Missing: ${missing.map((s) => s.manifest.name).join(', ')}`));
85
+ process.exit(1);
86
+ });
87
+ cliCmd
88
+ .command('install [name]')
89
+ .description('Install one (by name) or all missing declared CLIs')
90
+ .option('-y, --yes', 'skip the confirmation prompt')
91
+ .option('--dry-run', 'print install commands without executing them')
92
+ .option('--force', 'reinstall even when the check command currently passes')
93
+ .action(async (nameArg, opts) => {
94
+ let targets;
95
+ if (nameArg) {
96
+ const manifest = resolveCliManifest(nameArg, process.cwd());
97
+ if (!manifest) {
98
+ console.error(chalk.red(`No CLI manifest named "${nameArg}".`));
99
+ console.error(chalk.gray(`Looked in: ${userCliDir()}`));
100
+ process.exit(1);
101
+ }
102
+ targets = [manifest];
103
+ }
104
+ else {
105
+ const { manifests, errors } = listCliManifests(process.cwd());
106
+ for (const err of errors) {
107
+ console.error(chalk.red(`parse error: ${err.file}: ${err.reason}`));
108
+ }
109
+ if (manifests.length === 0) {
110
+ console.log(chalk.gray('No CLIs declared. Nothing to install.'));
111
+ return;
112
+ }
113
+ targets = manifests;
114
+ }
115
+ // Filter out already-installed unless --force
116
+ const work = targets.filter((m) => opts.force || !isCliInstalled(m));
117
+ if (work.length === 0) {
118
+ console.log(chalk.green(`All ${targets.length} declared CLI(s) already installed.`));
119
+ return;
120
+ }
121
+ // Preview + confirm
122
+ console.log(chalk.bold('\nWill install:'));
123
+ for (const m of work) {
124
+ const method = selectInstallMethod(m);
125
+ const action = method ? describeMethod(method) : chalk.red('no compatible install method');
126
+ console.log(` ${chalk.cyan(m.name.padEnd(20))} ${chalk.gray(action)}`);
127
+ }
128
+ console.log('');
129
+ if (!opts.yes && !opts.dryRun) {
130
+ try {
131
+ const proceed = await confirm({ message: 'Proceed?', default: true });
132
+ if (!proceed) {
133
+ console.log(chalk.gray('Cancelled.'));
134
+ return;
135
+ }
136
+ }
137
+ catch (err) {
138
+ if (isPromptCancelled(err)) {
139
+ console.log(chalk.gray('Cancelled.'));
140
+ return;
141
+ }
142
+ throw err;
143
+ }
144
+ }
145
+ // Execute
146
+ let failures = 0;
147
+ for (const m of work) {
148
+ console.log(chalk.bold(`\n→ ${m.name}`));
149
+ const result = installCli(m, { dryRun: opts.dryRun });
150
+ if (result.output)
151
+ console.log(chalk.gray(result.output));
152
+ if (result.error) {
153
+ console.log(chalk.red(` ${result.error}`));
154
+ failures++;
155
+ continue;
156
+ }
157
+ if (opts.dryRun)
158
+ continue;
159
+ if (result.installed) {
160
+ console.log(chalk.green(` installed (${describeMethod(result.method)})`));
161
+ if (m.postInstall) {
162
+ console.log(chalk.gray(m.postInstall.trim().split('\n').map((l) => ' ' + l).join('\n')));
163
+ }
164
+ }
165
+ else {
166
+ console.log(chalk.yellow(` install command ran but \`${describeCheck(m.check)}\` still fails — check the output above`));
167
+ failures++;
168
+ }
169
+ }
170
+ if (failures > 0)
171
+ process.exit(1);
172
+ });
173
+ cliCmd
174
+ .command('view <name>')
175
+ .description('Show the parsed manifest detail')
176
+ .action((name) => {
177
+ const manifest = resolveCliManifest(name, process.cwd());
178
+ if (!manifest) {
179
+ console.error(chalk.red(`No CLI manifest named "${name}".`));
180
+ process.exit(1);
181
+ }
182
+ console.log(chalk.bold.cyan(manifest.name));
183
+ if (manifest.description)
184
+ console.log(' ' + chalk.gray(manifest.description));
185
+ if (manifest.homepage)
186
+ console.log(' ' + chalk.gray(manifest.homepage));
187
+ console.log(' ' + chalk.gray(`source: ${manifest.source}`));
188
+ console.log(' ' + chalk.gray(`file: ${manifest.path}`));
189
+ console.log('');
190
+ console.log(chalk.bold(' check'));
191
+ console.log(' ' + describeCheck(manifest.check));
192
+ console.log('');
193
+ console.log(chalk.bold(' install methods'));
194
+ for (const method of manifest.install) {
195
+ console.log(' ' + describeMethod(method));
196
+ }
197
+ if (manifest.postInstall) {
198
+ console.log('');
199
+ console.log(chalk.bold(' post_install'));
200
+ for (const line of manifest.postInstall.trim().split('\n')) {
201
+ console.log(' ' + line);
202
+ }
203
+ }
204
+ console.log('');
205
+ console.log(` status: ${isCliInstalled(manifest) ? chalk.green('installed') : chalk.yellow('missing')}`);
206
+ });
207
+ cliCmd
208
+ .command('add <name>')
209
+ .description('Scaffold a new manifest at ~/.agents/cli/<name>.yaml')
210
+ .option('--npm <pkg>', 'declare an npm install method')
211
+ .option('--brew <formula>', 'declare a brew install method')
212
+ .option('--script <url>', 'declare a curl|sh install method')
213
+ .option('--description <text>', 'one-line description')
214
+ .option('--homepage <url>', 'project homepage')
215
+ .action((name, opts) => {
216
+ const dir = userCliDir();
217
+ fs.mkdirSync(dir, { recursive: true });
218
+ const target = path.join(dir, `${name}.yaml`);
219
+ if (fs.existsSync(target)) {
220
+ console.error(chalk.red(`Already exists: ${target}`));
221
+ process.exit(1);
222
+ }
223
+ const methods = [];
224
+ if (opts.npm)
225
+ methods.push(` - npm: "${opts.npm}"`);
226
+ if (opts.brew)
227
+ methods.push(` - brew: ${opts.brew}`);
228
+ if (opts.script)
229
+ methods.push(` - script: ${opts.script}`);
230
+ if (methods.length === 0) {
231
+ methods.push(` - npm: "${name}"`);
232
+ }
233
+ const lines = [
234
+ `name: ${name}`,
235
+ ...(opts.description ? [`description: ${opts.description}`] : []),
236
+ ...(opts.homepage ? [`homepage: ${opts.homepage}`] : []),
237
+ `check: ${name} --version`,
238
+ `install:`,
239
+ ...methods,
240
+ ];
241
+ fs.writeFileSync(target, lines.join('\n') + '\n');
242
+ console.log(chalk.green(`Created ${target}`));
243
+ });
244
+ }
@@ -68,7 +68,7 @@ Examples:
68
68
  agents cloud run "add pytest fixtures for the new billing module" --provider codex --env env_a1b2c3 --agent codex --timeout 30m
69
69
 
70
70
  # Factory pod targeting a specific computer (Droid)
71
- agents cloud run "QA the new onboarding flow end-to-end" --provider factory --computer mac-mini-1 --agent droid
71
+ agents cloud run "QA the new onboarding flow end-to-end" --provider factory --computer linux-vm-1 --agent droid
72
72
 
73
73
  # See every cloud task you've dispatched (most recent first)
74
74
  agents cloud list
@@ -9,9 +9,9 @@ import { cloneRepo } from '../lib/git.js';
9
9
  import { discoverCommands, resolveCommandSource, installCommandCentrally, listCentralCommands, listInstalledCommandsWithScope, getCommandInfo, diffVersionCommands, iterCommandsCapableVersions, removeCommandFromVersion, } from '../lib/commands.js';
10
10
  import { getCommandsDir } from '../lib/state.js';
11
11
  import { showResourceList, buildTargetsSection, } from './resource-view.js';
12
- import { getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, resolveAgentVersionTargets, } from '../lib/versions.js';
12
+ import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, resolveInstalledAgentTargets, } from '../lib/versions.js';
13
13
  import { recordVersionResources } from '../lib/state.js';
14
- import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, promptRemovalTargets, } from './utils.js';
14
+ import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, promptRemovalTargets, resolveAgentTargetsAutoInstalling, } from './utils.js';
15
15
  /** Register the `agents commands` command tree (list, add, remove, sync, prune, view). */
16
16
  export function registerCommandsCommands(program) {
17
17
  const commandsCmd = program
@@ -214,13 +214,17 @@ Examples:
214
214
  let selectedAgents;
215
215
  let versionSelections;
216
216
  if (options.agents) {
217
- const result = resolveAgentVersionTargets(options.agents, ALL_AGENT_IDS);
217
+ const result = await resolveAgentTargetsAutoInstalling(options.agents, ALL_AGENT_IDS, { yes: options.yes });
218
+ if (!result) {
219
+ console.log(chalk.gray('Cancelled.'));
220
+ return;
221
+ }
218
222
  selectedAgents = result.selectedAgents;
219
223
  versionSelections = result.versionSelections;
220
224
  }
221
225
  else {
222
226
  const result = await promptAgentVersionSelection(ALL_AGENT_IDS, {
223
- skipPrompts: options.yes || !isInteractiveTerminal(),
227
+ skipPrompts: options.yes,
224
228
  });
225
229
  selectedAgents = result.selectedAgents;
226
230
  versionSelections = result.versionSelections;
@@ -334,11 +338,24 @@ Examples:
334
338
  console.log(chalk.yellow(` Command '${cmdName}' not found in any version.`));
335
339
  continue;
336
340
  }
337
- // Filter by --agents if specified
341
+ // Filter by --agents if specified. Routes through resolveInstalledAgentTargets
342
+ // so the same selector syntax used everywhere else (agent, agent@default,
343
+ // agent@x.y.z, agent@all, literal all) works here too.
338
344
  let availableTargets = cmdInfo.targets;
339
345
  if (options?.agents) {
340
- const requestedAgents = new Set(options.agents.split(','));
341
- availableTargets = availableTargets.filter((t) => requestedAgents.has(t.agent));
346
+ const requestedTargets = resolveInstalledAgentTargets(options.agents, ALL_AGENT_IDS);
347
+ const requested = new Set();
348
+ for (const aid of requestedTargets.directAgents) {
349
+ for (const ver of listInstalledVersions(aid)) {
350
+ requested.add(`${aid}@${ver}`);
351
+ }
352
+ }
353
+ for (const [aid, versions] of requestedTargets.versionSelections) {
354
+ for (const ver of versions) {
355
+ requested.add(`${aid}@${ver}`);
356
+ }
357
+ }
358
+ availableTargets = availableTargets.filter((t) => requested.has(`${t.agent}@${t.version}`));
342
359
  }
343
360
  if (availableTargets.length === 0) {
344
361
  console.log(chalk.yellow(` Command '${cmdName}' not found in specified agents.`));
@@ -383,17 +400,17 @@ Examples:
383
400
  .action(() => {
384
401
  console.error(chalk.red('"agents commands sync" is gone.'));
385
402
  console.error(chalk.gray('Sync runs automatically when you launch the agent.'));
386
- console.error(chalk.gray('To remove orphans, use: agents prune commands'));
403
+ console.error(chalk.gray('To remove orphans, use: agents prune cleanup commands'));
387
404
  process.exit(1);
388
405
  });
389
- // `commands prune` moved to the top-level `agents prune` command.
406
+ // `commands prune` moved to the top-level `agents prune cleanup` command.
390
407
  commandsCmd
391
408
  .command('prune', { hidden: true })
392
409
  .allowUnknownOption()
393
410
  .allowExcessArguments()
394
411
  .action(() => {
395
412
  console.error(chalk.red('"agents commands prune" moved.'));
396
- console.error(chalk.gray('Use: agents prune commands (or `agents prune` for everything)'));
413
+ console.error(chalk.gray('Use: agents prune cleanup commands (or `agents prune cleanup` for everything)'));
397
414
  process.exit(1);
398
415
  });
399
416
  commandsCmd
@@ -3,7 +3,7 @@ import * as fs from 'fs';
3
3
  import * as os from 'os';
4
4
  import * as path from 'path';
5
5
  import { registerCommandGroups } from '../lib/help.js';
6
- import { openComputerClient, resolveHelperApp, resolveHelperExec, resolveSocketPath, resolveLogPath, resolvePolicyPath, describeTransport, loadComputerAllowList, writeComputerPolicy, } from '../lib/computer-rpc.js';
6
+ import { openComputerClient, resolveHelperApp, resolveHelperExec, resolveSocketPath, resolveLogPath, resolvePolicyPath, resolvePeersPath, describeTransport, loadComputerAllowList, loadDefaultPeers, writeComputerPolicy, writeComputerPeers, } from '../lib/computer-rpc.js';
7
7
  // Help groups — mirror `agents browser` so the mental model carries over.
8
8
  const COMPUTER_HELP_GROUPS = [
9
9
  { title: 'Installation', names: ['install-helper'] },
@@ -46,6 +46,8 @@ function registerStatusCommand(program) {
46
46
  const previewParts = allowed.slice(0, 5);
47
47
  const previewSuffix = allowed.length > 5 ? ` (+${allowed.length - 5} more)` : '';
48
48
  console.log(`policy: ${allowed.length} app${allowed.length === 1 ? '' : 's'} allowed${allowed.length > 0 ? `: ${previewParts.join(', ')}${previewSuffix}` : ''}`);
49
+ const callers = loadDefaultPeers();
50
+ console.log(`peers: ${callers.length} caller${callers.length === 1 ? '' : 's'} (peer-auth on socket)`);
49
51
  if (!installed) {
50
52
  console.log('');
51
53
  console.log('Run: agents computer install-helper');
@@ -284,6 +286,15 @@ function registerStartCommand(program) {
284
286
  console.log(` add to ~/.agents/permissions/groups/<name>.yaml under allow:`);
285
287
  console.log(` - "Computer(com.apple.finder)"`);
286
288
  }
289
+ // Peer-auth allow list — which caller executables may connect to
290
+ // the socket. Default: this CLI's Node binary, plus Rush.app if
291
+ // installed. A `nc -U socket` from a malicious npm postinstall has
292
+ // a different exec path and gets refused at accept().
293
+ const callers = loadDefaultPeers();
294
+ writeComputerPeers(callers);
295
+ console.log(`peers: ${callers.length} caller${callers.length === 1 ? '' : 's'} allowed (${resolvePeersPath()})`);
296
+ for (const p of callers)
297
+ console.log(` ${p}`);
287
298
  // Bootout first to clear any prior registration. Best-effort.
288
299
  try {
289
300
  execFileSync('/bin/launchctl', ['bootout', domain, plistPath], { stdio: 'pipe' });
@@ -356,6 +367,12 @@ function registerReloadCommand(program) {
356
367
  const allowed = loadComputerAllowList();
357
368
  writeComputerPolicy(allowed);
358
369
  console.log(`policy: ${allowed.length} app${allowed.length === 1 ? '' : 's'} allowed (${resolvePolicyPath()})`);
370
+ // Rewrite peers list too — an upgrade of the npm-global CLI moves
371
+ // its node path; without this the reloaded daemon would reject the
372
+ // very binary that just signaled it.
373
+ const callers = loadDefaultPeers();
374
+ writeComputerPeers(callers);
375
+ console.log(`peers: ${callers.length} caller${callers.length === 1 ? '' : 's'} allowed (${resolvePeersPath()})`);
359
376
  // Resolve the daemon's pid via `launchctl list <label>`. The plist
360
377
  // output includes a "PID" key when the service is running.
361
378
  const uid = process.getuid?.();
@@ -15,7 +15,7 @@
15
15
  * unified diff body for each divergent file. Mirrors the resolution that
16
16
  * the shim drives at runtime: project > user > system > extras.
17
17
  *
18
- * Read-only: doctor never mutates state. Run `agents prune` to act on orphan
18
+ * Read-only: doctor never mutates state. Run `agents prune cleanup` to act on orphan
19
19
  * readouts, or just launch the agent to apply pending sync.
20
20
  */
21
21
  import type { Command } from 'commander';
@@ -114,7 +114,7 @@ function renderOverviewText(clis, syncRows, orphanRows) {
114
114
  const label = `${AGENT_NAMES[row.agent] || row.agent}@${row.version}`;
115
115
  console.log(` ${chalk.yellow('warn ')} ${label} ${chalk.gray(parts.join(', '))}`);
116
116
  }
117
- console.log(chalk.gray(' Run `agents prune` to remove.'));
117
+ console.log(chalk.gray(' Run `agents prune cleanup` to remove.'));
118
118
  }
119
119
  }
120
120
  function parseTargetArg(arg) {
@@ -310,7 +310,7 @@ function renderTargetText(report, options) {
310
310
  }
311
311
  else {
312
312
  console.log(` Verdict: ${verdictParts.join(', ')}.`);
313
- console.log(chalk.gray(` Run \`agents sync --agent ${report.agent} --agent-version ${report.version}\` to reconcile, or \`agents prune\` to drop extras.`));
313
+ console.log(chalk.gray(` Run \`agents sync --agent ${report.agent} --agent-version ${report.version}\` to reconcile, or \`agents prune cleanup\` to drop extras.`));
314
314
  }
315
315
  }
316
316
  // ─── command registration ────────────────────────────────────────────────────
@@ -6,20 +6,13 @@
6
6
  * injection, and multi-agent fallback chains for rate-limit resilience.
7
7
  */
8
8
  import chalk from 'chalk';
9
- import { buildExecCommand, parseExecEnv, execAgent, runWithFallback, AGENT_COMMANDS, } from '../lib/exec.js';
10
- import { profileExists, resolveProfileForRun } from '../lib/profiles.js';
11
9
  import { setHelpSections } from '../lib/help.js';
12
- import { readBundle, resolveBundleEnv, describeBundle } from '../lib/secrets/bundles.js';
13
- import { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES, } from '../lib/rotate.js';
14
- import { getGlobalDefault, getVersionHomePath, resolveVersionAlias } from '../lib/versions.js';
15
- import { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion } from '../lib/plugins.js';
16
- import { parseWorkflowFrontmatter, resolveWorkflowRef } from '../lib/workflows.js';
10
+ import { AGENTS } from '../lib/agents.js';
17
11
  import * as fs from 'fs';
18
12
  import * as path from 'path';
19
- const VALID_AGENTS = Object.keys(AGENT_COMMANDS);
20
13
  /** Type guard that narrows a string to a known AgentId. */
21
14
  function isValidAgent(agent) {
22
- return VALID_AGENTS.includes(agent);
15
+ return agent in AGENTS;
23
16
  }
24
17
  /** Build a one-line banner describing which version the strategy picked. */
25
18
  function formatRotationBanner(result, verb = 'balanced') {
@@ -33,7 +26,7 @@ export function registerRunCommand(program) {
33
26
  const runCmd = program
34
27
  .command('run <agent> [prompt]')
35
28
  .description('Execute an agent. Pass a prompt for headless runs; omit it to launch the agent interactively.')
36
- .option('-m, --mode <mode>', 'How much the agent can do: plan (read-only), edit (can write files), full (writes + all permissions)', 'plan')
29
+ .option('-m, --mode <mode>', 'How much the agent can do: plan (read-only), edit (can write files), auto (smart classifier auto-approves safe ops, prompts for risky), skip (bypass all permission prompts). \'full\' accepted as alias for skip.', 'plan')
37
30
  .option('-e, --effort <effort>', 'Reasoning effort: low | medium | high | xhigh | max | auto (claude and codex only)', 'auto')
38
31
  .option('--model <model>', 'Override the model directly (e.g., claude-opus-4-6)')
39
32
  .option('--env <key=value>', 'Pass environment variable to the agent (repeatable, e.g., --env DEBUG=1 --env API_KEY=xyz)', (val, prev) => [...prev, val], [])
@@ -73,8 +66,12 @@ export function registerRunCommand(program) {
73
66
  agents run claude "deploy the worker" --secrets prod --mode edit
74
67
  `,
75
68
  notes: `
76
- Modes:
77
- plan read-only edit can write files full writes + all permissions
69
+ Modes (not every agent supports every mode — check agents.yaml capabilities):
70
+ plan read-only investigation; no writes, no shell side-effects
71
+ edit may edit files; prompts for shell / risky operations
72
+ auto smart classifier auto-approves safe ops, prompts for risky (claude, copilot)
73
+ skip bypass every permission prompt (dangerously-skip-permissions)
74
+ Legacy 'full' is silently rewritten to 'skip'.
78
75
 
79
76
  Run strategy (set via --strategy or run.<agent>.strategy in agents.yaml):
80
77
  pinned use the workspace/global pinned version (default)
@@ -88,6 +85,17 @@ export function registerRunCommand(program) {
88
85
  `,
89
86
  });
90
87
  runCmd.action(async (agentSpec, prompt, options) => {
88
+ const [{ buildExecCommand, parseExecEnv, execAgent, runWithFallback, normalizeMode, resolveMode }, { ALL_AGENT_IDS }, { profileExists, resolveProfileForRun }, { readAndResolveBundleEnv, describeBundle }, { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES }, { getGlobalDefault, getVersionHomePath, resolveVersionAlias }, { buildDiscoveredPlugin, loadPluginManifest, syncPluginToVersion }, { parseWorkflowFrontmatter, resolveWorkflowRef },] = await Promise.all([
89
+ import('../lib/exec.js'),
90
+ import('../lib/agents.js'),
91
+ import('../lib/profiles.js'),
92
+ import('../lib/secrets/bundles.js'),
93
+ import('../lib/rotate.js'),
94
+ import('../lib/versions.js'),
95
+ import('../lib/plugins.js'),
96
+ import('../lib/workflows.js'),
97
+ ]);
98
+ const isValidAgent = (agent) => ALL_AGENT_IDS.includes(agent);
91
99
  // Parse agent@version
92
100
  const [rawAgent, rawVersion] = agentSpec.split('@');
93
101
  let agent;
@@ -195,7 +203,7 @@ export function registerRunCommand(program) {
195
203
  }
196
204
  else {
197
205
  console.error(chalk.red(`Unknown agent: ${rawAgent}`));
198
- console.error(chalk.gray(`Available agents: ${VALID_AGENTS.join(', ')}`));
206
+ console.error(chalk.gray(`Available agents: ${ALL_AGENT_IDS.join(', ')}`));
199
207
  console.error(chalk.gray(`Or add a profile: agents profiles add <name>`));
200
208
  process.exit(1);
201
209
  }
@@ -244,9 +252,21 @@ export function registerRunCommand(program) {
244
252
  }
245
253
  }
246
254
  }
255
+ // Accept the four canonical modes plus 'full' as a permanent silent
256
+ // alias for 'skip' (rewritten downstream by normalizeMode in exec.ts).
247
257
  const mode = options.mode;
248
- if (!['plan', 'edit', 'full'].includes(mode)) {
249
- console.error(chalk.red(`Invalid mode: ${mode}. Use 'plan', 'edit', or 'full'`));
258
+ if (!['plan', 'edit', 'auto', 'skip', 'full'].includes(mode)) {
259
+ console.error(chalk.red(`Invalid mode: ${mode}. Use plan, edit, auto, or skip ('full' accepted as alias for skip).`));
260
+ process.exit(1);
261
+ }
262
+ // Surface capability errors as a clean CLI message instead of a stack
263
+ // trace from buildExecCommand. resolveMode degrades 'auto' silently and
264
+ // throws on unsupported 'plan'/'skip' — we catch and pretty-print.
265
+ try {
266
+ resolveMode(agent, normalizeMode(mode));
267
+ }
268
+ catch (err) {
269
+ console.error(chalk.red(err.message));
250
270
  process.exit(1);
251
271
  }
252
272
  const effort = options.effort;
@@ -268,7 +288,7 @@ export function registerRunCommand(program) {
268
288
  let secretsEnv = {};
269
289
  for (const bundleName of options.secrets) {
270
290
  try {
271
- const bundle = readBundle(bundleName);
291
+ const { bundle, env: bundleEnv } = readAndResolveBundleEnv(bundleName, { caller: `agent ${agent}` });
272
292
  const entries = describeBundle(bundle);
273
293
  const counts = {};
274
294
  for (const e of entries) {
@@ -276,7 +296,7 @@ export function registerRunCommand(program) {
276
296
  }
277
297
  const breakdown = Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(', ');
278
298
  console.log(chalk.gray(`[secrets] Resolved ${bundleName}: ${entries.length} keys (${breakdown})`));
279
- secretsEnv = { ...secretsEnv, ...resolveBundleEnv(bundle, { caller: `agent ${agent}` }) };
299
+ secretsEnv = { ...secretsEnv, ...bundleEnv };
280
300
  }
281
301
  catch (err) {
282
302
  console.error(chalk.red(err.message));
@@ -328,7 +348,7 @@ export function registerRunCommand(program) {
328
348
  const [fbAgent, fbVersion] = entry.split('@');
329
349
  if (!isValidAgent(fbAgent)) {
330
350
  console.error(chalk.red(`Unknown fallback agent: ${fbAgent}`));
331
- console.error(chalk.gray(`Available: ${VALID_AGENTS.join(', ')}`));
351
+ console.error(chalk.gray(`Available: ${ALL_AGENT_IDS.join(', ')}`));
332
352
  process.exit(1);
333
353
  }
334
354
  if (fbAgent === agent) {
@@ -1,19 +1,8 @@
1
1
  /**
2
- * Software Factory CLI -- thin client over `prix/factory/service`.
2
+ * Software Factory CLI submits Linear issues to a remote orchestrator.
3
3
  *
4
- * One verb today:
5
- *
6
- * agents factory submit <linear-ref> POST /factory/submit
7
- *
8
- * Everything else (planner pod, worker dispatch, PR-merged/CI-failed
9
- * webhooks, retry caps, heartbeat reaper) lives server-side in
10
- * `agents/prix/factory/service/src/factory.ts`, driven by the
11
- * `factory-tick` k8s CronJob. The laptop is optional after submit.
12
- *
13
- * Future verbs (list / status / tail / cancel / message) are intentionally
14
- * deferred until the matching server endpoints land; they'll be thin
15
- * clients too. No supervisor, ledger, oracle, or `~/.agents/factory/`
16
- * registry on the laptop -- ever.
4
+ * Requires FACTORY_FLOOR_URL pointing at a Factory-compatible endpoint.
5
+ * Beta-gated; enable with `agents beta enable factory`.
17
6
  */
18
7
  import type { Command } from 'commander';
19
8
  export declare function registerFactoryCommands(program: Command): void;
@@ -51,8 +51,8 @@ export function registerFactoryCommands(program) {
51
51
  .description('Software Factory -- submit Linear tickets to the cloud orchestrator.')
52
52
  .addHelpText('after', `
53
53
  Examples:
54
- agents factory submit EXAMPLE-2451
55
- agents factory submit https://linear.app/example/issue/EXAMPLE-2451
54
+ agents factory submit PROJ-123
55
+ agents factory submit https://linear.app/example/issue/PROJ-123
56
56
  `);
57
57
  factory.hook('preAction', () => {
58
58
  if (enabled)
@@ -63,7 +63,7 @@ Examples:
63
63
  });
64
64
  factory
65
65
  .command('submit <linear-ref>')
66
- .description('Submit a Linear issue (EXAMPLE-123 or URL) to the Software Factory.')
66
+ .description('Submit a Linear issue (PROJ-123 or URL) to the Software Factory.')
67
67
  .option('--json', 'Output machine-readable JSON')
68
68
  .action(async (ref, opts) => {
69
69
  const result = await postFactorySubmit(ref);
@@ -0,0 +1,7 @@
1
+ /**
2
+ * `agents feedback` — frictionless, in-CLI feedback. Opens a Discussion
3
+ * pre-filled with version + OS + agent inventory; falls back to printing the
4
+ * URL when no browser is available.
5
+ */
6
+ import type { Command } from 'commander';
7
+ export declare function registerFeedbackCommand(program: Command): void;