@phnx-labs/agents-cli 1.20.59 → 1.20.60
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 +13 -1
- package/README.md +8 -5
- package/dist/bin/agents +0 -0
- package/dist/commands/exec.js +38 -1
- package/dist/lib/agents.js +6 -4
- package/dist/lib/hosts/dispatch.d.ts +36 -0
- package/dist/lib/hosts/dispatch.js +40 -2
- package/dist/lib/permissions.d.ts +14 -5
- package/dist/lib/permissions.js +139 -28
- package/dist/lib/plugins.d.ts +8 -0
- package/dist/lib/plugins.js +108 -0
- package/dist/lib/resources/permissions.d.ts +1 -1
- package/dist/lib/resources/permissions.js +5 -1
- package/dist/lib/resources/types.d.ts +1 -1
- package/dist/lib/routines.d.ts +16 -0
- package/dist/lib/routines.js +46 -1
- package/dist/lib/shims.js +13 -3
- package/dist/lib/skills.js +14 -1
- package/dist/lib/staleness/detectors/permissions.js +28 -2
- package/dist/lib/staleness/detectors/subagents.js +20 -1
- package/dist/lib/staleness/detectors/workflows.js +33 -0
- package/dist/lib/staleness/writers/subagents.js +13 -5
- package/dist/lib/subagents.d.ts +10 -1
- package/dist/lib/subagents.js +102 -14
- package/dist/lib/versions.js +12 -2
- package/dist/lib/workflows.d.ts +5 -3
- package/dist/lib/workflows.js +246 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 1.20.60
|
|
6
|
+
|
|
7
|
+
- **Fix Goose skill sync status for its native central-storage path.** `agents skills list goose@<version>` now reports skills under `~/.agents/skills/` as installed instead of falsely requiring a per-version `.config/goose/skills/` copy that Goose never reads. Source: `apps/cli/src/lib/skills.ts`.
|
|
8
|
+
- **Correct the documented `auto` and ACP `skip` semantics.** The README and bundled `run` skill now distinguish Kimi's interactive `--auto` from its already-auto-approved headless `-p` path, document Droid's native `--auto high`, and explain that ACP `skip` prefers `allow_always` but falls back to the first permission option offered by the server. Documentation only; runtime behavior is unchanged. Source: `README.md`, `skills/run/SKILL.md`.
|
|
9
|
+
- **Wire Antigravity subagents and Kimi workflow sync (RUSH-1548, RUSH-1581).** Antigravity now receives subagents as custom-agent Markdown under `~/.gemini/config/agents/<name>/agent.md` with the `>= 1.0.16` version gate enforced during sync. Kimi receives workflows as managed `type: flow` skills under `.kimi-code/skills/<name>/SKILL.md`, using the canonical slug as the flow name and an `agents_workflow` marker so native user-owned flows are not overwritten or removed. Antigravity workflows remain disabled for version-home sync until a global filesystem target is documented. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/workflows.ts`.
|
|
10
|
+
- **Release retries rebuild the exact merged, CI-tested release tree even after `main` advances.** A registry/auth failure after the release PR merged previously stranded that version because the catch-up guard required the release merge to remain current `main`. The local release script now validates the original PR head and full green matrix, verifies the SHA-pinned keychain helper, rebuilds the unpinned menu-bar helper from historical source in a detached temporary worktree, rejects mismatched remote tags, and tags/publishes that exact merge without including later commits. Source: `apps/cli/scripts/release.sh`.
|
|
11
|
+
- **Wire Gemini plugins/subagents and Goose workflows/allowlists.** Gemini now syncs plugin bundles as Gemini extensions (`.gemini/extensions/<name>/gemini-extension.json`) from CLI 0.8.0+ and subagents as `.gemini/agents/*.md` from CLI 0.36.0+. Goose now syncs workflows as recipe/subrecipe YAML under `.config/goose/recipes/` and permission groups into `.config/goose/permission.yaml`. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/plugins.ts`, `apps/cli/src/lib/subagents.ts`, `apps/cli/src/lib/workflows.ts`, `apps/cli/src/lib/permissions.ts`. (RUSH-1568, RUSH-1569, RUSH-1574, RUSH-1582)
|
|
12
|
+
- **Wire Gemini permissions/allowlist support (RUSH-1567).** Gemini permission groups now sync Bash allow/deny rules into `.gemini/settings.json` as `tools.core` / `tools.exclude` entries with per-command `ShellTool(...)` patterns; non-Bash canonical permissions remain unsupported by Gemini's native tool grammar and are skipped. Flip `allowlist: true`, register the permission writer/detector through the capability table, and replace the dormant legacy `tools.allowed` serializer. Source: `apps/cli/src/lib/agents.ts`, `apps/cli/src/lib/permissions.ts`, `apps/cli/src/lib/resources/permissions.ts`, `apps/cli/src/lib/staleness/detectors/permissions.ts`.
|
|
13
|
+
- **Fix: `agents run <agent>@<version> --host <host>` now forwards the version pin and most run flags to the remote host.** Previously the `--host` branch stripped `@version` and ignored `--strategy`, `--effort`, `--add-dir`, `--json`, `--verbose`, `--timeout`, `--yes`, and `--acp`, so the remote host applied its own defaults. The local CLI now parses `agent@version` verbatim, normalizes `--strategy`/`--balanced`, makes `--add-dir` paths remote-portable, and forwards all of these flags to the remote `agents run` invocation. `--add-dir` portability uses the same `~`/`$HOME` re-rooting that `--cwd` already uses, so a Linux remote resolves home paths against its own `/home/<user>`. Source: `apps/cli/src/lib/hosts/dispatch.ts`, `apps/cli/src/commands/exec.ts`, `apps/cli/src/lib/hosts/dispatch.test.ts`.
|
|
14
|
+
- **Fix: routine edits no longer rewrite the whole YAML file, so `~/.agents` stays clean and `agents repo pull` can sync routines across the fleet.** `writeJob` previously re-emitted the entire document via `yaml.stringify` on every mutation (pause/resume, `routines devices --set`, add), restyling untouched scalars — unquoting `schedule`, re-wrapping the folded `prompt` block — which left the git-backed user repo perpetually dirty. That made cross-device `agents repo pull` refuse ("uncommitted changes"), so a `devices:` pin set on one machine never reached the others and `Devices: all` routines kept firing on every box. A new `serializeJob` edits only the changed keys via the YAML Document API, preserving byte-for-byte formatting of untouched nodes; new/unparseable/non-mapping files fall back to canonical stringify. Source: `apps/cli/src/lib/routines.ts`, `apps/cli/src/lib/__tests__/routines.serialize.test.ts`.
|
|
15
|
+
|
|
5
16
|
## 1.20.59
|
|
6
17
|
|
|
7
18
|
- **Fix: remote secrets now choose the Windows PowerShell wrapper from the original `--host` name, not the resolved `user@ip` SSH target.** Inline enrolled Windows hosts resolve to address-based SSH targets, but the OS registry is keyed by the host name; `agents secrets view/list/exec --host <windows>`, `agents run --secrets bundle@<windows>`, and remote secrets unlock/export paths now pass that original name into command construction so Windows hosts no longer fall back to `bash -lc`. Source: `apps/cli/src/lib/secrets/remote.ts`, `apps/cli/src/commands/secrets.ts`, `apps/cli/src/commands/exec.ts`. (RUSH-1431)
|
|
@@ -12,6 +23,8 @@
|
|
|
12
23
|
- **OpenCode permissions write to the loaded config path (RUSH-1623).** Global config is `~/.config/opencode/opencode.jsonc` (not `~/.opencode/`); project config is `opencode.jsonc` at the project root. Source: `apps/cli/src/lib/permissions.ts`, `apps/cli/src/lib/agents.ts`.
|
|
13
24
|
- **Catch-up publishes now require the exact CI-tested release tree and its full green matrix.** A package version already present on `main` no longer counts as release validation by itself: `release.sh` resolves the merged `release/v<version>` PR, requires its merge commit to be current `main`, fetches the PR head that ran CI, requires that head tree to equal current `main`, and rechecks every expected CI context before publishing. This closes the path that let 1.20.58 reach npm before its tag-triggered Windows matrix exposed failures. Source: `apps/cli/scripts/release.sh`.
|
|
14
25
|
- **Windows release validation now matches portable path behavior.** Home-relative project paths normalize native separators to `/` before they are stored as `~/…`, and the systemd-manifest and remote-shell assertions now compare the escaped/quoted forms that the runtime deliberately emits. This restores the Windows Node 22/24 release matrix without weakening command escaping. Source: `apps/cli/src/lib/project-root.ts`, `apps/cli/src/lib/{project-root,daemon}.test.ts`, `apps/cli/src/lib/hosts/dispatch.test.ts`.
|
|
26
|
+
- **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)
|
|
27
|
+
|
|
15
28
|
## 1.20.58
|
|
16
29
|
|
|
17
30
|
- **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)
|
|
@@ -19,7 +32,6 @@
|
|
|
19
32
|
- **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)
|
|
20
33
|
- **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`.
|
|
21
34
|
- **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`.
|
|
22
|
-
- **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)
|
|
23
35
|
- **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`.
|
|
24
36
|
- **`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`.
|
|
25
37
|
- **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`).
|
package/README.md
CHANGED
|
@@ -161,9 +161,11 @@ Supports plan (read-only), edit, auto, and skip modes, effort levels, JSON outpu
|
|
|
161
161
|
|
|
162
162
|
Treat `skip` as a last-resort escape hatch. In direct-exec runs (without `--acp`),
|
|
163
163
|
agents-cli forwards the harness's native no-prompt flag; it does not add another
|
|
164
|
-
safety layer. Prefer `auto` where
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
safety layer. Prefer `auto` where it adds a safer automatic policy (smart classifier
|
|
165
|
+
on Claude/Copilot, native high-auto mode on Droid, or interactive Kimi), or `edit`
|
|
166
|
+
everywhere else. For headless Kimi, `edit`, `auto`, and `skip` all use the same
|
|
167
|
+
already-auto-approved `-p` behavior, so prefer `edit` rather than signaling a blanket
|
|
168
|
+
bypass. Harnesses without a native bypass flag reject direct-exec `skip`.
|
|
167
169
|
|
|
168
170
|
| Harness | Direct-exec `--mode skip` becomes |
|
|
169
171
|
|---|---|
|
|
@@ -179,8 +181,9 @@ reject direct-exec `skip`.
|
|
|
179
181
|
| Droid | `--skip-permissions-unsafe` |
|
|
180
182
|
|
|
181
183
|
With `--acp`, these native flags are not used. agents-cli instead grants `skip`
|
|
182
|
-
permission requests at the ACP protocol layer
|
|
183
|
-
last-resort
|
|
184
|
+
permission requests at the ACP protocol layer: it selects `allow_always` when offered,
|
|
185
|
+
otherwise the first permission option offered by the server. The same last-resort
|
|
186
|
+
warning applies.
|
|
184
187
|
|
|
185
188
|
Codex has no native smart-classifier mode, so `agents run codex --mode auto` resolves
|
|
186
189
|
to sandboxed `edit` and can still prompt. `agents run codex --mode skip` is different:
|
package/dist/bin/agents
CHANGED
|
Binary file
|
package/dist/commands/exec.js
CHANGED
|
@@ -482,6 +482,7 @@ export function registerRunCommand(program) {
|
|
|
482
482
|
const { resolveHost, resolveHostByCap } = await import('../lib/hosts/registry.js');
|
|
483
483
|
const { dispatchToHost, runInteractiveOnHost } = await import('../lib/hosts/dispatch.js');
|
|
484
484
|
const { registerHostSession, registerInteractiveHostSession } = await import('../lib/hosts/session-index.js');
|
|
485
|
+
const { normalizeRunStrategy, RUN_STRATEGIES } = await import('../lib/rotate.js');
|
|
485
486
|
// A password-auth device throws DeviceOffloadUnsupportedError here; it's
|
|
486
487
|
// printed cleanly by the top-level catch in index.ts (covers every
|
|
487
488
|
// resolveHost caller), so it never falls through to capability routing.
|
|
@@ -506,12 +507,30 @@ export function registerRunCommand(program) {
|
|
|
506
507
|
process.exit(1);
|
|
507
508
|
}
|
|
508
509
|
try {
|
|
509
|
-
const runAgent = agentSpec.split('@')
|
|
510
|
+
const [runAgent, rawRunVersion] = agentSpec.split('@');
|
|
511
|
+
// Forward the explicit @version pin verbatim. Resolving aliases like
|
|
512
|
+
// @latest locally would check local installs, but the remote host may
|
|
513
|
+
// have versions the laptop does not. The remote agents CLI resolves
|
|
514
|
+
// aliases against its own installed versions.
|
|
515
|
+
const runVersion = rawRunVersion || undefined;
|
|
516
|
+
// Normalize the effective strategy exactly like the local path so we
|
|
517
|
+
// fail fast on invalid input and can forward --balanced/--strategy.
|
|
518
|
+
const explicitStrategy = options.strategy ? normalizeRunStrategy(options.strategy) : null;
|
|
519
|
+
if (options.strategy && !explicitStrategy) {
|
|
520
|
+
console.error(chalk.red(`Invalid strategy: ${options.strategy}. Use ${RUN_STRATEGIES.join(', ')}.`));
|
|
521
|
+
process.exit(1);
|
|
522
|
+
}
|
|
523
|
+
if (options.balanced && explicitStrategy && explicitStrategy !== 'balanced') {
|
|
524
|
+
console.error(chalk.red('--balanced conflicts with --strategy. Use one strategy override.'));
|
|
525
|
+
process.exit(1);
|
|
526
|
+
}
|
|
527
|
+
const runStrategy = options.balanced ? 'balanced' : explicitStrategy ?? undefined;
|
|
510
528
|
// Working directory on the host: an explicit --remote-cwd is used
|
|
511
529
|
// verbatim; --cwd/--project are made portable (a local-home absolute
|
|
512
530
|
// becomes `~/…` so the remote shell re-roots it at ITS home).
|
|
513
531
|
const { toRemotePortable } = await import('../lib/project-root.js');
|
|
514
532
|
const hostCwd = options.remoteCwd ?? (options.cwd ? toRemotePortable(options.cwd) : undefined);
|
|
533
|
+
const hostAddDirs = options.addDir.length > 0 ? options.addDir.map(toRemotePortable) : undefined;
|
|
515
534
|
// `--resume [id]`: commander yields the string id, or `true` when the
|
|
516
535
|
// flag is passed bare. A bare resume needs the interactive picker,
|
|
517
536
|
// which can't run over a detached remote dispatch — only forward a
|
|
@@ -549,9 +568,18 @@ export function registerRunCommand(program) {
|
|
|
549
568
|
}
|
|
550
569
|
const exitCode = await runInteractiveOnHost(host, {
|
|
551
570
|
agent: runAgent,
|
|
571
|
+
version: resumeId ? undefined : runVersion,
|
|
572
|
+
strategy: resumeId ? undefined : runStrategy,
|
|
552
573
|
prompt,
|
|
553
574
|
mode: options.mode,
|
|
554
575
|
model: options.model,
|
|
576
|
+
effort: options.effort,
|
|
577
|
+
addDir: hostAddDirs,
|
|
578
|
+
json: options.json,
|
|
579
|
+
verbose: options.verbose,
|
|
580
|
+
timeout: options.timeout,
|
|
581
|
+
yes: options.yes,
|
|
582
|
+
acp: options.acp,
|
|
555
583
|
remoteCwd: hostCwd,
|
|
556
584
|
sessionId: hostSessionId,
|
|
557
585
|
name: options.name,
|
|
@@ -571,9 +599,18 @@ export function registerRunCommand(program) {
|
|
|
571
599
|
const hostSessionId = runAgent === 'claude' && !resumeId ? randomUUID() : undefined;
|
|
572
600
|
const { task, exitCode } = await dispatchToHost(host, {
|
|
573
601
|
agent: runAgent,
|
|
602
|
+
version: resumeId ? undefined : runVersion,
|
|
603
|
+
strategy: resumeId ? undefined : runStrategy,
|
|
574
604
|
prompt,
|
|
575
605
|
mode: options.mode,
|
|
576
606
|
model: options.model,
|
|
607
|
+
effort: options.effort,
|
|
608
|
+
addDir: hostAddDirs,
|
|
609
|
+
json: options.json,
|
|
610
|
+
verbose: options.verbose,
|
|
611
|
+
timeout: options.timeout,
|
|
612
|
+
yes: options.yes,
|
|
613
|
+
acp: options.acp,
|
|
577
614
|
remoteCwd: hostCwd,
|
|
578
615
|
sessionId: hostSessionId,
|
|
579
616
|
name: options.name,
|
package/dist/lib/agents.js
CHANGED
|
@@ -265,7 +265,8 @@ export const AGENTS = {
|
|
|
265
265
|
url: 'https://developers.googleblog.com/an-important-update-transitioning-gemini-cli-to-antigravity-cli/',
|
|
266
266
|
},
|
|
267
267
|
// gemini hooks: shipped in v0.26.0 (Jan 2026); older binaries silently ignore the `hooks` key.
|
|
268
|
-
|
|
268
|
+
// extensions: gemini-extension.json bundles shipped in v0.8.0; custom subagents in v0.36.0.
|
|
269
|
+
capabilities: { hooks: { since: '0.26.0' }, mcp: true, mcpHttp: true, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: { since: '0.8.0' }, subagents: { since: '0.36.0' }, rules: { file: 'GEMINI.md' }, workflows: false, memory: false, modes: ['plan', 'edit', 'skip'], rulesImports: true },
|
|
269
270
|
},
|
|
270
271
|
cursor: {
|
|
271
272
|
id: 'cursor',
|
|
@@ -419,7 +420,8 @@ export const AGENTS = {
|
|
|
419
420
|
supportsHooks: true,
|
|
420
421
|
// Plugins: Open Plugins under ~/.agents/plugins/<name>/ (same layout as
|
|
421
422
|
// agents-cli source). Version isolation copies into versionHome/.agents/plugins/.
|
|
422
|
-
|
|
423
|
+
// Workflows sync as Goose recipe YAML; permissions sync to permission.yaml.
|
|
424
|
+
capabilities: { hooks: { since: '1.34.0' }, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: { since: '1.25.0' }, commands: false, plugins: true, subagents: false, rules: { file: 'AGENTS.md' }, workflows: true, memory: false, modes: ['edit'] },
|
|
423
425
|
},
|
|
424
426
|
// Google Antigravity CLI (`agy`) — official replacement for Gemini CLI as of IO 2026.
|
|
425
427
|
// configDir nests inside `~/.gemini/` since agy shares the parent dir with the Gemini
|
|
@@ -448,7 +450,7 @@ export const AGENTS = {
|
|
|
448
450
|
variableSyntax: '{{args}}',
|
|
449
451
|
supportsHooks: true,
|
|
450
452
|
cloudProvider: 'antigravity',
|
|
451
|
-
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents:
|
|
453
|
+
capabilities: { hooks: true, mcp: true, mcpHttp: false, mcpHeaders: false, allowlist: true, skills: true, commands: true, plugins: true, subagents: { since: '1.0.16' }, rules: { file: 'AGENTS.md' }, workflows: false, memory: false, modes: ['edit', 'skip'], rulesImports: false },
|
|
452
454
|
},
|
|
453
455
|
// xAI Grok Build CLI (`grok`) — early beta, SuperGrok Heavy. Auth via OAuth on
|
|
454
456
|
// first launch, or XAI_API_KEY env var for headless. MCP servers configured inline
|
|
@@ -522,7 +524,7 @@ export const AGENTS = {
|
|
|
522
524
|
plugins: true,
|
|
523
525
|
subagents: true, // YAML agent files under ~/.kimi-code/agents/ (see transformSubagentForKimi)
|
|
524
526
|
rules: { file: 'AGENTS.md' },
|
|
525
|
-
workflows:
|
|
527
|
+
workflows: true,
|
|
526
528
|
memory: false,
|
|
527
529
|
modes: ['plan', 'edit', 'auto', 'skip'],
|
|
528
530
|
rulesImports: false,
|
|
@@ -39,8 +39,26 @@ export declare function terminateDispatchedTask(task: HostTask): void;
|
|
|
39
39
|
export interface DispatchOptions {
|
|
40
40
|
agent: string;
|
|
41
41
|
prompt: string;
|
|
42
|
+
/** Explicit agent version pin (e.g. "2.1.207") to forward as `agent@version`. */
|
|
43
|
+
version?: string;
|
|
44
|
+
/** Explicit run strategy (e.g. "balanced") to forward as `--strategy <strategy>`. */
|
|
45
|
+
strategy?: string;
|
|
42
46
|
mode?: string;
|
|
43
47
|
model?: string;
|
|
48
|
+
/** Reasoning effort to forward as `--effort <effort>`. */
|
|
49
|
+
effort?: string;
|
|
50
|
+
/** Additional directories to grant, already made remote-portable. */
|
|
51
|
+
addDir?: string[];
|
|
52
|
+
/** Stream events as JSON lines. */
|
|
53
|
+
json?: boolean;
|
|
54
|
+
/** Show detailed execution logs. */
|
|
55
|
+
verbose?: boolean;
|
|
56
|
+
/** Kill the agent after this duration, forwarded as `--timeout <duration>`. */
|
|
57
|
+
timeout?: string;
|
|
58
|
+
/** Skip the interactive budget-confirm prompt. */
|
|
59
|
+
yes?: boolean;
|
|
60
|
+
/** Route through the Agent Client Protocol. */
|
|
61
|
+
acp?: boolean;
|
|
44
62
|
remoteCwd?: string;
|
|
45
63
|
/**
|
|
46
64
|
* Force the remote run's NEW session to use this exact id (Claude only, via
|
|
@@ -68,10 +86,28 @@ export interface DispatchOptions {
|
|
|
68
86
|
export declare function buildRunForwardedArgs(opts: DispatchOptions): string[];
|
|
69
87
|
export interface InteractiveDispatchOptions {
|
|
70
88
|
agent: string;
|
|
89
|
+
/** Explicit agent version pin (e.g. "2.1.207") to forward as `agent@version`. */
|
|
90
|
+
version?: string;
|
|
91
|
+
/** Explicit run strategy (e.g. "balanced") to forward as `--strategy <strategy>`. */
|
|
92
|
+
strategy?: string;
|
|
71
93
|
/** Optional prompt — forwarded only when the caller explicitly forced interactive mode. */
|
|
72
94
|
prompt?: string;
|
|
73
95
|
mode?: string;
|
|
74
96
|
model?: string;
|
|
97
|
+
/** Reasoning effort to forward as `--effort <effort>`. */
|
|
98
|
+
effort?: string;
|
|
99
|
+
/** Additional directories to grant, already made remote-portable. */
|
|
100
|
+
addDir?: string[];
|
|
101
|
+
/** Stream events as JSON lines. */
|
|
102
|
+
json?: boolean;
|
|
103
|
+
/** Show detailed execution logs. */
|
|
104
|
+
verbose?: boolean;
|
|
105
|
+
/** Kill the agent after this duration, forwarded as `--timeout <duration>`. */
|
|
106
|
+
timeout?: string;
|
|
107
|
+
/** Skip the interactive budget-confirm prompt. */
|
|
108
|
+
yes?: boolean;
|
|
109
|
+
/** Route through the Agent Client Protocol. */
|
|
110
|
+
acp?: boolean;
|
|
75
111
|
remoteCwd?: string;
|
|
76
112
|
sessionId?: string;
|
|
77
113
|
name?: string;
|
|
@@ -175,11 +175,30 @@ async function launchDetached(host, target, opts) {
|
|
|
175
175
|
* resume wins when — defensively — both are set.
|
|
176
176
|
*/
|
|
177
177
|
export function buildRunForwardedArgs(opts) {
|
|
178
|
-
const
|
|
178
|
+
const agentArg = opts.version ? `${opts.agent}@${opts.version}` : opts.agent;
|
|
179
|
+
const args = ['run', agentArg, opts.prompt, '--quiet'];
|
|
180
|
+
if (opts.strategy)
|
|
181
|
+
args.push('--strategy', opts.strategy);
|
|
179
182
|
if (opts.mode)
|
|
180
183
|
args.push('--mode', opts.mode);
|
|
181
184
|
if (opts.model)
|
|
182
185
|
args.push('--model', opts.model);
|
|
186
|
+
if (opts.effort)
|
|
187
|
+
args.push('--effort', opts.effort);
|
|
188
|
+
if (opts.addDir) {
|
|
189
|
+
for (const dir of opts.addDir)
|
|
190
|
+
args.push('--add-dir', dir);
|
|
191
|
+
}
|
|
192
|
+
if (opts.json)
|
|
193
|
+
args.push('--json');
|
|
194
|
+
if (opts.verbose)
|
|
195
|
+
args.push('--verbose');
|
|
196
|
+
if (opts.timeout)
|
|
197
|
+
args.push('--timeout', opts.timeout);
|
|
198
|
+
if (opts.yes)
|
|
199
|
+
args.push('--yes');
|
|
200
|
+
if (opts.acp)
|
|
201
|
+
args.push('--acp');
|
|
183
202
|
if (opts.name)
|
|
184
203
|
args.push('--name', opts.name);
|
|
185
204
|
if (opts.resume)
|
|
@@ -196,15 +215,34 @@ export function buildRunForwardedArgs(opts) {
|
|
|
196
215
|
* infer headless from the prompt).
|
|
197
216
|
*/
|
|
198
217
|
export function buildInteractiveRunForwardedArgs(opts) {
|
|
199
|
-
const
|
|
218
|
+
const agentArg = opts.version ? `${opts.agent}@${opts.version}` : opts.agent;
|
|
219
|
+
const args = ['run', agentArg];
|
|
200
220
|
if (opts.prompt && opts.forceInteractive)
|
|
201
221
|
args.push(opts.prompt);
|
|
202
222
|
if (opts.forceInteractive)
|
|
203
223
|
args.push('--interactive');
|
|
224
|
+
if (opts.strategy)
|
|
225
|
+
args.push('--strategy', opts.strategy);
|
|
204
226
|
if (opts.mode)
|
|
205
227
|
args.push('--mode', opts.mode);
|
|
206
228
|
if (opts.model)
|
|
207
229
|
args.push('--model', opts.model);
|
|
230
|
+
if (opts.effort)
|
|
231
|
+
args.push('--effort', opts.effort);
|
|
232
|
+
if (opts.addDir) {
|
|
233
|
+
for (const dir of opts.addDir)
|
|
234
|
+
args.push('--add-dir', dir);
|
|
235
|
+
}
|
|
236
|
+
if (opts.json)
|
|
237
|
+
args.push('--json');
|
|
238
|
+
if (opts.verbose)
|
|
239
|
+
args.push('--verbose');
|
|
240
|
+
if (opts.timeout)
|
|
241
|
+
args.push('--timeout', opts.timeout);
|
|
242
|
+
if (opts.yes)
|
|
243
|
+
args.push('--yes');
|
|
244
|
+
if (opts.acp)
|
|
245
|
+
args.push('--acp');
|
|
208
246
|
if (opts.name)
|
|
209
247
|
args.push('--name', opts.name);
|
|
210
248
|
if (opts.resume)
|
|
@@ -106,15 +106,15 @@ export declare function convertToClaudeFormat(set: PermissionSet): ClaudePermiss
|
|
|
106
106
|
export declare function convertToCursorFormat(set: PermissionSet): CursorPermissions;
|
|
107
107
|
/**
|
|
108
108
|
* Convert canonical permission set to Gemini format.
|
|
109
|
-
* Gemini reads tool allow
|
|
110
|
-
* Bash permissions map to
|
|
111
|
-
*
|
|
112
|
-
* Blanket Bash grants map to bare "run_shell_command" (no prefix filter).
|
|
109
|
+
* Gemini reads tool allow/deny lists from settings.json under
|
|
110
|
+
* `tools.core` / `tools.exclude`. Bash permissions map to ShellTool(pattern).
|
|
111
|
+
* Blanket Bash grants map to bare "ShellTool" (no command filter).
|
|
113
112
|
* Non-Bash tool patterns are skipped; Gemini uses different tool names.
|
|
114
113
|
*/
|
|
115
114
|
export declare function convertToGeminiFormat(set: PermissionSet): {
|
|
116
115
|
tools: {
|
|
117
|
-
|
|
116
|
+
core: string[];
|
|
117
|
+
exclude?: string[];
|
|
118
118
|
};
|
|
119
119
|
};
|
|
120
120
|
/** Convert canonical Bash rules into Droid's command arrays. */
|
|
@@ -140,6 +140,15 @@ export declare function convertToAntigravityFormat(set: PermissionSet): {
|
|
|
140
140
|
deny?: string[];
|
|
141
141
|
};
|
|
142
142
|
};
|
|
143
|
+
export interface GoosePermissionConfig {
|
|
144
|
+
user: {
|
|
145
|
+
always_allow: string[];
|
|
146
|
+
ask_before: string[];
|
|
147
|
+
never_allow: string[];
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
/** Convert canonical permissions to Goose's per-tool permission.yaml shape. */
|
|
151
|
+
export declare function convertToGooseFormat(set: PermissionSet): GoosePermissionConfig;
|
|
143
152
|
/**
|
|
144
153
|
* Convert canonical permission set to Grok format.
|
|
145
154
|
* Grok reads ~/.grok/config.toml with
|
package/dist/lib/permissions.js
CHANGED
|
@@ -306,14 +306,21 @@ export function buildPermissionsFromGroups(groupNames) {
|
|
|
306
306
|
// Matches lines like: - "Bash(git *)" or - "WebFetch(domain:example.com)"
|
|
307
307
|
// Handles nested quotes that break YAML parsers
|
|
308
308
|
const lines = content.split('\n');
|
|
309
|
+
let section = null;
|
|
309
310
|
for (const line of lines) {
|
|
311
|
+
const sectionMatch = line.match(/^\s*(allow|deny)\s*:\s*(?:#.*)?$/);
|
|
312
|
+
if (sectionMatch) {
|
|
313
|
+
section = sectionMatch[1];
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
310
316
|
// Match: optional whitespace, dash, whitespace, quote, content, quote
|
|
311
317
|
// Use greedy match to capture everything between first and last quote
|
|
312
318
|
const match = line.match(/^\s*-\s*"(.+)"$/);
|
|
313
319
|
if (match) {
|
|
314
320
|
const rule = match[1];
|
|
315
|
-
// 99-deny group rules go to deny, others
|
|
316
|
-
|
|
321
|
+
// 99-deny group rules go to deny, others follow their YAML section.
|
|
322
|
+
// Legacy group files used bare lists with no section; keep those as allow.
|
|
323
|
+
if (section === 'deny' || groupName === '99-deny' || groupName.includes('-deny')) {
|
|
317
324
|
allDeny.push(rule);
|
|
318
325
|
}
|
|
319
326
|
else {
|
|
@@ -486,32 +493,40 @@ function parseCanonicalPattern(permission) {
|
|
|
486
493
|
const BLANKET_BASH_FORMS = new Set(['Bash', 'Bash(*)', 'Bash(**)']);
|
|
487
494
|
/**
|
|
488
495
|
* Convert canonical permission set to Gemini format.
|
|
489
|
-
* Gemini reads tool allow
|
|
490
|
-
* Bash permissions map to
|
|
491
|
-
*
|
|
492
|
-
* Blanket Bash grants map to bare "run_shell_command" (no prefix filter).
|
|
496
|
+
* Gemini reads tool allow/deny lists from settings.json under
|
|
497
|
+
* `tools.core` / `tools.exclude`. Bash permissions map to ShellTool(pattern).
|
|
498
|
+
* Blanket Bash grants map to bare "ShellTool" (no command filter).
|
|
493
499
|
* Non-Bash tool patterns are skipped; Gemini uses different tool names.
|
|
494
500
|
*/
|
|
495
501
|
export function convertToGeminiFormat(set) {
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
502
|
+
const serialize = (permissions) => {
|
|
503
|
+
const tools = new Set();
|
|
504
|
+
for (const perm of permissions) {
|
|
505
|
+
if (BLANKET_BASH_FORMS.has(perm)) {
|
|
506
|
+
tools.add('ShellTool');
|
|
507
|
+
continue;
|
|
508
|
+
}
|
|
509
|
+
const parsed = parseCanonicalPattern(perm);
|
|
510
|
+
if (!parsed || parsed.tool !== 'bash')
|
|
511
|
+
continue;
|
|
512
|
+
const command = normalizeBashPattern(parsed.pattern);
|
|
513
|
+
if (command === '*') {
|
|
514
|
+
tools.add('ShellTool');
|
|
515
|
+
}
|
|
516
|
+
else {
|
|
517
|
+
tools.add(`ShellTool(${command})`);
|
|
518
|
+
}
|
|
512
519
|
}
|
|
513
|
-
|
|
514
|
-
|
|
520
|
+
return Array.from(tools);
|
|
521
|
+
};
|
|
522
|
+
const core = serialize(set.allow);
|
|
523
|
+
const exclude = serialize(set.deny ?? []);
|
|
524
|
+
return {
|
|
525
|
+
tools: {
|
|
526
|
+
core,
|
|
527
|
+
...(exclude.length ? { exclude } : {}),
|
|
528
|
+
},
|
|
529
|
+
};
|
|
515
530
|
}
|
|
516
531
|
/**
|
|
517
532
|
* Strip Claude's `:*` subcommand-wildcard suffix and return a space-glob form.
|
|
@@ -596,6 +611,49 @@ const ANTIGRAVITY_ACTION_BY_TOOL = {
|
|
|
596
611
|
write: 'write_file',
|
|
597
612
|
webfetch: 'read_url',
|
|
598
613
|
};
|
|
614
|
+
const GOOSE_TOOL_BY_CANONICAL = {
|
|
615
|
+
bash: 'developer__shell',
|
|
616
|
+
read: 'developer__text_editor',
|
|
617
|
+
write: 'developer__text_editor',
|
|
618
|
+
edit: 'developer__text_editor',
|
|
619
|
+
grep: 'developer__analyze',
|
|
620
|
+
glob: 'developer__analyze',
|
|
621
|
+
webfetch: 'developer__fetch',
|
|
622
|
+
mcp: undefined,
|
|
623
|
+
};
|
|
624
|
+
function canonicalToGooseTool(permission) {
|
|
625
|
+
if (BLANKET_BASH_FORMS.has(permission))
|
|
626
|
+
return 'developer__shell';
|
|
627
|
+
const parsed = parseCanonicalPattern(permission);
|
|
628
|
+
if (!parsed)
|
|
629
|
+
return null;
|
|
630
|
+
return GOOSE_TOOL_BY_CANONICAL[parsed.tool] ?? null;
|
|
631
|
+
}
|
|
632
|
+
/** Convert canonical permissions to Goose's per-tool permission.yaml shape. */
|
|
633
|
+
export function convertToGooseFormat(set) {
|
|
634
|
+
const alwaysAllow = new Set();
|
|
635
|
+
const neverAllow = new Set();
|
|
636
|
+
for (const permission of set.allow) {
|
|
637
|
+
const tool = canonicalToGooseTool(permission);
|
|
638
|
+
if (tool)
|
|
639
|
+
alwaysAllow.add(tool);
|
|
640
|
+
}
|
|
641
|
+
for (const permission of set.deny ?? []) {
|
|
642
|
+
const tool = canonicalToGooseTool(permission);
|
|
643
|
+
if (tool)
|
|
644
|
+
neverAllow.add(tool);
|
|
645
|
+
}
|
|
646
|
+
for (const tool of neverAllow) {
|
|
647
|
+
alwaysAllow.delete(tool);
|
|
648
|
+
}
|
|
649
|
+
return {
|
|
650
|
+
user: {
|
|
651
|
+
always_allow: Array.from(alwaysAllow).sort(),
|
|
652
|
+
ask_before: [],
|
|
653
|
+
never_allow: Array.from(neverAllow).sort(),
|
|
654
|
+
},
|
|
655
|
+
};
|
|
656
|
+
}
|
|
599
657
|
/**
|
|
600
658
|
* Convert canonical permission set to Grok format.
|
|
601
659
|
* Grok reads ~/.grok/config.toml with
|
|
@@ -1274,17 +1332,29 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
|
|
|
1274
1332
|
const geminiPerms = convertToGeminiFormat(set);
|
|
1275
1333
|
const settingsPath = path.join(versionHome, '.gemini', 'settings.json');
|
|
1276
1334
|
updateGeminiSettings(settingsPath, (settings) => {
|
|
1277
|
-
// Remove stale
|
|
1335
|
+
// Remove stale keys written by earlier serializer versions:
|
|
1336
|
+
// top-level `permissions`, and Gemini's pre-core/exclude `tools.allowed`.
|
|
1278
1337
|
delete settings.permissions;
|
|
1279
1338
|
const tools = (typeof settings.tools === 'object' && settings.tools !== null && !Array.isArray(settings.tools))
|
|
1280
1339
|
? settings.tools
|
|
1281
1340
|
: {};
|
|
1341
|
+
delete tools.allowed;
|
|
1282
1342
|
if (merge) {
|
|
1283
|
-
const
|
|
1284
|
-
|
|
1343
|
+
const existingCore = Array.isArray(tools.core) ? tools.core : [];
|
|
1344
|
+
const existingExclude = Array.isArray(tools.exclude) ? tools.exclude : [];
|
|
1345
|
+
tools.core = Array.from(new Set([...existingCore, ...geminiPerms.tools.core]));
|
|
1346
|
+
const mergedExclude = Array.from(new Set([...existingExclude, ...(geminiPerms.tools.exclude ?? [])]));
|
|
1347
|
+
if (mergedExclude.length)
|
|
1348
|
+
tools.exclude = mergedExclude;
|
|
1349
|
+
else
|
|
1350
|
+
delete tools.exclude;
|
|
1285
1351
|
}
|
|
1286
1352
|
else {
|
|
1287
|
-
tools.
|
|
1353
|
+
tools.core = geminiPerms.tools.core;
|
|
1354
|
+
if (geminiPerms.tools.exclude?.length)
|
|
1355
|
+
tools.exclude = geminiPerms.tools.exclude;
|
|
1356
|
+
else
|
|
1357
|
+
delete tools.exclude;
|
|
1288
1358
|
}
|
|
1289
1359
|
settings.tools = tools;
|
|
1290
1360
|
});
|
|
@@ -1349,6 +1419,47 @@ export function applyPermissionsToVersion(agentId, set, versionHome, merge = tru
|
|
|
1349
1419
|
fs.writeFileSync(configPath, TOML.stringify(config), 'utf-8');
|
|
1350
1420
|
return { success: true };
|
|
1351
1421
|
}
|
|
1422
|
+
if (agentId === 'goose') {
|
|
1423
|
+
const permissionsPath = path.join(versionHome, '.config', 'goose', 'permission.yaml');
|
|
1424
|
+
let config = {};
|
|
1425
|
+
if (fs.existsSync(permissionsPath)) {
|
|
1426
|
+
const parsed = yaml.parse(fs.readFileSync(permissionsPath, 'utf-8'));
|
|
1427
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
1428
|
+
config = parsed;
|
|
1429
|
+
}
|
|
1430
|
+
}
|
|
1431
|
+
const converted = convertToGooseFormat(set).user;
|
|
1432
|
+
const user = (typeof config.user === 'object' && config.user !== null && !Array.isArray(config.user))
|
|
1433
|
+
? config.user
|
|
1434
|
+
: {};
|
|
1435
|
+
const currentAlways = Array.isArray(user.always_allow) ? user.always_allow.filter((v) => typeof v === 'string') : [];
|
|
1436
|
+
const currentAsk = Array.isArray(user.ask_before) ? user.ask_before.filter((v) => typeof v === 'string') : [];
|
|
1437
|
+
const currentNever = Array.isArray(user.never_allow) ? user.never_allow.filter((v) => typeof v === 'string') : [];
|
|
1438
|
+
if (merge) {
|
|
1439
|
+
const incomingAlways = new Set(converted.always_allow);
|
|
1440
|
+
const incomingNever = new Set(converted.never_allow);
|
|
1441
|
+
const touched = new Set([...incomingAlways, ...incomingNever]);
|
|
1442
|
+
const always = new Set(currentAlways.filter(tool => !touched.has(tool)));
|
|
1443
|
+
const ask = new Set(currentAsk.filter(tool => !touched.has(tool)));
|
|
1444
|
+
const never = new Set(currentNever.filter(tool => !touched.has(tool)));
|
|
1445
|
+
for (const tool of incomingAlways)
|
|
1446
|
+
always.add(tool);
|
|
1447
|
+
for (const tool of incomingNever)
|
|
1448
|
+
never.add(tool);
|
|
1449
|
+
user.always_allow = Array.from(always).sort();
|
|
1450
|
+
user.ask_before = Array.from(ask).sort();
|
|
1451
|
+
user.never_allow = Array.from(never).sort();
|
|
1452
|
+
}
|
|
1453
|
+
else {
|
|
1454
|
+
user.always_allow = converted.always_allow;
|
|
1455
|
+
user.ask_before = converted.ask_before;
|
|
1456
|
+
user.never_allow = converted.never_allow;
|
|
1457
|
+
}
|
|
1458
|
+
config.user = user;
|
|
1459
|
+
fs.mkdirSync(path.dirname(permissionsPath), { recursive: true });
|
|
1460
|
+
fs.writeFileSync(permissionsPath, yaml.stringify(config), 'utf-8');
|
|
1461
|
+
return { success: true };
|
|
1462
|
+
}
|
|
1352
1463
|
if (agentId === 'kimi') {
|
|
1353
1464
|
const configPath = path.join(versionHome, '.kimi-code', 'config.toml');
|
|
1354
1465
|
let config = {};
|
package/dist/lib/plugins.d.ts
CHANGED
|
@@ -191,6 +191,14 @@ export declare function resolveOpenCodePluginSources(pluginRoot: string): string
|
|
|
191
191
|
export declare function installOpenCodePlugin(plugin: DiscoveredPlugin, versionHome: string): boolean;
|
|
192
192
|
export declare function isOpenCodePluginInstalled(pluginName: string, versionHome: string): boolean;
|
|
193
193
|
export declare function removeOpenCodePlugin(pluginName: string, versionHome: string): boolean;
|
|
194
|
+
/**
|
|
195
|
+
* Gemini CLI extensions live under `$HOME/.gemini/extensions/<name>/` and
|
|
196
|
+
* require a `gemini-extension.json` manifest at the extension root.
|
|
197
|
+
*/
|
|
198
|
+
export declare function geminiExtensionsDir(versionHome: string): string;
|
|
199
|
+
export declare function installGeminiPlugin(plugin: DiscoveredPlugin, versionHome: string): boolean;
|
|
200
|
+
export declare function isGeminiPluginInstalled(pluginName: string, versionHome: string): boolean;
|
|
201
|
+
export declare function removeGeminiPlugin(pluginName: string, versionHome: string): boolean;
|
|
194
202
|
/**
|
|
195
203
|
* Goose auto-discovers Open Plugins at `$HOME/.agents/plugins/<name>/`.
|
|
196
204
|
* Under agents-cli version isolation HOME is the version home, so we install to:
|