@phnx-labs/agents-cli 1.20.57 → 1.20.58
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 +20 -0
- package/README.md +34 -3
- package/dist/bin/agents +0 -0
- package/dist/commands/defaults.js +24 -0
- package/dist/commands/exec.js +28 -4
- package/dist/commands/secrets.js +28 -19
- package/dist/commands/versions.js +11 -3
- package/dist/commands/view.js +19 -4
- package/dist/lib/agents.d.ts +21 -0
- package/dist/lib/agents.js +28 -4
- package/dist/lib/daemon.d.ts +5 -5
- package/dist/lib/daemon.js +65 -17
- package/dist/lib/git.d.ts +9 -0
- package/dist/lib/git.js +12 -0
- package/dist/lib/hosts/dispatch.d.ts +21 -0
- package/dist/lib/hosts/dispatch.js +88 -5
- package/dist/lib/permissions.d.ts +19 -1
- package/dist/lib/permissions.js +137 -0
- package/dist/lib/project-root.d.ts +65 -0
- package/dist/lib/project-root.js +133 -0
- package/dist/lib/resources/permissions.js +2 -0
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/secrets/agent.d.ts +26 -23
- package/dist/lib/secrets/agent.js +196 -216
- package/dist/lib/secrets/remote.js +1 -0
- package/dist/lib/session/active.d.ts +3 -0
- package/dist/lib/session/active.js +1 -0
- package/dist/lib/session/parse.js +38 -15
- package/dist/lib/session/state.d.ts +4 -1
- package/dist/lib/session/state.js +18 -1
- package/dist/lib/session/types.d.ts +8 -0
- package/dist/lib/staleness/detectors/permissions.js +42 -0
- package/dist/lib/staleness/detectors/subagents.js +30 -0
- package/dist/lib/staleness/writers/subagents.js +13 -1
- package/dist/lib/subagents.d.ts +22 -0
- package/dist/lib/subagents.js +146 -0
- package/dist/lib/teams/agents.d.ts +14 -0
- package/dist/lib/teams/agents.js +158 -22
- package/dist/lib/types.d.ts +13 -0
- package/dist/lib/versions.d.ts +39 -0
- package/dist/lib/versions.js +199 -12
- package/package.json +1 -1
- package/scripts/postinstall.js +26 -11
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.20.58
|
|
6
|
+
|
|
7
|
+
- **Self-updating agent CLIs are represented as one live installation.** `agents view` no longer invents version-home rows for single-binary installers such as Droid, Grok, Cursor, Kiro, Goose, and Hermes; it reports the version returned by the installed binary and folds away stale per-version directories. `agents add <agent>@<version>` now installs or keeps that agent's current release instead of rejecting an unsupported pinned install. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/versions.ts`, `apps/cli/src/commands/{versions,view}.ts`. (RUSH-1321)
|
|
8
|
+
- **Stopped teammate resumes are transactional from launch through persistence.** If a local or remote resume fails, the existing teammate record, directory, runtime metadata, stdout mirror, and log cursor are restored; any replacement wrapper and its descendants are terminated as one process group. A successful resume whose log was truncated restarts parsing at byte zero, and a secondary restore-write failure retains the original launch error as its cause. Source: `apps/cli/src/lib/teams/agents.ts`, `apps/cli/src/lib/hosts/dispatch.ts`. (#1104, #1108)
|
|
9
|
+
- **Wire allowlist support for Cursor CLI.** Cursor agent CLI stores allow/deny in `~/.cursor/cli-config.json` (`permissions.allow`/`deny` with Shell/Read/Write/WebFetch/Mcp). Flip `allowlist: true`, add `convertToCursorFormat` (Bash→Shell), and write via `applyPermissionsToVersion` + detector. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/permissions.ts`. (RUSH-1387)
|
|
10
|
+
- **GitHub Copilot CLI subagents now sync (RUSH-1390).** Installed subagents flatten into GitHub Copilot custom-agent profiles at `~/.copilot/agents/<name>.agent.md` (the Droid custom-droid format), gated to Copilot CLI ≥ 0.0.353. `agents subagents list/view` now surfaces synced Copilot agents and `agents subagents remove` soft-deletes their `.agent.md` files to trash — both previously skipped `copilot` entirely. Source: `apps/cli/src/lib/subagents.ts` (`listSubagentsForAgent`, `removeSubagentFromVersion`, `transformSubagentForCopilot`), `apps/cli/src/lib/staleness/writers/subagents.ts`, `apps/cli/src/lib/staleness/detectors/subagents.ts`, `apps/cli/src/lib/agents.ts`.
|
|
11
|
+
- **Menu-bar Quick Dispatch preserves typed drafts when focus is stolen (RUSH-1592).** If another app activates while the `Cmd-Shift-O` capture panel is open, the panel can hide without destroying the note; the next summon restores the draft text plus selected screenshots, action, and agents. Return submits and clears the draft; Escape clears without dispatching. Source: `apps/cli/menubar/Sources/MenubarHelper/PromptPanel.swift`, `apps/cli/docs/menubar.md`.
|
|
12
|
+
- **Wire subagents support for Kiro CLI.** Kiro custom agents are JSON files under `~/.kiro/agents/*.json` (introduced in kiro-cli v1.23.0). Flip Kiro's `subagents: { since: '1.23.0' }`, add `transformSubagentForKiro`, and wire the subagents writer, detector, install/remove, and orphan-detection paths. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/staleness/{writers,detectors}/subagents.ts`. (RUSH-1393)
|
|
13
|
+
- **Menu-bar ticket agents now carry every selected screenshot into the Linear issue (RUSH-1668).** `Cmd-Shift-O` already passed selected file paths to the ticket agent, but the prompt only asked it to inspect them, so agents could create text-only issues and stop. The brief now identifies every selected path as user-provided ticket material, requires each file to be uploaded, supplies the existing `linear update <id> --proof <path>` path as a reliable default, and leaves description/comment/other placement to the agent's judgment. Source: `apps/cli/menubar/Sources/MenubarHelper/{AgentsCLI,IssueSelfTest}.swift`, `apps/cli/docs/menubar.md`.
|
|
14
|
+
- **`agents sessions --active --json` now carries session attachment metadata for Factory previews (RUSH-1524).** Claude and Droid prompt image/document blocks that reference local files are preserved as `{ path, name, mediaType, sizeBytes }`, and the active-session state dedupes them into `attachments` so consumers can render screenshot thumbnails and open the original files instead of only seeing an attachment count. Source: `apps/cli/src/lib/session/parse.ts`, `apps/cli/src/lib/session/state.ts`, `apps/cli/src/lib/session/active.ts`.
|
|
15
|
+
- **Retired the standalone `com.phnx-labs.agents-secrets-agent` launchd service — the always-on daemon is now the sole broker host (#416, step 2).** `ensureAgentRunning()` no longer installs a separate launchd service: it retires any leftover plist via the new `retireLegacySecretsAgentService()` and relies on the daemon (Path 0), with a one-off detached broker as the only fallback. The upgrade migration (`scripts/postinstall.js` → `healLongRunningProcesses`) now `launchctl bootout`s the legacy service **first**, then (re)starts the daemon so it takes over the broker socket, instead of kickstarting the old service onto new code. `agents secrets start` is now a thin alias that brings the daemon up (and waits for the broker to answer); `agents secrets stop` locks all bundles and retires any leftover legacy service while leaving the always-on daemon running; `agents secrets status` reports broker reachability (daemon-hosted vs standalone) rather than "service installed". The stale broker teardown (version-skew self-heal) retires the legacy service instead of kickstarting it. Source: `apps/cli/src/lib/secrets/agent.ts` (`retireLegacySecretsAgentService`, `ensureAgentRunning`, `teardownStaleBroker`, `uninstallSecretsAgentService`; removed `installSecretsAgentService`/`kickstartSecretsAgentService`/`generateServicePlist`), `apps/cli/scripts/postinstall.js` (`healLongRunningProcesses`), `apps/cli/src/commands/secrets.ts` (`start`/`stop`/`status`).
|
|
16
|
+
|
|
17
|
+
- **Clarify the native escape hatch behind `--mode skip`.** The README and bundled `run` skill now discourage `skip`, list its exact direct-exec per-harness flag mappings and ACP `allow_always` behavior, replace an older recommendation of unsafe `full` for ordinary writes, and distinguish Codex `auto` (sandboxed `edit`, which can still prompt) from Codex `skip` (`--dangerously-bypass-approvals-and-sandbox`, equivalent to unsandboxed `--yolo`). Documentation only; runtime behavior is unchanged. Source: `README.md`, `skills/run/SKILL.md`.
|
|
18
|
+
- **Wire allowlist support for Kiro CLI.** Kiro 2.8.0+ permission groups now sync into `~/.kiro/settings/permissions.yaml` as v3 capability rules for shell, filesystem, and web access; existing user-authored rules are preserved and duplicate generated rules are removed. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/permissions.ts`, `apps/cli/src/lib/staleness/detectors/permissions.ts`. (RUSH-1392)
|
|
19
|
+
- **Fix: remote `agents secrets view <bundle>@host --reveal` no longer leaves a 60-second SSH control master behind.** The interactive `-tt` reveal path now opts out of default SSH multiplexing (`multiplex: false`), matching the transport guidance for one-shot commands that must not keep a `ControlPersist` socket open after a Touch ID/passphrase reveal. Source: `apps/cli/src/lib/secrets/remote.ts` (`remoteSecretsRaw`).
|
|
20
|
+
- **`agents run --host <host> --cwd <dir>` now sets the working directory ON the host (and a new `--project` shorthand jumps to a project by name).** Previously `--cwd` was silently dropped for `--host` runs — only the separate `--remote-cwd` flag worked — so `agents run claude --host s1 --cwd ~/src/foo` landed in the remote login-shell's default directory with no warning. `--cwd` is now forwarded as the host working directory, and a home-anchored path (`~/…`, `$HOME/…`, or a local-home absolute the shell already expanded like `/Users/me/…`) is re-rooted at the *remote* `$HOME` so it resolves correctly across machines with different home paths (`/Users/me` → `/home/me`). `--remote-cwd` remains as the explicit override. New `-P, --project <slug>[@worktree]` resolves a bare project name against your projects root (e.g. `~/src/github.com/<user>`) — auto-inferred from the repo you launch inside and cached in `agents.yaml`, or set/shown with `agents defaults project-root [path]`; `--project foo@fix` targets the `fix` git worktree. Works for local and `--host` runs. Verified end-to-end: `agents run claude --host yosemite-s1 --project agents-cli` runs the remote agent with `pwd` = `/home/muqsit/src/github.com/muqsitnawaz/agents-cli`. Source: `apps/cli/src/lib/project-root.ts` (new), `apps/cli/src/lib/hosts/dispatch.ts` (`remoteCdPrefix`), `apps/cli/src/commands/exec.ts` (`--project`/`--cwd` host wiring), `apps/cli/src/commands/defaults.ts` (`project-root`).
|
|
21
|
+
- **Fix daemon crash-looping when its pinned Node version is pruned (fleet-wide).** The routine daemon's launchd/systemd manifest hardcoded `~/.nvm/versions/node/v24.0.0/bin` on PATH, and launched the CLI entry bare when it was an extension-less shim or a `bin/agents → dist/index.js` symlink (an extension check on the link name missed it). The moment that exact nvm patch was upgraded away, the shim's `#!/usr/bin/env node` shebang fell through to an ancient system node (Node 18 → `SyntaxError: node:util has no export 'styleText'` from `@inquirer/core`), and the service crash-looped at import — observed at 100k+ restarts on Linux workers, silently killing all scheduled routines. `getDaemonLaunch` now detects Node-script entries by resolving symlinks and sniffing the shebang (not just the `.js`/`.cjs`/`.mjs` extension), so it pins them to `process.execPath`; and the generated PATH now leads with `path.dirname(process.execPath)` — the Node that installed the service — instead of a hardcoded nvm version, so both the shim and child routine processes always resolve a working runtime. Source: `apps/cli/src/lib/daemon.ts` (`getDaemonLaunch`, `isNodeScriptEntry`, `daemonNodeBinDir`, `generateSystemdUnit`, `generateLaunchdPlist`).
|
|
22
|
+
- **Fix global npm upgrades restarting the routines daemon through `scripts/postinstall.js`.** The postinstall process is itself `process.argv[1]`, so its daemon self-heal could stamp `node scripts/postinstall.js daemon _run` into launchd. Daemon startup now accepts an explicit CLI entry and postinstall passes the resolved signed native binary (or JavaScript entrypoint), with the same value threaded through launchd, systemd, and detached startup. Source: `apps/cli/scripts/postinstall.js`, `apps/cli/src/lib/daemon.ts`.
|
|
23
|
+
- **Fix a standalone secrets service stealing the daemon-hosted broker socket during postinstall.** The standalone and hosted brokers now bind through one race-safe owner arbitration path: an existing reachable broker wins without its socket being unlinked, a persistent losing service stays quiescent instead of triggering launchd restart churn, takes over if the owner stops, and releases its standby PID on service shutdown; only an unreachable stale socket is reclaimed. This covers the release ordering where postinstall restarts the daemon first and then kickstarts an installed standalone service. Source: `apps/cli/src/lib/secrets/agent.ts` (`bindBrokerSocket`, `runSecretsAgent`, `startHostedBroker`).
|
|
24
|
+
|
|
5
25
|
## 1.20.57
|
|
6
26
|
|
|
7
27
|
- **`agents teams resume` / `agents teams message` — resume a stopped teammate with a follow-up message.** A teammate that ended its turn with more to do (PR open awaiting review, headless turn cap, a redirect after the fact) could not be reached: `agents message` resolves only *live* sessions, so a completed/stopped/failed teammate had no path back short of finishing the work by hand or spawning a fresh, context-less teammate. `teams resume <team> <teammate> <message>` re-enters the teammate's **own** session with the message as the next user turn, re-launching through the same backend (local process or remote host) in its original worktree and flipping it back to `running` so `teams status` tracks it live. `teams message` is the same command with automatic routing by reconciled status: a **running** teammate is steered via its mailbox (delivered at its next tool call, no re-launch); a **stopped** one is resumed; a **pending** one is refused with a pointer to `teams start`. Works for every harness — the resume delegates to `agents run --resume`, inheriting native resume for Claude/Codex and the universal `/continue` replay for the rest (OpenCode, Grok, Kimi, …); the resume target is the teammate's captured underlying session id (`remoteSessionId ?? agentId`), and a non-Claude teammate that died before emitting a session id is refused with a clear error rather than resumed into a fresh run. This also makes good on `teams stop`'s long-standing "can be restarted later" promise, which no code implemented. Source: `apps/cli/src/commands/teams.ts` (`message`/`resume` subcommands, `decideTeamMessageRoute`), `apps/cli/src/lib/teams/agents.ts` (`AgentManager.resumeTeammate`, resume-aware `buildRunArgv`/`buildCommand`/`launchProcess`/`launchRemoteProcess`).
|
package/README.md
CHANGED
|
@@ -155,7 +155,37 @@ agents run claude "Review PRs merged this week, summarize risks" \
|
|
|
155
155
|
| agents run codex "Write regression tests for the top 3 risks"
|
|
156
156
|
```
|
|
157
157
|
|
|
158
|
-
Supports plan (read-only) and
|
|
158
|
+
Supports plan (read-only), edit, auto, and skip modes, effort levels, JSON output for scripting, and timeout limits.
|
|
159
|
+
|
|
160
|
+
### What does `--mode skip` actually do?
|
|
161
|
+
|
|
162
|
+
Treat `skip` as a last-resort escape hatch. In direct-exec runs (without `--acp`),
|
|
163
|
+
agents-cli forwards the harness's native no-prompt flag; it does not add another
|
|
164
|
+
safety layer. Prefer `auto` where the harness has a smart classifier (Claude Code and
|
|
165
|
+
GitHub Copilot), or `edit` everywhere else. Harnesses without a native bypass flag
|
|
166
|
+
reject direct-exec `skip`.
|
|
167
|
+
|
|
168
|
+
| Harness | Direct-exec `--mode skip` becomes |
|
|
169
|
+
|---|---|
|
|
170
|
+
| Claude Code | `--dangerously-skip-permissions` |
|
|
171
|
+
| Codex | `--dangerously-bypass-approvals-and-sandbox` (equivalent to `--yolo`) |
|
|
172
|
+
| Gemini | `--yolo` |
|
|
173
|
+
| Cursor | `-f` |
|
|
174
|
+
| OpenClaw | `--mode full` |
|
|
175
|
+
| GitHub Copilot | `--allow-all` (alias: `--yolo`) |
|
|
176
|
+
| Antigravity | `--dangerously-skip-permissions` |
|
|
177
|
+
| Grok | `--always-approve` |
|
|
178
|
+
| Kimi | `--yolo` interactively; no extra flag in headless `-p` runs, which already auto-approve |
|
|
179
|
+
| Droid | `--skip-permissions-unsafe` |
|
|
180
|
+
|
|
181
|
+
With `--acp`, these native flags are not used. agents-cli instead grants `skip`
|
|
182
|
+
permission requests at the ACP protocol layer with `allow_always`; the same
|
|
183
|
+
last-resort warning applies.
|
|
184
|
+
|
|
185
|
+
Codex has no native smart-classifier mode, so `agents run codex --mode auto` resolves
|
|
186
|
+
to sandboxed `edit` and can still prompt. `agents run codex --mode skip` is different:
|
|
187
|
+
it bypasses approvals **and** removes the sandbox. `full` remains an alias for `skip`,
|
|
188
|
+
but new scripts should use the explicit `skip` name.
|
|
159
189
|
|
|
160
190
|
### One protocol, every harness
|
|
161
191
|
|
|
@@ -432,7 +462,7 @@ tools:
|
|
|
432
462
|
---
|
|
433
463
|
```
|
|
434
464
|
|
|
435
|
-
Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit
|
|
465
|
+
Workflows that need to write — post PR comments, edit files, send Slack — should run with `--mode edit`, or `--mode auto` on Claude Code and GitHub Copilot. Reserve `--mode skip` (legacy alias: `full`) for last-resort bypasses. `agents run` defaults to `--mode plan` (read-only), which deadlocks at `ExitPlanMode` in headless runs.
|
|
436
466
|
|
|
437
467
|
Resolution is project > user > system: a `<repo>/.agents/workflows/<name>/` overrides a same-named workflow in `~/.agents/workflows/`. Commit project workflows with your repo so teammates get the same pipeline.
|
|
438
468
|
|
|
@@ -825,7 +855,7 @@ Which DotAgents resources each agent CLI can load. Source of truth: [src/lib/age
|
|
|
825
855
|
| OpenCode | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
|
|
826
856
|
| Copilot | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
|
|
827
857
|
| Amp | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
|
|
828
|
-
| Kiro | yes | no | yes |
|
|
858
|
+
| Kiro | yes | no | yes | >= 2.8.0 | yes | yes | no | no | `AGENTS.md` | no |
|
|
829
859
|
| Goose | yes | no | yes | no | no | no | no | no | `AGENTS.md` | no |
|
|
830
860
|
| Roo Code | yes | no | yes | no | yes | yes | no | no | `AGENTS.md` | no |
|
|
831
861
|
|
|
@@ -853,6 +883,7 @@ Which DotAgents resources each agent CLI can load. Source of truth: [src/lib/age
|
|
|
853
883
|
|------------|-------|------|
|
|
854
884
|
| Hooks | Codex | >= 0.116.0 |
|
|
855
885
|
| Hooks | Gemini | >= 0.26.0 |
|
|
886
|
+
| Permissions | Kiro | >= 2.8.0 |
|
|
856
887
|
| File-based commands | Codex | < 0.117.0 (0.117+ uses command-as-skill) |
|
|
857
888
|
| Plugins | Codex | >= 0.128.0 |
|
|
858
889
|
|
package/dist/bin/agents
CHANGED
|
Binary file
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
import chalk from 'chalk';
|
|
7
7
|
import { setHelpSections } from '../lib/help.js';
|
|
8
8
|
import { listRunDefaults, setRunDefault, unsetRunDefault, } from '../lib/run-defaults.js';
|
|
9
|
+
import { getProjectRoot, setProjectRoot } from '../lib/project-root.js';
|
|
9
10
|
function formatRunDefault(entry) {
|
|
10
11
|
const parts = [];
|
|
11
12
|
if (entry.defaults.mode)
|
|
@@ -86,4 +87,27 @@ export function registerDefaultsCommands(program) {
|
|
|
86
87
|
process.exit(1);
|
|
87
88
|
}
|
|
88
89
|
});
|
|
90
|
+
defaults
|
|
91
|
+
.command('project-root [path]')
|
|
92
|
+
.description('Show or set the projects root for `agents run --project` (auto-inferred when unset)')
|
|
93
|
+
.action((rootPath) => {
|
|
94
|
+
try {
|
|
95
|
+
if (!rootPath) {
|
|
96
|
+
const current = getProjectRoot();
|
|
97
|
+
if (current) {
|
|
98
|
+
console.log(`Projects root: ${chalk.white(current)}`);
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
console.log(chalk.gray('Projects root not set — auto-inferred and cached on first `--project` use.'));
|
|
102
|
+
}
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const stored = setProjectRoot(rootPath);
|
|
106
|
+
console.log(chalk.green(`Set projects root: ${stored}`));
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
console.error(chalk.red(err.message));
|
|
110
|
+
process.exit(1);
|
|
111
|
+
}
|
|
112
|
+
});
|
|
89
113
|
}
|
package/dist/commands/exec.js
CHANGED
|
@@ -211,7 +211,8 @@ export function registerRunCommand(program) {
|
|
|
211
211
|
.option('--no-auto-secrets', 'Skip auto-injection of secrets declared by a workflow\'s frontmatter `secrets:` field. Has no effect on bare-agent runs.')
|
|
212
212
|
.option('--secrets-keys <keys>', 'Inject only this comma-separated subset of keys from --secrets bundles (e.g. KEY1,KEY2). Missing keys are an error. Applies to all --secrets bundles on this run.')
|
|
213
213
|
.option('--allow-expired', 'Inject secrets even if their expiry date has passed (overrides the pre-run expiry abort).')
|
|
214
|
-
.option('--cwd <dir>', 'Working directory for the agent (defaults to current directory)')
|
|
214
|
+
.option('--cwd <dir>', 'Working directory for the agent (defaults to current directory). With --host, the directory ON the host.')
|
|
215
|
+
.option('-P, --project <ref>', 'Project shorthand <slug>[@worktree], resolved against your projects root (auto-inferred, cached). Sets the cwd locally or on --host.')
|
|
215
216
|
.option('--add-dir <dir>', 'Grant access to an additional directory outside the project (Claude and Codex, repeatable)', (val, prev) => [...prev, val], [])
|
|
216
217
|
.option('--json', 'Stream events as JSON lines (for parsing by other tools)')
|
|
217
218
|
.option('--quiet', 'Suppress preamble (rotation banner, "Running:" line). Useful when piping JSON events to a parser.', false)
|
|
@@ -238,7 +239,7 @@ export function registerRunCommand(program) {
|
|
|
238
239
|
.option('--interval <dur>', 'Loop delay between iterations ("0" back-to-back, "30m" paces). Loop only.')
|
|
239
240
|
.option('--host <name>', 'Offload this run onto another machine over SSH instead of running locally — a device, a registered agent host, or user@host. See `agents devices` / `agents hosts`.')
|
|
240
241
|
.option('--device <name>', 'Alias of --host: offload this run onto a registered device (from `agents devices`).')
|
|
241
|
-
.option('--remote-cwd <dir>', '
|
|
242
|
+
.option('--remote-cwd <dir>', 'Explicit host working directory for --host runs (overrides --cwd; usually --cwd suffices).')
|
|
242
243
|
.option('--no-follow', 'With --host, dispatch detached and return immediately (track via `agents hosts ps/logs`).')
|
|
243
244
|
.option('--any', 'With --host <cap> (a capability tag), pick any matching host instead of erroring when several match.')
|
|
244
245
|
.option('--lease [backend]', 'Invent a disposable cloud box for this run and tear it down after (via crabbox). Optional backend selects the cloud (hetzner/aws/do). Unlike --host, no machine is registered.')
|
|
@@ -454,6 +455,24 @@ export function registerRunCommand(program) {
|
|
|
454
455
|
// --host/--on/--computer: offload this run onto a registered agent host
|
|
455
456
|
// over SSH instead of running locally. The three flags are aliases.
|
|
456
457
|
const hostGiven = [options.host, options.device, options.on, options.computer].filter((v) => !!v);
|
|
458
|
+
// --project <slug>[@worktree]: resolve the projects-root shorthand into a
|
|
459
|
+
// cwd. On a host run it resolves home-relative (`~/…`, so the host expands
|
|
460
|
+
// it); locally it becomes an absolute path. It owns the working directory,
|
|
461
|
+
// so it is mutually exclusive with both --cwd and --remote-cwd.
|
|
462
|
+
if (options.project) {
|
|
463
|
+
if (options.cwd || options.remoteCwd) {
|
|
464
|
+
console.error(chalk.red('Pass --project alone — not with --cwd or --remote-cwd.'));
|
|
465
|
+
process.exit(1);
|
|
466
|
+
}
|
|
467
|
+
const { resolveProjectRef } = await import('../lib/project-root.js');
|
|
468
|
+
try {
|
|
469
|
+
options.cwd = await resolveProjectRef(options.project, { forRemote: hostGiven.length > 0 });
|
|
470
|
+
}
|
|
471
|
+
catch (err) {
|
|
472
|
+
console.error(chalk.red(err.message));
|
|
473
|
+
process.exit(1);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
457
476
|
if (hostGiven.length > 0) {
|
|
458
477
|
if (new Set(hostGiven).size > 1) {
|
|
459
478
|
console.error(chalk.red('Conflicting --host/--device values — pass just one.'));
|
|
@@ -488,6 +507,11 @@ export function registerRunCommand(program) {
|
|
|
488
507
|
}
|
|
489
508
|
try {
|
|
490
509
|
const runAgent = agentSpec.split('@')[0];
|
|
510
|
+
// Working directory on the host: an explicit --remote-cwd is used
|
|
511
|
+
// verbatim; --cwd/--project are made portable (a local-home absolute
|
|
512
|
+
// becomes `~/…` so the remote shell re-roots it at ITS home).
|
|
513
|
+
const { toRemotePortable } = await import('../lib/project-root.js');
|
|
514
|
+
const hostCwd = options.remoteCwd ?? (options.cwd ? toRemotePortable(options.cwd) : undefined);
|
|
491
515
|
// `--resume [id]`: commander yields the string id, or `true` when the
|
|
492
516
|
// flag is passed bare. A bare resume needs the interactive picker,
|
|
493
517
|
// which can't run over a detached remote dispatch — only forward a
|
|
@@ -528,7 +552,7 @@ export function registerRunCommand(program) {
|
|
|
528
552
|
prompt,
|
|
529
553
|
mode: options.mode,
|
|
530
554
|
model: options.model,
|
|
531
|
-
remoteCwd:
|
|
555
|
+
remoteCwd: hostCwd,
|
|
532
556
|
sessionId: hostSessionId,
|
|
533
557
|
name: options.name,
|
|
534
558
|
resume: resumeId,
|
|
@@ -550,7 +574,7 @@ export function registerRunCommand(program) {
|
|
|
550
574
|
prompt,
|
|
551
575
|
mode: options.mode,
|
|
552
576
|
model: options.model,
|
|
553
|
-
remoteCwd:
|
|
577
|
+
remoteCwd: hostCwd,
|
|
554
578
|
sessionId: hostSessionId,
|
|
555
579
|
name: options.name,
|
|
556
580
|
resume: resumeId,
|
package/dist/commands/secrets.js
CHANGED
|
@@ -12,14 +12,14 @@ import { terminalWidth, truncateToWidth, stringWidth } from '../lib/session/widt
|
|
|
12
12
|
import * as fs from 'fs';
|
|
13
13
|
import { SSH_TARGET_RE, assertValidSshTarget, sshExec } from '../lib/ssh-exec.js';
|
|
14
14
|
import { quoteWin32ExecArg, composeWin32CommandLine } from '../lib/platform/index.js';
|
|
15
|
-
import { ensureDaemonStarted } from '../lib/daemon.js';
|
|
15
|
+
import { ensureDaemonStarted, isDaemonRunning } from '../lib/daemon.js';
|
|
16
16
|
import { parseHostsOption, remoteResolveEnv, remoteSecretsRaw, remoteSecretsStream, resolveSshTarget, } from '../lib/secrets/remote.js';
|
|
17
17
|
import { remoteShellFor, buildWindowsStdinImportCommand } from '../lib/hosts/remote-cmd.js';
|
|
18
18
|
import { resolveRemoteOsSync } from '../lib/hosts/remote-os.js';
|
|
19
19
|
import { bundleExists, bundleItemStore, bundlePolicy, deleteBundle, describeBundle, keychainItemsForBundle, keychainRef, listBundles, migrateLegacyBundles, parseDotenv, readAndResolveBundleEnv, readBundle, renameBundle, rotateBundleSecret, sanitizeProcessEnv, validateBundleName, validateEnvKey, validateExpiresFutureDated, validateSecretType, writeBundle, } from '../lib/secrets/bundles.js';
|
|
20
20
|
import { getKeychainToken, getKeychainTokens, hasKeychainToken, secretsKeychainItem, setKeychainToken, } from '../lib/secrets/index.js';
|
|
21
21
|
import { assertOpAvailable, createPasswordItem, deleteItemByTitle, extractSecrets, itemExistsByTitle, listItems, listVaults, } from '../lib/onepassword.js';
|
|
22
|
-
import { DEFAULT_TTL_MS, agentLoad, agentLock, agentStatus, ensureAgentRunning,
|
|
22
|
+
import { DEFAULT_TTL_MS, agentLoad, agentLock, agentPing, agentStatus, ensureAgentRunning, runAgentLoadFromStdin, runSecretsAgent, uninstallSecretsAgentService, } from '../lib/secrets/agent.js';
|
|
23
23
|
import { parseDuration } from '../lib/hooks/cache.js';
|
|
24
24
|
import { emit } from '../lib/events.js';
|
|
25
25
|
import { registerCommandGroups, setHelpSections } from '../lib/help.js';
|
|
@@ -1770,13 +1770,13 @@ Examples:
|
|
|
1770
1770
|
ttlMs = secs * 1000;
|
|
1771
1771
|
}
|
|
1772
1772
|
if (!(await ensureAgentRunning())) {
|
|
1773
|
-
console.error(chalk.red('Could not start the secrets
|
|
1773
|
+
console.error(chalk.red('Could not start the secrets broker.'));
|
|
1774
1774
|
process.exit(1);
|
|
1775
1775
|
}
|
|
1776
1776
|
// #415: the daemon should be always-on for any background need, not only
|
|
1777
|
-
// after `routines add`.
|
|
1778
|
-
// the
|
|
1779
|
-
//
|
|
1777
|
+
// after `routines add`. `ensureAgentRunning` prefers the daemon (it hosts
|
|
1778
|
+
// the broker, #416), but can fall back to a one-off broker spawn when the
|
|
1779
|
+
// daemon can't come up — so ensure the daemon is up regardless. Idempotent
|
|
1780
1780
|
// (single-instance start lock, #414) and best-effort — never blocks unlock.
|
|
1781
1781
|
ensureDaemonStarted();
|
|
1782
1782
|
let loaded = 0;
|
|
@@ -1830,13 +1830,14 @@ Examples:
|
|
|
1830
1830
|
console.log(chalk.gray('secrets-agent is macOS-only.'));
|
|
1831
1831
|
return;
|
|
1832
1832
|
}
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1833
|
+
const brokerUp = (await agentPing()).reachable;
|
|
1834
|
+
console.log(chalk.gray('broker: ') +
|
|
1835
|
+
(brokerUp
|
|
1836
|
+
? chalk.green('running') + chalk.gray(isDaemonRunning() ? ' (hosted by the daemon)' : ' (standalone)')
|
|
1837
|
+
: chalk.yellow('not running — starts on demand, or run `agents secrets start` to bring the daemon up now')));
|
|
1837
1838
|
const entries = await agentStatus();
|
|
1838
1839
|
if (entries.length === 0) {
|
|
1839
|
-
console.log(chalk.gray('No bundles unlocked. The secrets
|
|
1840
|
+
console.log(chalk.gray('No bundles unlocked. The secrets broker is idle or not running.'));
|
|
1840
1841
|
console.log(chalk.gray('Try: agents secrets unlock <bundle>'));
|
|
1841
1842
|
return;
|
|
1842
1843
|
}
|
|
@@ -1887,29 +1888,37 @@ Examples:
|
|
|
1887
1888
|
});
|
|
1888
1889
|
cmd
|
|
1889
1890
|
.command('start')
|
|
1890
|
-
.description('
|
|
1891
|
+
.description('Bring up the always-on daemon that hosts the secrets broker (macOS). Survives heavy load; reads connect instantly.')
|
|
1891
1892
|
.action(async () => {
|
|
1892
1893
|
if (process.platform !== 'darwin') {
|
|
1893
|
-
console.error(chalk.red('secrets
|
|
1894
|
+
console.error(chalk.red('The secrets broker is macOS-only.'));
|
|
1894
1895
|
process.exit(1);
|
|
1895
1896
|
}
|
|
1896
|
-
process.stdout.write(chalk.gray('
|
|
1897
|
-
|
|
1898
|
-
|
|
1897
|
+
process.stdout.write(chalk.gray('Starting the daemon…\n'));
|
|
1898
|
+
ensureDaemonStarted();
|
|
1899
|
+
// The daemon hosts the broker socket-first; wait briefly for it to answer.
|
|
1900
|
+
const deadline = Date.now() + 10000;
|
|
1901
|
+
let reachable = (await agentPing()).reachable;
|
|
1902
|
+
while (!reachable && Date.now() < deadline) {
|
|
1903
|
+
await new Promise((r) => setTimeout(r, 200));
|
|
1904
|
+
reachable = (await agentPing()).reachable;
|
|
1905
|
+
}
|
|
1906
|
+
if (reachable) {
|
|
1907
|
+
console.log(chalk.green('secrets broker running.') + chalk.gray(' Hosted by the always-on daemon; unlock/auto-cache now connect instantly.'));
|
|
1899
1908
|
}
|
|
1900
1909
|
else {
|
|
1901
|
-
console.error(chalk.red('
|
|
1910
|
+
console.error(chalk.red('Daemon started but the broker did not become reachable in time (machine may be heavily loaded — it will keep retrying).'));
|
|
1902
1911
|
process.exit(1);
|
|
1903
1912
|
}
|
|
1904
1913
|
});
|
|
1905
1914
|
cmd
|
|
1906
1915
|
.command('stop')
|
|
1907
|
-
.description('
|
|
1916
|
+
.description('Lock all bundles and retire any legacy standalone service. The always-on daemon (which hosts the broker) is left running.')
|
|
1908
1917
|
.action(async () => {
|
|
1909
1918
|
if (process.platform !== 'darwin')
|
|
1910
1919
|
return;
|
|
1911
1920
|
await uninstallSecretsAgentService();
|
|
1912
|
-
console.log(chalk.green('
|
|
1921
|
+
console.log(chalk.green('Locked all bundles.') + chalk.gray(' The broker stays hosted by the always-on daemon; a legacy standalone service, if any, was retired.'));
|
|
1913
1922
|
});
|
|
1914
1923
|
cmd
|
|
1915
1924
|
.command('_agent-run', { hidden: true })
|
|
@@ -4,7 +4,7 @@ import ora from 'ora';
|
|
|
4
4
|
import * as fs from 'fs';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { select, confirm, checkbox } from '@inquirer/prompts';
|
|
7
|
-
import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, warnAgentDeprecated, } from '../lib/agents.js';
|
|
7
|
+
import { AGENTS, ALL_AGENT_IDS, getAccountEmail, getAccountInfo, agentLabel, warnAgentDeprecated, isSelfUpdatingAgent, } from '../lib/agents.js';
|
|
8
8
|
import { formatUsageSummary, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
9
9
|
import { viewAction } from './view.js';
|
|
10
10
|
import { readManifest, writeManifest, createDefaultManifest } from '../lib/manifest.js';
|
|
@@ -67,7 +67,7 @@ async function setDefaultVersion(agent, installedVersion) {
|
|
|
67
67
|
createVersionedAlias(agent, installedVersion);
|
|
68
68
|
const symlinkResult = await switchConfigSymlink(agent, installedVersion);
|
|
69
69
|
if (symlinkResult.success) {
|
|
70
|
-
console.log(chalk.green(' Set as default'));
|
|
70
|
+
console.log(chalk.green(isSelfUpdatingAgent(agent) ? ' Set as active config profile' : ' Set as default'));
|
|
71
71
|
if (symlinkResult.backupPath) {
|
|
72
72
|
console.log(chalk.gray(` Backed up existing config to: ${symlinkResult.backupPath}`));
|
|
73
73
|
}
|
|
@@ -752,7 +752,15 @@ export function registerVersionsCommands(program) {
|
|
|
752
752
|
warnIfShimShadowed(agentId);
|
|
753
753
|
const useEmail = await getAccountEmail(agentId, getVersionHomePath(agentId, finalVersion));
|
|
754
754
|
const useEmailStr = useEmail ? chalk.cyan(` (${useEmail})`) : '';
|
|
755
|
-
|
|
755
|
+
// Self-updating agents are one binary; `use` only swaps the config
|
|
756
|
+
// symlink (the profile), it does not change which binary runs — so say
|
|
757
|
+
// "profile", not "version", to match what actually happened.
|
|
758
|
+
if (isSelfUpdatingAgent(agentId)) {
|
|
759
|
+
console.log(chalk.green(`Switched ${agentLabel(agentConfig.id)} to config profile ${finalVersion}`) + useEmailStr);
|
|
760
|
+
}
|
|
761
|
+
else {
|
|
762
|
+
console.log(chalk.green(`Set ${agentLabel(agentConfig.id)}@${finalVersion} as global default`) + useEmailStr);
|
|
763
|
+
}
|
|
756
764
|
}
|
|
757
765
|
}
|
|
758
766
|
catch (err) {
|
package/dist/commands/view.js
CHANGED
|
@@ -7,7 +7,7 @@ import * as path from 'path';
|
|
|
7
7
|
import { AGENTS, ALL_AGENT_IDS, getAllCliStates, getAccountInfo, resolveAgentName, formatAgentError, agentLabel, colorAgent, } from '../lib/agents.js';
|
|
8
8
|
import { deriveUsageStatusFromSnapshot, formatUsageSection, formatUsageSummary, formatUsageStatusBadge, getUsageInfoForIdentity, getUsageInfoByIdentity, getUsageLookupKey, } from '../lib/usage.js';
|
|
9
9
|
import { readManifest } from '../lib/manifest.js';
|
|
10
|
-
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, } from '../lib/versions.js';
|
|
10
|
+
import { listInstalledVersions, listInstalledVersionDirs, getGlobalDefault, getVersionHomePath, getVersionDir, getAvailableResources, getActuallySyncedResources, getNewResources, getProjectOnlyResources, hasNewResources, promptNewResourceSelection, syncResourcesToVersion, removeVersion, printTrashFooter, reconcileStaleLatestForAgent, isGlobalBinaryAgent, getLiveVersion, } from '../lib/versions.js';
|
|
11
11
|
import { ensureVersionedAliasCurrent, removeShim, } from '../lib/shims.js';
|
|
12
12
|
import { getAgentResources } from '../lib/resources.js';
|
|
13
13
|
import { resolveVersionFilter, AgentSpecError } from '../lib/agent-spec/index.js';
|
|
@@ -358,6 +358,19 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
358
358
|
profileOnly.push(agentId);
|
|
359
359
|
}
|
|
360
360
|
}
|
|
361
|
+
// For self-updating global-binary agents (droid) the on-disk version-dir name
|
|
362
|
+
// is a stale label — the real version is whatever `<cli> --version` reports.
|
|
363
|
+
// Resolve it once so every row/width pass shows the live version, while the
|
|
364
|
+
// per-version home + account lookups keep using the real dir name.
|
|
365
|
+
const liveVersionByAgent = new Map();
|
|
366
|
+
await Promise.all(versionManaged
|
|
367
|
+
.filter((agentId) => isGlobalBinaryAgent(agentId))
|
|
368
|
+
.map(async (agentId) => {
|
|
369
|
+
const live = await getLiveVersion(agentId);
|
|
370
|
+
if (live)
|
|
371
|
+
liveVersionByAgent.set(agentId, live);
|
|
372
|
+
}));
|
|
373
|
+
const displayVersion = (agentId, dirVersion) => liveVersionByAgent.get(agentId) ?? dirVersion;
|
|
361
374
|
// Show version-managed agents
|
|
362
375
|
if (versionManaged.length > 0) {
|
|
363
376
|
// Calculate column widths across all agents for alignment
|
|
@@ -370,7 +383,8 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
370
383
|
const versions = listInstalledVersions(agentId);
|
|
371
384
|
const globalDefault = getGlobalDefault(agentId);
|
|
372
385
|
for (const v of versions) {
|
|
373
|
-
const
|
|
386
|
+
const shown = displayVersion(agentId, v);
|
|
387
|
+
const label = v === globalDefault ? `${shown} (default)` : shown;
|
|
374
388
|
maxVerLabel = Math.max(maxVerLabel, label.length);
|
|
375
389
|
const rawInfo = infoMap.get(`${agentId}:${v}`);
|
|
376
390
|
const info = rawInfo ? mergeCanonical(rawInfo) : undefined;
|
|
@@ -423,9 +437,10 @@ async function showInstalledVersions(filterAgentId) {
|
|
|
423
437
|
});
|
|
424
438
|
for (const version of sortedVersions) {
|
|
425
439
|
const isDefault = version === globalDefault;
|
|
426
|
-
const
|
|
440
|
+
const shown = displayVersion(agentId, version);
|
|
441
|
+
const base = isDefault ? `${shown} (default)` : shown;
|
|
427
442
|
const padded = base.padEnd(maxVerLabel);
|
|
428
|
-
const label = isDefault ? `${
|
|
443
|
+
const label = isDefault ? `${shown}${chalk.green(' (default)')}${' '.repeat(maxVerLabel - base.length)}` : padded;
|
|
429
444
|
const rawInfo = infoMap.get(`${agentId}:${version}`);
|
|
430
445
|
const vInfo = rawInfo ? mergeCanonical(rawInfo) : undefined;
|
|
431
446
|
const usageKey = getUsageLookupKey(vInfo);
|
package/dist/lib/agents.d.ts
CHANGED
|
@@ -34,6 +34,27 @@ export declare function findInPath(command: string): string | null;
|
|
|
34
34
|
export declare const AGENTS: Record<AgentId, AgentConfig>;
|
|
35
35
|
/** All registered agent IDs derived from the AGENTS registry. */
|
|
36
36
|
export declare const ALL_AGENT_IDS: AgentId[];
|
|
37
|
+
/**
|
|
38
|
+
* A self-updating agent is a single global binary installed by an official
|
|
39
|
+
* `curl … | sh` / `brew install` script that carries NO version token — the
|
|
40
|
+
* installer can only ever fetch the *current* release, and the binary then keeps
|
|
41
|
+
* itself up to date in place (droid, grok, antigravity, cursor, hermes, forge,
|
|
42
|
+
* kiro, goose). There is no semver to pin, so agents-cli must not model these as
|
|
43
|
+
* having multiple installable version-homes the way it does for npm-packaged
|
|
44
|
+
* agents (claude, codex, kimi, …).
|
|
45
|
+
*
|
|
46
|
+
* The predicate is `!npmPackage && installScript && !installScript.includes('VERSION')`:
|
|
47
|
+
* - `npmPackage` empty → not installed from npm, so `agents add x@1.2.3`
|
|
48
|
+
* can't resolve a registry version.
|
|
49
|
+
* - `installScript` present → it IS installed by a script (not unmanaged).
|
|
50
|
+
* - no `VERSION` placeholder → the script has no slot for a pinned version
|
|
51
|
+
* (contrast: an installer templated with `VERSION`
|
|
52
|
+
* could pin, and is NOT self-updating).
|
|
53
|
+
*
|
|
54
|
+
* Route every "is this a pinnable, multi-version agent?" decision through here —
|
|
55
|
+
* never a scattered `agent === 'droid'`.
|
|
56
|
+
*/
|
|
57
|
+
export declare function isSelfUpdatingAgent(agent: AgentId): boolean;
|
|
37
58
|
/** Get the chalk color function for an agent. Works for any AgentId or SessionAgentId. */
|
|
38
59
|
export declare function colorAgent(agentId: string): (s: string) => string;
|
|
39
60
|
/** Return the agent's display name, colored. */
|
package/dist/lib/agents.js
CHANGED
|
@@ -290,7 +290,7 @@ export const AGENTS = {
|
|
|
290
290
|
format: 'markdown',
|
|
291
291
|
variableSyntax: '$ARGUMENTS',
|
|
292
292
|
supportsHooks: true,
|
|
293
|
-
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist:
|
|
293
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: false, rules: { file: '.cursorrules' }, workflows: false, memory: false, modes: ['edit', 'skip'] }, // allowlist: ~/.cursor/cli-config.json
|
|
294
294
|
},
|
|
295
295
|
opencode: {
|
|
296
296
|
id: 'opencode',
|
|
@@ -310,7 +310,7 @@ export const AGENTS = {
|
|
|
310
310
|
format: 'markdown',
|
|
311
311
|
variableSyntax: '$ARGUMENTS',
|
|
312
312
|
supportsHooks: false,
|
|
313
|
-
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] },
|
|
313
|
+
capabilities: { hooks: false, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '1.1.1' }, skills: true, commands: true, plugins: true, subagents: true, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit'] },
|
|
314
314
|
},
|
|
315
315
|
openclaw: {
|
|
316
316
|
id: 'openclaw',
|
|
@@ -353,7 +353,7 @@ export const AGENTS = {
|
|
|
353
353
|
format: 'markdown',
|
|
354
354
|
variableSyntax: '$ARGUMENTS',
|
|
355
355
|
supportsHooks: true,
|
|
356
|
-
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents:
|
|
356
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: false, skills: true, commands: true, plugins: true, subagents: { since: '0.0.353' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'auto', 'skip'] },
|
|
357
357
|
},
|
|
358
358
|
amp: {
|
|
359
359
|
id: 'amp',
|
|
@@ -393,7 +393,7 @@ export const AGENTS = {
|
|
|
393
393
|
format: 'markdown',
|
|
394
394
|
variableSyntax: '$ARGUMENTS',
|
|
395
395
|
supportsHooks: true,
|
|
396
|
-
capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist:
|
|
396
|
+
capabilities: { hooks: { since: '0.10.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: { since: '2.8.0' }, skills: true, commands: true, plugins: false, subagents: { since: '1.23.0' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit'] },
|
|
397
397
|
},
|
|
398
398
|
goose: {
|
|
399
399
|
id: 'goose',
|
|
@@ -661,6 +661,30 @@ export const AGENTS = {
|
|
|
661
661
|
};
|
|
662
662
|
/** All registered agent IDs derived from the AGENTS registry. */
|
|
663
663
|
export const ALL_AGENT_IDS = Object.keys(AGENTS);
|
|
664
|
+
/**
|
|
665
|
+
* A self-updating agent is a single global binary installed by an official
|
|
666
|
+
* `curl … | sh` / `brew install` script that carries NO version token — the
|
|
667
|
+
* installer can only ever fetch the *current* release, and the binary then keeps
|
|
668
|
+
* itself up to date in place (droid, grok, antigravity, cursor, hermes, forge,
|
|
669
|
+
* kiro, goose). There is no semver to pin, so agents-cli must not model these as
|
|
670
|
+
* having multiple installable version-homes the way it does for npm-packaged
|
|
671
|
+
* agents (claude, codex, kimi, …).
|
|
672
|
+
*
|
|
673
|
+
* The predicate is `!npmPackage && installScript && !installScript.includes('VERSION')`:
|
|
674
|
+
* - `npmPackage` empty → not installed from npm, so `agents add x@1.2.3`
|
|
675
|
+
* can't resolve a registry version.
|
|
676
|
+
* - `installScript` present → it IS installed by a script (not unmanaged).
|
|
677
|
+
* - no `VERSION` placeholder → the script has no slot for a pinned version
|
|
678
|
+
* (contrast: an installer templated with `VERSION`
|
|
679
|
+
* could pin, and is NOT self-updating).
|
|
680
|
+
*
|
|
681
|
+
* Route every "is this a pinnable, multi-version agent?" decision through here —
|
|
682
|
+
* never a scattered `agent === 'droid'`.
|
|
683
|
+
*/
|
|
684
|
+
export function isSelfUpdatingAgent(agent) {
|
|
685
|
+
const cfg = AGENTS[agent];
|
|
686
|
+
return !cfg.npmPackage && !!cfg.installScript && !cfg.installScript.includes('VERSION');
|
|
687
|
+
}
|
|
664
688
|
// Capability-filtered agent lists used to live here as `*_CAPABLE_AGENTS`
|
|
665
689
|
// constants. They were a frequent source of silent-skip bugs (e.g. grok
|
|
666
690
|
// rules sync gated on `COMMANDS_CAPABLE_AGENTS`). Use `capableAgents(cap)`
|
package/dist/lib/daemon.d.ts
CHANGED
|
@@ -82,12 +82,12 @@ export declare function readDaemonClaudeOAuthToken(opts?: {
|
|
|
82
82
|
*/
|
|
83
83
|
export declare function writeOwnerOnlyServiceManifest(filePath: string, content: string): void;
|
|
84
84
|
/** Generate a macOS launchd plist for auto-starting the daemon. */
|
|
85
|
-
export declare function generateLaunchdPlist(oauthToken?: string | null): string;
|
|
85
|
+
export declare function generateLaunchdPlist(oauthToken?: string | null, agentsBin?: string): string;
|
|
86
86
|
/** Generate a Linux systemd user unit for auto-starting the daemon. */
|
|
87
|
-
export declare function generateSystemdUnit(oauthToken?: string | null): string;
|
|
87
|
+
export declare function generateSystemdUnit(oauthToken?: string | null, agentsBin?: string): string;
|
|
88
88
|
export declare function getAgentsBinPath(argv1?: string | undefined, execPath?: string): string;
|
|
89
89
|
/** Start the daemon via launchd, systemd, or as a detached process. */
|
|
90
|
-
export declare function startDaemon(): {
|
|
90
|
+
export declare function startDaemon(agentsBin?: string): {
|
|
91
91
|
pid: number | null;
|
|
92
92
|
method: string;
|
|
93
93
|
};
|
|
@@ -129,8 +129,8 @@ export declare function buildDetachedDaemonEnv(baseEnv?: NodeJS.ProcessEnv, oaut
|
|
|
129
129
|
* Going through `process.execPath` means a real PE/binary is spawned with
|
|
130
130
|
* `detached: true` and no console, so nothing signals the daemon after launch.
|
|
131
131
|
*
|
|
132
|
-
* When the entry isn't a
|
|
133
|
-
*
|
|
132
|
+
* When the entry isn't a Node script (e.g. a native compiled launcher), run it
|
|
133
|
+
* directly — it owns its own runtime resolution.
|
|
134
134
|
*/
|
|
135
135
|
export declare function getDaemonLaunch(agentsBin?: string): {
|
|
136
136
|
command: string;
|