@phnx-labs/agents-cli 1.20.0 → 1.20.4

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 (111) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/README.md +4 -4
  3. package/dist/commands/cli.js +3 -3
  4. package/dist/commands/cloud.js +1 -1
  5. package/dist/commands/commands.js +24 -7
  6. package/dist/commands/exec.js +36 -16
  7. package/dist/commands/feedback.d.ts +7 -0
  8. package/dist/commands/feedback.js +89 -0
  9. package/dist/commands/helper.d.ts +12 -0
  10. package/dist/commands/helper.js +87 -0
  11. package/dist/commands/hooks.js +86 -7
  12. package/dist/commands/import.js +90 -37
  13. package/dist/commands/mcp.js +166 -10
  14. package/dist/commands/packages.js +196 -27
  15. package/dist/commands/permissions.js +21 -6
  16. package/dist/commands/profiles.d.ts +8 -0
  17. package/dist/commands/profiles.js +117 -4
  18. package/dist/commands/pull.js +4 -4
  19. package/dist/commands/routines.js +6 -6
  20. package/dist/commands/rules.js +8 -4
  21. package/dist/commands/secrets-migrate.d.ts +24 -0
  22. package/dist/commands/secrets-migrate.js +198 -0
  23. package/dist/commands/secrets-sync.d.ts +11 -0
  24. package/dist/commands/secrets-sync.js +155 -0
  25. package/dist/commands/secrets.js +74 -39
  26. package/dist/commands/skills.js +22 -5
  27. package/dist/commands/subagents.js +69 -49
  28. package/dist/commands/teams.js +48 -10
  29. package/dist/commands/utils.d.ts +33 -0
  30. package/dist/commands/utils.js +139 -0
  31. package/dist/commands/versions.js +4 -4
  32. package/dist/commands/view.d.ts +6 -0
  33. package/dist/commands/view.js +169 -8
  34. package/dist/commands/workflows.js +29 -6
  35. package/dist/index.js +4 -0
  36. package/dist/lib/acp/client.js +6 -1
  37. package/dist/lib/agents.d.ts +4 -0
  38. package/dist/lib/agents.js +41 -17
  39. package/dist/lib/auto-pull-worker.js +18 -1
  40. package/dist/lib/browser/chrome.js +4 -0
  41. package/dist/lib/browser/drivers/ssh.js +1 -1
  42. package/dist/lib/browser/profiles.d.ts +3 -3
  43. package/dist/lib/browser/profiles.js +3 -3
  44. package/dist/lib/browser/service.js +19 -0
  45. package/dist/lib/browser/types.d.ts +4 -4
  46. package/dist/lib/cli-resources.d.ts +36 -8
  47. package/dist/lib/cli-resources.js +268 -46
  48. package/dist/lib/cloud/factory.d.ts +1 -1
  49. package/dist/lib/cloud/factory.js +1 -1
  50. package/dist/lib/events.d.ts +16 -2
  51. package/dist/lib/events.js +33 -2
  52. package/dist/lib/exec.d.ts +39 -11
  53. package/dist/lib/exec.js +90 -31
  54. package/dist/lib/help.js +11 -5
  55. package/dist/lib/hooks/cache.d.ts +38 -0
  56. package/dist/lib/hooks/cache.js +242 -0
  57. package/dist/lib/hooks/profile.d.ts +33 -0
  58. package/dist/lib/hooks/profile.js +129 -0
  59. package/dist/lib/hooks.d.ts +0 -10
  60. package/dist/lib/hooks.js +68 -15
  61. package/dist/lib/import.d.ts +21 -0
  62. package/dist/lib/import.js +55 -2
  63. package/dist/lib/mcp.d.ts +15 -0
  64. package/dist/lib/mcp.js +40 -0
  65. package/dist/lib/permissions.d.ts +13 -0
  66. package/dist/lib/permissions.js +51 -1
  67. package/dist/lib/plugin-marketplace.d.ts +10 -0
  68. package/dist/lib/plugin-marketplace.js +47 -1
  69. package/dist/lib/plugins.js +15 -1
  70. package/dist/lib/profiles-presets.d.ts +26 -0
  71. package/dist/lib/profiles-presets.js +187 -8
  72. package/dist/lib/profiles.d.ts +34 -0
  73. package/dist/lib/profiles.js +112 -1
  74. package/dist/lib/pty-server.js +27 -3
  75. package/dist/lib/routines-format.d.ts +17 -5
  76. package/dist/lib/routines-format.js +37 -16
  77. package/dist/lib/routines.d.ts +1 -1
  78. package/dist/lib/routines.js +2 -2
  79. package/dist/lib/runner.js +64 -10
  80. package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
  81. package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
  82. package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
  83. package/dist/lib/secrets/bundles.d.ts +18 -22
  84. package/dist/lib/secrets/bundles.js +75 -99
  85. package/dist/lib/secrets/index.d.ts +51 -27
  86. package/dist/lib/secrets/index.js +147 -156
  87. package/dist/lib/secrets/install-helper.d.ts +45 -0
  88. package/dist/lib/secrets/install-helper.js +165 -0
  89. package/dist/lib/secrets/linux.js +4 -4
  90. package/dist/lib/secrets/sync.d.ts +56 -0
  91. package/dist/lib/secrets/sync.js +180 -0
  92. package/dist/lib/session/render.js +4 -4
  93. package/dist/lib/session/types.d.ts +1 -1
  94. package/dist/lib/shims.d.ts +4 -1
  95. package/dist/lib/shims.js +5 -35
  96. package/dist/lib/state.d.ts +14 -1
  97. package/dist/lib/state.js +49 -5
  98. package/dist/lib/teams/agents.d.ts +5 -4
  99. package/dist/lib/teams/agents.js +47 -21
  100. package/dist/lib/teams/api.d.ts +2 -1
  101. package/dist/lib/teams/api.js +4 -3
  102. package/dist/lib/types.d.ts +57 -1
  103. package/dist/lib/types.js +2 -0
  104. package/dist/lib/usage.d.ts +27 -2
  105. package/dist/lib/usage.js +100 -17
  106. package/dist/lib/versions.d.ts +35 -1
  107. package/dist/lib/versions.js +288 -64
  108. package/package.json +13 -12
  109. package/scripts/install-helper.js +97 -0
  110. package/scripts/postinstall.js +16 -0
  111. package/dist/lib/secrets/Agents CLI.app/Contents/embedded.provisionprofile +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,86 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.20.4
4
+
5
+ **Plugin marketplace sync (skip outside-pointing symlinks)**
6
+
7
+ - `copyPluginToMarketplace` used `fs.cpSync(plugin.root, dest, { recursive: true, dereference: false })`, which faithfully preserved every symlink — including the ones plugin authors put at the top of their plugin source for prompt-side references (the rush plugin's `app -> ../../../rush/app`, `web -> rush/web`, `widgets -> rush/widgets`). Those targets resolve to the rush monorepo (~8.7 GB of `app/` including node_modules + .next builds, 782 MB of `web/`, plus 463 MB brand-assets). Every claude version got a full set of those symlinks in `~/.claude/plugins/marketplaces/agents-cli/plugins/rush/`. When the consumer (Claude Code, OpenClaw) discovers plugins, it walks the marketplace tree and follows those symlinks — producing multi-minute startup hangs.
8
+ - The copy now walks the source tree and drops symlinks whose `realpath` escapes the plugin root, leaving internal symlinks intact (cpSync rewrites internal targets to absolute paths into the source tree, which the consumer still resolves correctly). One informational line per plugin lists the skipped names so plugin authors notice.
9
+ - Existing per-version marketplace directories still hold the bloat from prior syncs; clean up with `rm` against `~/.claude/plugins/marketplaces/agents-cli/plugins/*/{app,web,widgets,*-symlinks-that-escaped}` then re-run `agents pull` or any plugin sync to re-copy with the filter.
10
+
11
+ ## 1.20.3
12
+
13
+ **`agents run` startup latency (stale-while-revalidate the usage probe + memoize agents.yaml)**
14
+
15
+ - The default `agents run` strategy is `available`, which calls `getUsageInfoForIdentity` to skip rate-limited accounts. With a 2-minute cache, every cold invocation past that window made a blocking `fetch` to `api.anthropic.com/api/oauth/usage` (5 s timeout, plus an optional 15 s OAuth token refresh) before `spawn(claude)` — so `agents run claude` regularly stalled 5–8 s with nothing on screen after the rotation banner.
16
+ - The cache is now stale-while-revalidate: fresh (<2 min) returns instantly with no network, stale-but-recent (<24 h) returns the cached snapshot instantly and refreshes in the background, and only a fully cold / >24 h cache blocks on the live fetch. The background refresh defers its first await past `setImmediate` so the synchronous Keychain CLI call (`security find-generic-password`, invoked by `loadClaudeOauth`) cannot block the foreground caller — that's how an SWR returns "instantly" even while the refresh is technically still on its first sync step.
17
+ - `readMeta()` had a `metaCache` module global plus `writeMetaUnlocked` cache-invalidation logic wired in years ago — but no read path ever consulted the cache. So every call did 2x `fs.readFileSync` + 2x `yaml.parse` on system + user `agents.yaml`, and hot callers (`getConfiguredRunStrategy`, `getGlobalDefault`, `getVersionResources`, `ensureVersionResourcePatterns`) fire it multiple times per `agents run`. The read path now consults the cache, keyed on the combined mtime of both source files — out-of-band edits still invalidate on the next stat, and in-process writers already clear it.
18
+
19
+ ## 1.20.2
20
+
21
+ **Grok and Antigravity Support & Documentation**
22
+
23
+ - **Grok CLI Integration**: Added support for installing Grok via `agents add grok@<version>`, which invokes the official xAI installer with the specified version. Grok MCP server configuration paths (via `config.toml`) and memory file mapping are now correctly documented.
24
+ - **Antigravity (AGY) CLI Integration**: Added support for the Google Antigravity CLI. Since the AGY installer doesn't support version-pinned installs currently, `agents add agy` uses the `latest` version. Documented the canonical config path `~/.gemini/antigravity-cli/` and its `mcp_config.json`.
25
+ - **Documentation**: Updated `02-resource-sync.md` to reflect accurate MCP mappings and memory file symlinks for both Grok and Antigravity.
26
+ - **Profiles**: Hardened presets with verified 2026 model IDs and added generic proxy configuration. Show custom profiles in agents view.
27
+
28
+ ## 1.20.1
29
+
30
+ **Agents selector (auto-install missing versions + unified `@all` everywhere)**
31
+
32
+ - `--agents claude@2.1.999` used to hard-error when 2.1.999 wasn't installed. Now the CLI prompts to install it inline and continues (auto-install with `--yes`). No more breaking flow to run `agents add` first.
33
+ - `--agents claude@all` and the bare `all` literal now work across every callsite that takes `--agents` — previously `agents install gh:...`, `mcp register`, `mcp remove`, and inline `mcp add` had diverged from the canonical syntax and threw "Version all is not installed" despite the help text advertising it. Selector is unified end-to-end.
34
+
35
+ **Prompt (fail loud on non-TTY + `@all` syntax in picker)**
36
+
37
+ - Scripts that called `agents <resource> add` with no `--agents` and no `--yes` used to silently auto-pick a default version. That hid scripted misuse behind unpredictable picks. The non-TTY path now throws with a clear pointer at the new syntax: `--agents claude@all` (every installed version of Claude), `--agents all` (every capable agent at all versions), or `--agents claude@2.1.141` (one specific version).
38
+ - `--agents` parsing in `<resource> add` understands `@all` and the bare `all` literal; `promptAgentVersionSelection`'s picker surfaces version counts when there's more than one installed, mirroring what `@all` would target.
39
+
40
+ **Resources / install (`gh:` form sniffs every type, `mcp add gh:`, `--names` + `@all` unified across resource add)**
41
+
42
+ - `agents install gh:<owner>/<repo>` now sniffs every resource type in the source repo (commands, skills, hooks, MCP, permissions, profiles, subagents, workflows) instead of requiring one `--types` per kind. Pass `--types skills,workflows` to narrow.
43
+ - New `agents mcp add gh:<owner>/<repo>` form — install MCP servers directly from a git source, parallel to the other `<resource> add gh:` paths.
44
+ - `<resource> add` accepts `--names` and `@all` uniformly across commands, skills, hooks, MCP, permissions, profiles, rules, subagents, workflows — same flags, same semantics, regardless of resource kind.
45
+
46
+ **Profiles (interactive `create` wizard, gateway + self-hosted presets)**
47
+
48
+ - New `agents profiles create` command — interactive wizard to assemble a profile from gateway or self-hosted presets (OpenRouter, OpenAI-compatible) without hand-writing YAML.
49
+ - `--smoke-test` exercises the resolved env block against the configured endpoint before writing the profile.
50
+
51
+ **Feedback (in-CLI bug / idea / question routing)**
52
+
53
+ - New `agents feedback` command — collects a short description + optional category (bug, idea, question) and routes to the project's tracker without leaving the terminal.
54
+
55
+ **Routines (real exit codes for detached scheduled runs)**
56
+
57
+ - `monitorRunningJobs` used to hardcode `status: 'failed'` whenever it detected that a detached child had exited — `executeJobDetached` fires-and-forgets, so the real exit code was unreachable. Every scheduler-driven routine ended up labeled `failed/exitCode: null`, even when the agent completed cleanly.
58
+ - Fix: when finalizing a vanished child, scan the tail of its stream-json `stdout.log` for Claude's `type: result` terminator (which carries `is_error`). If found, set `status` and `exitCode` from it. Only fall back to `failed` when no result marker exists (process was killed mid-run).
59
+ - Routines list cell rendering hardened around 7-day retention boundaries.
60
+ - Codex/Gemini run finalization continues to fall back to `failed` until their stream tail parsers are added.
61
+
62
+ **Security**
63
+
64
+ - `security(cli)`: eliminated `shell: true` from manifest-driven installs — closes a command-injection vector in `install`/`add` paths that took git URLs or shell-interpolated metadata.
65
+ - `security(logs)`: prompts and tokens are redacted before `events.jsonl` is written, and event retention is shortened from 30d to 7d. Reduces blast radius on accidental disclosure.
66
+ - `security(exec)`: strip loader env vars (`DYLD_*`, `LD_*`, `NODE_OPTIONS`) from environments propagated to child agents — avoids passing host-process loader state into spawned binaries.
67
+ - `security(browser)`: CDP origin allowlist replaces the previous wildcard — only `localhost` and explicitly configured browser hosts can speak CDP into a session.
68
+ - `security(ci)`: keychain helper SHA is verified at publish time, so a tampered helper binary cannot ride a release.
69
+
70
+ **Copilot (fix user-scoped MCP path)**
71
+
72
+ - Copilot's user-scoped MCP path now correctly resolves to `mcp-config.json` (the path the IDE actually reads) instead of the legacy filename. Fixes user-level MCP registrations not appearing in Copilot sessions.
73
+
74
+ **Docs**
75
+
76
+ - Full docs site IA shipped: browser, cloud, computer, hooks, plugins, profiles, pty, secrets, subagents, teams, workflows.
77
+ - Brand identity block: `agents-cli` is Phoenix Labs OSS, not part of the Rush brand — guards downstream agents against pulling Rush styling into this project.
78
+
79
+ **Build / install**
80
+
81
+ - Staged dev install tarball strips `prepack` and `prepare` hooks so side-by-side dev installs don't accidentally re-run the full publish pipeline locally.
82
+ - `test(jobs)`: un-break 3 stale assertions on main.
83
+
3
84
  ## 1.20.0
4
85
 
5
86
  **Routines (overdue detection + catchup)**
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
  <a href="https://github.com/phnx-labs/agents-cli"><img src="https://img.shields.io/badge/github-phnx--labs%2Fagents--cli-blue?style=flat-square" alt="github" /></a>
12
12
  </p>
13
13
 
14
- **The missing toolchain for CLI coding agents.** Pin versions to escape regressions. Build hooks to control agent behavior, or skills to improve them. Then share your agent environment with your team, or clone it to any machine with one command.
14
+ **The missing toolchain for CLI coding agents.** Run any agent on your existing subscription. Spawn parallel teams in isolated terminals. Schedule routines, drive browsers and Electron apps, and store secrets behind Touch ID all from one CLI.
15
15
 
16
16
  <p align="center">
17
17
  <a href="https://github.com/anthropics/claude-code" title="Claude Code"><img src="assets/harnesses/anthropic.svg" height="32" alt="Claude Code" /></a>
@@ -598,11 +598,11 @@ Every agent run, version install, browser launch, and secrets access is logged t
598
598
  }
599
599
  ```
600
600
 
601
- **What's logged:** Operation type, agent, version, timing, truncated prompts (first 200 chars), exit codes, errors, and secret bundle/key names with caller context. **What's NOT logged:** Full prompts, outputs, file contents, or secret values.
601
+ **What's logged:** Operation type, agent, version, timing, prompt length + SHA-256 hash (raw text never stored), exit codes, errors, and secret bundle/key names with caller context. Argv entries that look like tokens or secret paths are redacted. **What's NOT logged:** Raw prompts, outputs, file contents, or secret values.
602
602
 
603
603
  **Permissions:** Logs directory is `0700` (owner-only), files are `0600`. Only you can read them.
604
604
 
605
- **Retention:** 30 days by default, then auto-pruned.
605
+ **Retention:** 7 days by default, then auto-pruned.
606
606
 
607
607
  **Opt out:** Set `AGENTS_DISABLE_EVENT_LOG=1` in your shell to disable completely.
608
608
 
@@ -693,7 +693,7 @@ Your choice. We hand off to the original CLI process — use your existing subsc
693
693
 
694
694
  **No CLI telemetry or phone-home.** API keys come from your shell environment or each agent CLI's existing auth, and remote calls only happen when you invoke a feature that requires them, such as cloud dispatch.
695
695
 
696
- For full transparency: `agents-cli` keeps a local event log at `~/.agents/.cache/logs/` so you can see exactly what agents did on your machine. Logs are owner-readable only (0600) and auto-prune after 30 days. Set `AGENTS_DISABLE_EVENT_LOG=1` to disable. See [Security & Privacy](#security--privacy) for details.
696
+ For full transparency: `agents-cli` keeps a local event log at `~/.agents/.cache/logs/` so you can see exactly what agents did on your machine. Logs are owner-readable only (0600) and auto-prune after 7 days. Set `AGENTS_DISABLE_EVENT_LOG=1` to disable. See [Security & Privacy](#security--privacy) for details.
697
697
 
698
698
  ### Which platforms?
699
699
 
@@ -2,7 +2,7 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import chalk from 'chalk';
4
4
  import { confirm } from '@inquirer/prompts';
5
- import { listCliManifests, listCliStatus, resolveCliManifest, installCli, describeMethod, selectInstallMethod, isCliInstalled, } from '../lib/cli-resources.js';
5
+ import { listCliManifests, listCliStatus, resolveCliManifest, installCli, describeMethod, describeCheck, selectInstallMethod, isCliInstalled, } from '../lib/cli-resources.js';
6
6
  import { getUserAgentsDir } from '../lib/state.js';
7
7
  import { isPromptCancelled } from './utils.js';
8
8
  function userCliDir() {
@@ -163,7 +163,7 @@ When to use:
163
163
  }
164
164
  }
165
165
  else {
166
- console.log(chalk.yellow(` install command ran but \`${m.check}\` still fails — check the output above`));
166
+ console.log(chalk.yellow(` install command ran but \`${describeCheck(m.check)}\` still fails — check the output above`));
167
167
  failures++;
168
168
  }
169
169
  }
@@ -188,7 +188,7 @@ When to use:
188
188
  console.log(' ' + chalk.gray(`file: ${manifest.path}`));
189
189
  console.log('');
190
190
  console.log(chalk.bold(' check'));
191
- console.log(' ' + manifest.check);
191
+ console.log(' ' + describeCheck(manifest.check));
192
192
  console.log('');
193
193
  console.log(chalk.bold(' install methods'));
194
194
  for (const method of manifest.install) {
@@ -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.`));
@@ -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 { readAndResolveBundleEnv, 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;
@@ -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) {
@@ -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;
@@ -0,0 +1,89 @@
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 { spawnSync } from 'node:child_process';
7
+ import { arch, platform, release } from 'node:os';
8
+ import { createRequire } from 'node:module';
9
+ import chalk from 'chalk';
10
+ const REPO = 'phnx-labs/agents-cli';
11
+ const DISCUSSION_BASE = `https://github.com/${REPO}/discussions/new`;
12
+ const ISSUE_BASE = `https://github.com/${REPO}/issues/new`;
13
+ function readCliVersion() {
14
+ try {
15
+ const require = createRequire(import.meta.url);
16
+ const pkg = require('../../package.json');
17
+ return pkg.version ?? 'unknown';
18
+ }
19
+ catch {
20
+ return 'unknown';
21
+ }
22
+ }
23
+ function openInBrowser(url) {
24
+ const openers = process.platform === 'darwin'
25
+ ? [['open', [url]]]
26
+ : process.platform === 'win32'
27
+ ? [['cmd', ['/c', 'start', '""', url]]]
28
+ : [
29
+ ['xdg-open', [url]],
30
+ ['gnome-open', [url]]
31
+ ];
32
+ for (const [cmd, args] of openers) {
33
+ const r = spawnSync(cmd, args, { stdio: 'ignore' });
34
+ if (r.status === 0)
35
+ return true;
36
+ }
37
+ return false;
38
+ }
39
+ function buildPrefill(kind, summary) {
40
+ const version = readCliVersion();
41
+ const os = `${platform()} ${release()} (${arch()})`;
42
+ const node = process.version;
43
+ const body = [
44
+ summary.trim() ? `${summary.trim()}\n` : '<!-- describe what you ran into / what you want -->\n',
45
+ '---',
46
+ '',
47
+ '**Environment**',
48
+ '',
49
+ `- agents-cli: \`${version}\``,
50
+ `- OS: \`${os}\``,
51
+ `- Node: \`${node}\``,
52
+ '',
53
+ '<!-- For bugs: include the exact command you ran and the full output. -->',
54
+ '<!-- For ideas: include a concrete use case. -->'
55
+ ].join('\n');
56
+ if (kind === 'bug') {
57
+ const url = `${ISSUE_BASE}?template=bug_report.yml&title=${encodeURIComponent(summary || 'Bug: ')}`;
58
+ return { url, body };
59
+ }
60
+ const category = kind === 'idea' ? 'ideas' : 'q-a';
61
+ const url = `${DISCUSSION_BASE}?category=${category}&title=${encodeURIComponent(summary || '')}&body=${encodeURIComponent(body)}`;
62
+ return { url, body };
63
+ }
64
+ export function registerFeedbackCommand(program) {
65
+ program
66
+ .command('feedback [summary...]')
67
+ .description('Open a pre-filled feedback Discussion or bug report')
68
+ .option('-b, --bug', 'File as a bug report (opens issue tracker)')
69
+ .option('-i, --idea', 'File as a feature idea (Discussions → Ideas)')
70
+ .option('-q, --question', 'Ask a question (Discussions → Q&A)')
71
+ .option('--print', 'Print the URL instead of opening it')
72
+ .action((summary, opts) => {
73
+ const kind = opts.bug ? 'bug' : opts.idea ? 'idea' : 'question';
74
+ const summaryText = (summary ?? []).join(' ').trim();
75
+ const { url } = buildPrefill(kind, summaryText);
76
+ if (opts.print) {
77
+ console.log(url);
78
+ return;
79
+ }
80
+ const opened = openInBrowser(url);
81
+ if (opened) {
82
+ console.log(chalk.dim(`Opened ${kind} form in your browser:\n ${url}`));
83
+ }
84
+ else {
85
+ console.log(chalk.yellow('Could not auto-open a browser. Paste this URL:'));
86
+ console.log(` ${url}`);
87
+ }
88
+ });
89
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * `agents helper` -- install, inspect, and reinstall the signed macOS
3
+ * Keychain helper at the stable user path.
4
+ *
5
+ * The signed `Agents CLI.app` ships inside the npm package, but its keychain
6
+ * ACLs need a stable signature-pinned location to survive `npm i -g` and
7
+ * version bumps. This command copies it to
8
+ * `~/Library/Application Support/agents-cli/` once and lets users force a
9
+ * reinstall when the trusted-app ACL needs to be re-established.
10
+ */
11
+ import type { Command } from 'commander';
12
+ export declare function registerHelperCommand(program: Command): void;
@@ -0,0 +1,87 @@
1
+ /**
2
+ * `agents helper` -- install, inspect, and reinstall the signed macOS
3
+ * Keychain helper at the stable user path.
4
+ *
5
+ * The signed `Agents CLI.app` ships inside the npm package, but its keychain
6
+ * ACLs need a stable signature-pinned location to survive `npm i -g` and
7
+ * version bumps. This command copies it to
8
+ * `~/Library/Application Support/agents-cli/` once and lets users force a
9
+ * reinstall when the trusted-app ACL needs to be re-established.
10
+ */
11
+ import chalk from 'chalk';
12
+ import { ensureKeychainHelperInstalled, getKeychainHelperPath, getKeychainHelperStatus, } from '../lib/secrets/install-helper.js';
13
+ function requireDarwin() {
14
+ if (process.platform !== 'darwin') {
15
+ console.error(chalk.red('agents helper: macOS only.'));
16
+ process.exit(1);
17
+ }
18
+ }
19
+ export function registerHelperCommand(program) {
20
+ const cmd = program
21
+ .command('helper')
22
+ .description('Manage the signed macOS Keychain helper (.app) install');
23
+ cmd
24
+ .command('install')
25
+ .description('Copy the bundled .app to ~/Library/Application Support/agents-cli/')
26
+ .action(() => {
27
+ requireDarwin();
28
+ try {
29
+ ensureKeychainHelperInstalled({ forceReinstall: true });
30
+ }
31
+ catch (err) {
32
+ console.error(chalk.red(err.message || String(err)));
33
+ process.exit(1);
34
+ }
35
+ const s = getKeychainHelperStatus();
36
+ console.log(chalk.green('Installed:'), s.destination);
37
+ console.log(chalk.dim('codesign:'), s.codesignOk ? chalk.green('ok') : chalk.red(s.codesignOutput));
38
+ console.log(chalk.dim('spctl: '), s.spctlOk ? chalk.green('ok') : chalk.yellow(s.spctlOutput));
39
+ });
40
+ cmd
41
+ .command('update')
42
+ .description('Reinstall the .app, overwriting any existing copy (alias of install)')
43
+ .action(() => {
44
+ requireDarwin();
45
+ try {
46
+ ensureKeychainHelperInstalled({ forceReinstall: true });
47
+ }
48
+ catch (err) {
49
+ console.error(chalk.red(err.message || String(err)));
50
+ process.exit(1);
51
+ }
52
+ const s = getKeychainHelperStatus();
53
+ console.log(chalk.green('Updated: '), s.destination);
54
+ console.log(chalk.dim('codesign:'), s.codesignOk ? chalk.green('ok') : chalk.red(s.codesignOutput));
55
+ console.log(chalk.dim('spctl: '), s.spctlOk ? chalk.green('ok') : chalk.yellow(s.spctlOutput));
56
+ });
57
+ cmd
58
+ .command('status')
59
+ .description('Show source, destination, codesign and notarization status')
60
+ .action(() => {
61
+ requireDarwin();
62
+ const s = getKeychainHelperStatus();
63
+ console.log(chalk.bold('Source: '), s.source ?? chalk.red('(not found)'));
64
+ console.log(chalk.bold('Destination:'), s.destination);
65
+ console.log(chalk.bold('Installed: '), s.installed ? chalk.green('yes') : chalk.yellow('no'));
66
+ if (s.installed) {
67
+ console.log(chalk.bold('codesign: '), s.codesignOk ? chalk.green('ok') : chalk.red(s.codesignOutput));
68
+ console.log(chalk.bold('spctl: '), s.spctlOk ? chalk.green('ok') : chalk.yellow(s.spctlOutput));
69
+ }
70
+ else {
71
+ console.log(chalk.dim('Run `agents helper install` to copy the bundled .app to the destination.'));
72
+ }
73
+ });
74
+ cmd
75
+ .command('where')
76
+ .description('Print the absolute path to the installed helper executable')
77
+ .action(() => {
78
+ requireDarwin();
79
+ try {
80
+ console.log(getKeychainHelperPath());
81
+ }
82
+ catch (err) {
83
+ console.error(chalk.red(err.message || String(err)));
84
+ process.exit(1);
85
+ }
86
+ });
87
+ }