@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.
- package/CHANGELOG.md +81 -0
- package/README.md +4 -4
- package/dist/commands/cli.js +3 -3
- package/dist/commands/cloud.js +1 -1
- package/dist/commands/commands.js +24 -7
- package/dist/commands/exec.js +36 -16
- package/dist/commands/feedback.d.ts +7 -0
- package/dist/commands/feedback.js +89 -0
- package/dist/commands/helper.d.ts +12 -0
- package/dist/commands/helper.js +87 -0
- package/dist/commands/hooks.js +86 -7
- package/dist/commands/import.js +90 -37
- package/dist/commands/mcp.js +166 -10
- package/dist/commands/packages.js +196 -27
- package/dist/commands/permissions.js +21 -6
- package/dist/commands/profiles.d.ts +8 -0
- package/dist/commands/profiles.js +117 -4
- package/dist/commands/pull.js +4 -4
- package/dist/commands/routines.js +6 -6
- package/dist/commands/rules.js +8 -4
- package/dist/commands/secrets-migrate.d.ts +24 -0
- package/dist/commands/secrets-migrate.js +198 -0
- package/dist/commands/secrets-sync.d.ts +11 -0
- package/dist/commands/secrets-sync.js +155 -0
- package/dist/commands/secrets.js +74 -39
- package/dist/commands/skills.js +22 -5
- package/dist/commands/subagents.js +69 -49
- package/dist/commands/teams.js +48 -10
- package/dist/commands/utils.d.ts +33 -0
- package/dist/commands/utils.js +139 -0
- package/dist/commands/versions.js +4 -4
- package/dist/commands/view.d.ts +6 -0
- package/dist/commands/view.js +169 -8
- package/dist/commands/workflows.js +29 -6
- package/dist/index.js +4 -0
- package/dist/lib/acp/client.js +6 -1
- package/dist/lib/agents.d.ts +4 -0
- package/dist/lib/agents.js +41 -17
- package/dist/lib/auto-pull-worker.js +18 -1
- package/dist/lib/browser/chrome.js +4 -0
- package/dist/lib/browser/drivers/ssh.js +1 -1
- package/dist/lib/browser/profiles.d.ts +3 -3
- package/dist/lib/browser/profiles.js +3 -3
- package/dist/lib/browser/service.js +19 -0
- package/dist/lib/browser/types.d.ts +4 -4
- package/dist/lib/cli-resources.d.ts +36 -8
- package/dist/lib/cli-resources.js +268 -46
- package/dist/lib/cloud/factory.d.ts +1 -1
- package/dist/lib/cloud/factory.js +1 -1
- package/dist/lib/events.d.ts +16 -2
- package/dist/lib/events.js +33 -2
- package/dist/lib/exec.d.ts +39 -11
- package/dist/lib/exec.js +90 -31
- package/dist/lib/help.js +11 -5
- package/dist/lib/hooks/cache.d.ts +38 -0
- package/dist/lib/hooks/cache.js +242 -0
- package/dist/lib/hooks/profile.d.ts +33 -0
- package/dist/lib/hooks/profile.js +129 -0
- package/dist/lib/hooks.d.ts +0 -10
- package/dist/lib/hooks.js +68 -15
- package/dist/lib/import.d.ts +21 -0
- package/dist/lib/import.js +55 -2
- package/dist/lib/mcp.d.ts +15 -0
- package/dist/lib/mcp.js +40 -0
- package/dist/lib/permissions.d.ts +13 -0
- package/dist/lib/permissions.js +51 -1
- package/dist/lib/plugin-marketplace.d.ts +10 -0
- package/dist/lib/plugin-marketplace.js +47 -1
- package/dist/lib/plugins.js +15 -1
- package/dist/lib/profiles-presets.d.ts +26 -0
- package/dist/lib/profiles-presets.js +187 -8
- package/dist/lib/profiles.d.ts +34 -0
- package/dist/lib/profiles.js +112 -1
- package/dist/lib/pty-server.js +27 -3
- package/dist/lib/routines-format.d.ts +17 -5
- package/dist/lib/routines-format.js +37 -16
- package/dist/lib/routines.d.ts +1 -1
- package/dist/lib/routines.js +2 -2
- package/dist/lib/runner.js +64 -10
- package/dist/lib/secrets/Agents CLI.app/Contents/CodeResources +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/MacOS/Agents CLI +0 -0
- package/dist/lib/secrets/Agents CLI.app/Contents/_CodeSignature/CodeResources +1 -9
- package/dist/lib/secrets/bundles.d.ts +18 -22
- package/dist/lib/secrets/bundles.js +75 -99
- package/dist/lib/secrets/index.d.ts +51 -27
- package/dist/lib/secrets/index.js +147 -156
- package/dist/lib/secrets/install-helper.d.ts +45 -0
- package/dist/lib/secrets/install-helper.js +165 -0
- package/dist/lib/secrets/linux.js +4 -4
- package/dist/lib/secrets/sync.d.ts +56 -0
- package/dist/lib/secrets/sync.js +180 -0
- package/dist/lib/session/render.js +4 -4
- package/dist/lib/session/types.d.ts +1 -1
- package/dist/lib/shims.d.ts +4 -1
- package/dist/lib/shims.js +5 -35
- package/dist/lib/state.d.ts +14 -1
- package/dist/lib/state.js +49 -5
- package/dist/lib/teams/agents.d.ts +5 -4
- package/dist/lib/teams/agents.js +47 -21
- package/dist/lib/teams/api.d.ts +2 -1
- package/dist/lib/teams/api.js +4 -3
- package/dist/lib/types.d.ts +57 -1
- package/dist/lib/types.js +2 -0
- package/dist/lib/usage.d.ts +27 -2
- package/dist/lib/usage.js +100 -17
- package/dist/lib/versions.d.ts +35 -1
- package/dist/lib/versions.js +288 -64
- package/package.json +13 -12
- package/scripts/install-helper.js +97 -0
- package/scripts/postinstall.js +16 -0
- 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.**
|
|
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,
|
|
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:**
|
|
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
|
|
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
|
|
package/dist/commands/cli.js
CHANGED
|
@@ -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) {
|
package/dist/commands/cloud.js
CHANGED
|
@@ -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
|
|
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,
|
|
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 =
|
|
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
|
|
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
|
|
341
|
-
|
|
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.`));
|
package/dist/commands/exec.js
CHANGED
|
@@ -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 {
|
|
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
|
|
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),
|
|
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
|
|
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: ${
|
|
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
|
|
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: ${
|
|
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
|
+
}
|