@phnx-labs/agents-cli 1.22.14 → 1.22.16
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 +53 -0
- package/README.md +7 -5
- package/dist/bin/agents +0 -0
- package/dist/commands/cloud.js +4 -6
- package/dist/commands/exec.js +96 -11
- package/dist/commands/humans.d.ts +10 -0
- package/dist/commands/humans.js +91 -0
- package/dist/commands/packages.js +90 -22
- package/dist/commands/projects.d.ts +2 -5
- package/dist/commands/projects.js +92 -65
- package/dist/commands/resume.d.ts +17 -0
- package/dist/commands/resume.js +80 -0
- package/dist/commands/routines.js +53 -71
- package/dist/commands/sessions.d.ts +13 -4
- package/dist/commands/sessions.js +37 -19
- package/dist/commands/setup-watchdog.d.ts +3 -0
- package/dist/commands/setup-watchdog.js +54 -0
- package/dist/commands/setup.js +8 -1
- package/dist/commands/ssh.d.ts +11 -0
- package/dist/commands/ssh.js +18 -4
- package/dist/commands/watchdog.d.ts +3 -4
- package/dist/commands/watchdog.js +40 -28
- package/dist/index.js +3 -2
- package/dist/lib/auto-dispatch.d.ts +8 -10
- package/dist/lib/auto-dispatch.js +20 -42
- package/dist/lib/channels/send.d.ts +4 -1
- package/dist/lib/channels/send.js +15 -7
- package/dist/lib/devices/doctor-findings.d.ts +14 -0
- package/dist/lib/devices/doctor-findings.js +92 -51
- package/dist/lib/exec.d.ts +6 -2
- package/dist/lib/exec.js +22 -4
- package/dist/lib/hooks.d.ts +4 -2
- package/dist/lib/hooks.js +173 -23
- package/dist/lib/humans.d.ts +25 -0
- package/dist/lib/humans.js +65 -0
- package/dist/lib/memory.js +2 -1
- package/dist/lib/menubar/MenubarHelper.app/Contents/CodeResources +0 -0
- package/dist/lib/menubar/MenubarHelper.app/Contents/MacOS/MenubarHelper +0 -0
- package/dist/lib/migrate.d.ts +1 -1
- package/dist/lib/migrate.js +116 -7
- package/dist/lib/notify.js +6 -2
- package/dist/lib/permissions.js +12 -10
- package/dist/lib/project-import.d.ts +7 -46
- package/dist/lib/project-import.js +9 -108
- package/dist/lib/projects.d.ts +20 -3
- package/dist/lib/projects.js +36 -18
- package/dist/lib/registry.d.ts +1 -1
- package/dist/lib/registry.js +11 -0
- package/dist/lib/routine-activation.d.ts +14 -0
- package/dist/lib/routine-activation.js +78 -0
- package/dist/lib/routines-project.d.ts +1 -1
- package/dist/lib/routines-project.js +3 -9
- package/dist/lib/routines.d.ts +3 -1
- package/dist/lib/routines.js +32 -19
- 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/bundles.d.ts +11 -0
- package/dist/lib/secrets/bundles.js +122 -5
- package/dist/lib/session/actor-sidecar.d.ts +5 -2
- package/dist/lib/session/actor-sidecar.js +4 -2
- package/dist/lib/session/db.d.ts +2 -1
- package/dist/lib/session/db.js +19 -3
- package/dist/lib/session/types.d.ts +4 -0
- package/dist/lib/staleness/writers/sources.d.ts +6 -1
- package/dist/lib/staleness/writers/sources.js +93 -7
- package/dist/lib/startup/command-registry.d.ts +2 -0
- package/dist/lib/startup/command-registry.js +5 -0
- package/dist/lib/state.d.ts +2 -0
- package/dist/lib/state.js +23 -10
- package/dist/lib/types.d.ts +64 -1
- package/dist/lib/versions.js +48 -14
- package/dist/lib/watchdog/rotate.d.ts +1 -1
- package/dist/lib/watchdog/rotate.js +1 -1
- package/package.json +1 -1
- package/dist/lib/watchdog/routine.d.ts +0 -44
- package/dist/lib/watchdog/routine.js +0 -69
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,58 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.22.16
|
|
4
|
+
|
|
5
|
+
- **Resume exact sessions locally or across the fleet with `agents resume <id>` and `agents run <agent|auto> --resume <id>`.** Full IDs use the local SQLite index before any SSH fan-out; remote owners route to the recorded device and version home. Session metadata now records launch mode alongside harness, version, account, cwd, and machine so strict resume reconstructs the original run. Claude, Codex, Grok, Kimi, Droid, and Cursor use their verified version-specific native resume syntax; `run auto --resume` can select another healthy harness/account and continue through `/continue` when native resume is unavailable. Source: `apps/cli/src/commands/{exec,resume,sessions}.ts`, `apps/cli/src/lib/{exec,session/db}.ts`, `packages/session-tracker/src/hook.sh`.
|
|
6
|
+
|
|
7
|
+
- **Hooks: one-level event dirs (`hooks/<event-name>/<script>`) are first-class.**
|
|
8
|
+
System hooks organize by harness event (`session-start/`, `pre-tool-use/`, …).
|
|
9
|
+
Install names stay the file basename. Dirs with top-level scripts expand into
|
|
10
|
+
individual hooks; fixture-only dirs remain directory bundles. Manifest `script:`
|
|
11
|
+
may be a relative path under `hooks/`. Source: `apps/cli/src/lib/hooks.ts`,
|
|
12
|
+
`apps/cli/src/lib/staleness/writers/sources.ts`, `apps/cli/src/lib/versions.ts`,
|
|
13
|
+
`apps/cli/src/lib/__tests__/hooks-nested-groups.test.ts`.
|
|
14
|
+
|
|
15
|
+
- **`agents humans show owner [--json]`** — new command to display the owner config from `~/.agents/humans.yaml`. The file is written automatically on first run when `notify.owner` exists in `agents.yaml`. Source: `apps/cli/src/lib/humans.ts`, `apps/cli/src/commands/humans.ts`.
|
|
16
|
+
|
|
17
|
+
- **`humans.yaml` — typed, versioned owner config.** `~/.agents/humans.yaml` (`version: 1`) now stores owner identity (name, timezone, quiet hours, severity), notification channels, and escalation policy. `notify.owner` in `agents.yaml` is migrated into it on first run and the `notify.owner` key is removed from `agents.yaml`; unrelated keys are preserved. `agents send --to owner` / `agents notify` prefer `humans.yaml` with a fallback to `agents.yaml` during the migration window. Source: `apps/cli/src/lib/humans.ts`, `apps/cli/src/commands/humans.ts`, `apps/cli/src/lib/migrate.ts`.
|
|
18
|
+
|
|
19
|
+
- **`agents memory` ignores `AGENTS.md`, `CLAUDE.md`, `GEMINI.md`, and `MEMORY.md`.** These rule/index files lived in `~/.agents/memory/` but were incorrectly surfaced as memory facts. `isFactFile()` now excludes them by name (case-insensitive). Source: `apps/cli/src/lib/memory.ts`.
|
|
20
|
+
|
|
21
|
+
- **Permissions write path fixed — `groups/` subdirectory.** `installPermissionSet`, `removePermissionSet`, and `savePermissionSet` now all write to the `groups/` subdirectory (matching `discoverPermissionGroups()` which already reads from `groups/`). Source: `apps/cli/src/lib/permissions.ts`.
|
|
22
|
+
|
|
23
|
+
- **Stop eagerly creating webhooks directories.** `ensureAgentsDir()` no longer creates `~/.agents/webhooks/` or `~/.agents/.system/webhooks/` on startup — both dirs are created on first actual use. Source: `apps/cli/src/lib/state.ts`.
|
|
24
|
+
|
|
25
|
+
- **Terminals canonically under `.cache/`.** The stale migration comment that blocked `terminals/` from moving to `~/.agents/.cache/terminals/` is replaced by the actual move. Factory already writes to `.cache/terminals/` (`foreman.registry.ts:9`), so no app-level change is needed. Source: `apps/cli/src/lib/migrate.ts`.
|
|
26
|
+
|
|
27
|
+
- **Menu bar warns when a device is under high load (local or remote).** The
|
|
28
|
+
agents-cli menu bar now shows a `⚠ <device> — high load N%` row in NEEDS YOU when
|
|
29
|
+
a machine's load or memory crosses the `headroom()` "loaded" threshold (≥75%), and
|
|
30
|
+
a red `✕` when critical. The local machine is probed natively via `getloadavg`
|
|
31
|
+
(zero subprocess); fleet peers come from the daemon-warmed `.fleet-stats.json`
|
|
32
|
+
cache with a freshness guard — never the slow `agents doctor` path. Action-required
|
|
33
|
+
rows are now emphasized so items that need you stand out. Source:
|
|
34
|
+
`apps/cli/menubar/Sources/MenubarHelper/LocalState.swift`,
|
|
35
|
+
`apps/cli/menubar/Sources/MenubarHelper/StatusItemController.swift`.
|
|
36
|
+
|
|
37
|
+
- **Projects canonicalization contract.** `agents projects import --from-factory` / `--min-confidence` / `--all` are gone; import is `--from-linear` only, and `~/.agents/factory/projects.json` is never read or migrated. `agents projects list --json` returns definitions only (zero session scan / SSH); `--with-agents` is an explicit opt-in for local active counts. New `agents projects save --json` reads one complete `ProjectDef` from stdin, validates, writes atomically under `~/.agents/projects/`, and prints the saved def. `agents projects rm <name> --json` returns machine-readable success/error. Factory's `managedProjects.ts` shells only through `agents projects list|save|rm` — never reads or writes project YAML/JSON directly, never seeds or migrates legacy Factory state; errors stay explicit for inline UI display. Source: `apps/cli/src/commands/projects.ts`, `apps/cli/src/lib/projects.ts`, `apps/factory/src/core/managedProjects.ts`, `apps/cli/docs/11-projects.md`.
|
|
38
|
+
|
|
39
|
+
- **`ProjectDef` YAML gains `dispatch` block and `linear.name`.** `~/.agents/projects/<name>.yaml` now accepts a `dispatch:` block (`enabled`, `maxAgents`, `provider`, `host`) that opts a project into auto-dispatch and is read directly by `agents __auto-dispatch` — previously these fields lived only in Factory's own registry. `linear.name` stores the Linear project display name alongside the existing `projectId` and `url`. Both fields are optional; existing YAMLs are unchanged. Source: `apps/cli/src/lib/projects.ts`, `apps/cli/src/lib/auto-dispatch.ts`.
|
|
40
|
+
|
|
41
|
+
- **`agents secrets` no longer pops a generic "Agents CLI needs to authenticate" Touch ID sheet on every agent launch.** `listBundles` — which runs on essentially every secrets touch (session-title generation, `agents devices list`, every `agents run`, every remote launch that resolves secrets on the host) — could not ask the keychain for just the bundle *metadata* items: with hashed service names (#316) those names are opaque, so it fell back to a **broad `agents-cli.` keychain scan** that also matched the ACL'd secret *value* items. On machines where a bundle carries a biometric ACL (e.g. a `hold`-tier bundle holding an SSN or a password), macOS evaluated that value ACL during the attributes-only scan and raised a **generic, context-less** Touch ID prompt — on every launch, so a busy fleet felt like a machine-wide bombardment. Neither `kSecUseAuthenticationUIFail` nor `LAContext.interactionNotAllowed` can list the no-ACL items while skipping the ACL'd ones (both return nothing), so the fix is to stop doing the broad scan: a per-machine **no-ACL metadata-name index** (opaque hashes only, in the regenerable helpers dir — it leaks nothing #316 didn't) is read as a silent file instead. The write paths keep it current; an absent/stale index self-heals by rebuilding from the one-time scan, and a missing entry only makes `secrets list` cosmetically incomplete — it never affects a resolve-by-name. Your sensitive bundles keep their biometric gate on real value reads; only the bundle *listing* goes silent. Source: `apps/cli/src/lib/secrets/bundles.ts`.
|
|
42
|
+
|
|
43
|
+
## 1.22.15
|
|
44
|
+
|
|
45
|
+
- **Separate routine definitions from device activation (#2023).** Enable a routine by listing its name in `~/.agents/devices/<hostname>/agents.yaml`; built-in Watchdog setup and `watchdog on|off` now update that host-owned manifest without rewriting the routine definition. Source: `apps/cli/src/lib/routine-activation.ts`, `apps/cli/src/commands/setup-watchdog.ts`.
|
|
46
|
+
|
|
47
|
+
- **`agents devices list` no longer shows the "Leased boxes" section by default — it moves behind a new `--all` flag (RUSH-2190).** Loading the section routes through crabbox's bundle auto-detect, which scans the keychain and can raise a macOS Touch ID sheet *after* the device table has printed, hanging non-interactive callers (observed: the `.agents-system` SessionStart topology hook). The default list now renders only registered devices, which are reachable without any secrets; the load/mem/headroom columns are unchanged (the stats probe was already broker-only). `agents devices list --all` restores the section; `--no-stats` remains a hard "instant, no provider calls" opt-out even with `--all`. Source: `apps/cli/src/commands/ssh.ts` (`showLeasedBoxesSection`), `apps/cli/src/commands/ssh.test.ts`.
|
|
48
|
+
|
|
49
|
+
- **Install: `plugin:` prefix on the unified path (Phase 5 packaging).**
|
|
50
|
+
`agents install plugin:<spec>` uses the same grammar and trust gate as
|
|
51
|
+
`agents plugins install` (`name@url`, local path, `--allow-exec-surfaces`).
|
|
52
|
+
Specialized verbs still work; `agents install` is the one add path for mcp,
|
|
53
|
+
skill, plugin, and GitHub sources. Source: `apps/cli/src/commands/packages.ts`,
|
|
54
|
+
`apps/cli/src/lib/registry.ts`.
|
|
55
|
+
|
|
3
56
|
## 1.22.14
|
|
4
57
|
|
|
5
58
|
- **`agents secrets view <bundle> --reveal` now resolves a locked keychain bundle
|
package/README.md
CHANGED
|
@@ -341,9 +341,11 @@ agents sessions resume # multi-select; packs two sessions pe
|
|
|
341
341
|
agents sessions resume "auth middleware" # pre-filter the pool, then choose
|
|
342
342
|
agents sessions resume --tmux # into persistent tmux — survives editor restarts
|
|
343
343
|
agents sessions resume --host zion --tmux # resume on another machine over SSH
|
|
344
|
+
agents resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 # original harness/version/device/mode
|
|
345
|
+
agents run auto --resume 019fd0c8-b3e9-77a2-a1a4-444698c4d897 # adapt if its account is unavailable
|
|
344
346
|
```
|
|
345
347
|
|
|
346
|
-
`agents sessions resume` reopens sessions in whatever terminal you're in -- auto-detected across iTerm, Ghostty, tmux, and the VSCodium agent-terminal, or forced with `--iterm` / `--ghostty` / `--tmux` / `--vscodium`. Back them with **tmux** and the runs turn durable: detach, close your editor, reboot the GUI -- the session is still alive to `agents tmux attach`. The whole `agents tmux` subsystem (persistent multiplexer sessions that survive editor restarts and can be shared with other tools) sits underneath.
|
|
348
|
+
`agents sessions resume` reopens several sessions in whatever terminal you're in -- auto-detected across iTerm, Ghostty, tmux, and the VSCodium agent-terminal, or forced with `--iterm` / `--ghostty` / `--tmux` / `--vscodium`. `agents resume <id>` resumes one session without requiring you to name its harness: exact IDs take a local SQLite fast path, then resolve fleet-wide and restore the source harness, version, device, cwd, and recorded launch mode. Back them with **tmux** and the runs turn durable: detach, close your editor, reboot the GUI -- the session is still alive to `agents tmux attach`. The whole `agents tmux` subsystem (persistent multiplexer sessions that survive editor restarts and can be shared with other tools) sits underneath.
|
|
347
349
|
|
|
348
350
|
### Send an agent to the background — and bring it back
|
|
349
351
|
|
|
@@ -396,7 +398,7 @@ agents watchdog --nudge # actually inject "Continue." into the stalled split
|
|
|
396
398
|
agents watchdog --watch # daemon loop: a tick every --interval
|
|
397
399
|
```
|
|
398
400
|
|
|
399
|
-
`agents watchdog` detects a stalled session, resolves the *exact* terminal split it lives in (tmux, iTerm, VSCodium, or a raw pty), and injects a nudge -- `Continue.` by default, or set `--text`. It's dry by default; `--nudge` acts on a single tick, and `agents watchdog
|
|
401
|
+
`agents watchdog` detects a stalled session, resolves the *exact* terminal split it lives in (tmux, iTerm, VSCodium, or a raw pty), and injects a nudge -- `Continue.` by default, or set `--text`. It's dry by default; `--nudge` acts on a single tick, and `agents watchdog on|off` enables or disables the built-in routine on this device. `agents setup watchdog` chooses devices. Steer a single run with `agents watchdog policy <id> off | keep | handsoff`.
|
|
400
402
|
|
|
401
403
|
A stalled session whose tail shows a hard account limit ("You've hit your weekly limit · resets …") is **rotated in place** instead of nudged: the watchdog gates on the same healthy-account selection `agents run auto` makes (zero healthy → one skip event per cooldown window, terminal untouched), injects the harness's exit sequence, relaunches `agents run auto --interactive --session-id <uuid>` in the *same* tab, then replays the old session's resume once the new TUI is live. Default on; `agents watchdog rotate off` disables it (nudging stays on).
|
|
402
404
|
|
|
@@ -921,11 +923,11 @@ agents routines run daily-digest # Test it now, ignore the schedule
|
|
|
921
923
|
agents routines logs daily-digest # Last execution — status + report (add --full for raw stdout)
|
|
922
924
|
agents routines stats # Run count, failed, missed, avg/p50/p95 duration — per job or all
|
|
923
925
|
|
|
924
|
-
#
|
|
926
|
+
# Definitions sync to every device; activation is stored per hostname
|
|
925
927
|
agents routines add nightly-drain --schedule "0 3 * * *" --agent claude \
|
|
926
|
-
--
|
|
928
|
+
--prompt "Drain the local work queue"
|
|
927
929
|
|
|
928
|
-
agents routines devices nightly-drain --set yosemite-s0,mac-mini #
|
|
930
|
+
agents routines devices nightly-drain --set yosemite-s0,mac-mini # enable on both hosts
|
|
929
931
|
agents routines list --host yosemite-s0 # query another device
|
|
930
932
|
|
|
931
933
|
# Signed webhook trigger: Linear issue labeled "agent" fires a routine
|
package/dist/bin/agents
CHANGED
|
Binary file
|
package/dist/commands/cloud.js
CHANGED
|
@@ -7,8 +7,7 @@ import { resolveProvider, getAllProviders, getDefaultProviderId } from '../lib/c
|
|
|
7
7
|
import { insertTask, updateTaskStatus, getTaskById, listTasks as listStoredTasks, listActiveTasks } from '../lib/cloud/store.js';
|
|
8
8
|
import { renderStream } from '../lib/cloud/stream.js';
|
|
9
9
|
import { MissingTargetError, MAX_IMAGES_PER_DISPATCH } from '../lib/cloud/types.js';
|
|
10
|
-
import { normalizeTriggerEvent, validateTrigger, writeJob, jobExists, GITHUB_TRIGGER_EVENTS } from '../lib/routines.js';
|
|
11
|
-
import { machineId } from '../lib/machine-id.js';
|
|
10
|
+
import { normalizeTriggerEvent, validateTrigger, writeJob, setJobEnabled, jobExists, GITHUB_TRIGGER_EVENTS } from '../lib/routines.js';
|
|
12
11
|
import { emit } from '../lib/events.js';
|
|
13
12
|
import { shareRuntimeEnv } from '../lib/share/config.js';
|
|
14
13
|
/** Map a supported image file extension to its wire mimeType. Rejects anything else. */
|
|
@@ -310,16 +309,15 @@ Examples:
|
|
|
310
309
|
};
|
|
311
310
|
if (repoValues[0])
|
|
312
311
|
routine.repo = repoValues[0];
|
|
313
|
-
// --host
|
|
314
|
-
//
|
|
315
|
-
// fleet of receivers can't each dispatch a duplicate).
|
|
312
|
+
// --host controls where the enabled local receiver dispatches the run.
|
|
313
|
+
// Device activation remains in this receiver's device manifest.
|
|
316
314
|
if (options.host) {
|
|
317
315
|
routine.host = options.host;
|
|
318
316
|
if (options.remoteCwd)
|
|
319
317
|
routine.remoteCwd = options.remoteCwd;
|
|
320
|
-
routine.devices = [machineId()];
|
|
321
318
|
}
|
|
322
319
|
writeJob(routine);
|
|
320
|
+
setJobEnabled(routine.name, true);
|
|
323
321
|
if (json) {
|
|
324
322
|
console.log(JSON.stringify({ ok: true, registered: routineName, trigger }, null, 2));
|
|
325
323
|
}
|
package/dist/commands/exec.js
CHANGED
|
@@ -532,7 +532,7 @@ export function registerRunCommand(program) {
|
|
|
532
532
|
.option('--headless', 'Force headless mode. Auto-enabled when a prompt is provided; pass explicitly to stay headless with no prompt (reads the prompt from stdin).', false)
|
|
533
533
|
.option('--no-auth-check', 'Skip the pre-launch "looks logged out" warning on an interactive run (advisory; never blocks anyway). Also silenced by AGENTS_NO_AUTH_CHECK=1.')
|
|
534
534
|
.option('-i, --interactive', 'Force interactive mode even when a prompt is provided. Mutually exclusive with --headless.')
|
|
535
|
-
.option('--resume [id]', 'Resume a previous conversation.
|
|
535
|
+
.option('--resume [id]', 'Resume a previous conversation. Full IDs resolve locally first, then fleet-wide. Claude, Codex, Grok, Kimi, Droid, and Cursor use version-gated native resume; other agents replay via /continue. Pair with a prompt to continue headlessly.')
|
|
536
536
|
.option('--session-id <id>', 'Force a NEW conversation to use this exact session UUID (Claude only). This CREATES a session — to resume an existing one, use --resume.')
|
|
537
537
|
.option('--name <slug>', 'Name the run — seeds the session label so it shows up as `<name>` in `agents sessions` and resolves by it (and `agents hosts logs <name>` for --host runs) instead of an opaque id. An agent-generated title later refines the label; your name shows until then. Optional.')
|
|
538
538
|
.option('--notify', 'Post a desktop notification when a headless run finishes. Fired by this process on exit, so it survives whatever launched the run (the menu bar dispatching it, a terminal you closed).')
|
|
@@ -668,7 +668,7 @@ export function registerRunCommand(program) {
|
|
|
668
668
|
|
|
669
669
|
Fallback: --fallback codex,antigravity retries on rate-limit failure via /continue handoff. Each entry accepts @version.
|
|
670
670
|
|
|
671
|
-
Resume: --resume <id>
|
|
671
|
+
Resume: --resume <id> resolves full IDs locally first, then fleet-wide, and restores the source version/device/mode. Claude, Codex, Grok, Kimi, Droid, and Cursor use version-gated native resume; others replay via /continue. agents resume <id> infers the harness too.
|
|
672
672
|
|
|
673
673
|
Passthrough: everything after -- is forwarded verbatim to the underlying agent CLI.
|
|
674
674
|
agents run kimi -- --plan --some-native-flag value
|
|
@@ -781,7 +781,89 @@ export function registerRunCommand(program) {
|
|
|
781
781
|
`Pin a concrete harness instead: agents run <harness>@<version>.`));
|
|
782
782
|
process.exit(1);
|
|
783
783
|
}
|
|
784
|
-
|
|
784
|
+
let autoHarnessRequested = normalizedAgentSpec === RUN_AUTO_KEYWORD;
|
|
785
|
+
let resolvedResumeSource;
|
|
786
|
+
// Concrete resume ids resolve BEFORE placement. Full UUIDs take the local
|
|
787
|
+
// SQLite fast path; only a local miss fans out to the fleet. This lets a
|
|
788
|
+
// command entered on zion discover that the owning version-home is on a
|
|
789
|
+
// worker, while a command entered on that worker never pays for SSH.
|
|
790
|
+
if (typeof options.resume === 'string' && options.resume.trim()) {
|
|
791
|
+
const selector = options.resume.trim();
|
|
792
|
+
const injectedSource = (() => {
|
|
793
|
+
try {
|
|
794
|
+
const parsed = JSON.parse(process.env.AGENTS_RESUME_SOURCE_JSON ?? 'null');
|
|
795
|
+
return parsed?.id === selector ? parsed : undefined;
|
|
796
|
+
}
|
|
797
|
+
catch {
|
|
798
|
+
return undefined;
|
|
799
|
+
}
|
|
800
|
+
})();
|
|
801
|
+
delete process.env.AGENTS_RESUME_SOURCE_JSON;
|
|
802
|
+
const outcome = injectedSource
|
|
803
|
+
? { kind: 'resolved', session: injectedSource }
|
|
804
|
+
: await (await import('./sessions.js')).resolveSessionMetadataValue(selector);
|
|
805
|
+
if (outcome.kind === 'partial') {
|
|
806
|
+
console.error(chalk.red(`Could not resolve session while these devices were unavailable: ${outcome.failedPeers.join(', ')}`));
|
|
807
|
+
process.exit(2);
|
|
808
|
+
}
|
|
809
|
+
if (outcome.kind === 'not-found') {
|
|
810
|
+
console.error(chalk.red(`No session matching "${selector}".`));
|
|
811
|
+
process.exit(1);
|
|
812
|
+
}
|
|
813
|
+
if (outcome.kind === 'ambiguous') {
|
|
814
|
+
console.error(chalk.red(`"${selector}" matches ${outcome.candidates.length} sessions. Pass the full session id.`));
|
|
815
|
+
process.exit(1);
|
|
816
|
+
}
|
|
817
|
+
resolvedResumeSource = outcome.session;
|
|
818
|
+
const [requestedAgent, requestedVersion] = normalizedAgentSpec.split('@');
|
|
819
|
+
if (!autoHarnessRequested && requestedAgent !== resolvedResumeSource.agent) {
|
|
820
|
+
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} belongs to ${resolvedResumeSource.agent}, not ${requestedAgent}. ` +
|
|
821
|
+
`Use: agents resume ${resolvedResumeSource.id}`));
|
|
822
|
+
process.exit(1);
|
|
823
|
+
}
|
|
824
|
+
if (!autoHarnessRequested && requestedVersion && requestedVersion !== resolvedResumeSource.version) {
|
|
825
|
+
console.error(chalk.red(`Session ${resolvedResumeSource.shortId} started with ${resolvedResumeSource.agent}@${resolvedResumeSource.version ?? 'unknown'}, ` +
|
|
826
|
+
`not @${requestedVersion}.`));
|
|
827
|
+
process.exit(1);
|
|
828
|
+
}
|
|
829
|
+
// Omitted --mode inherits the effective source mode. Commander keeps
|
|
830
|
+
// the normal new-run default as a real value, so consult its provenance
|
|
831
|
+
// rather than mistaking the default for an explicit override.
|
|
832
|
+
if (command.getOptionValueSource('mode') === 'default') {
|
|
833
|
+
if (resolvedResumeSource.mode)
|
|
834
|
+
options.mode = resolvedResumeSource.mode;
|
|
835
|
+
else if (!options.quiet)
|
|
836
|
+
process.stderr.write(chalk.yellow(`[agents] session ${resolvedResumeSource.shortId} predates stored launch modes; using --mode ${options.mode}\n`));
|
|
837
|
+
}
|
|
838
|
+
const { machineId } = await import('../lib/machine-id.js');
|
|
839
|
+
const sourceMachine = resolvedResumeSource.machine;
|
|
840
|
+
const explicitPlacement = hostTargetGiven(options).length > 0;
|
|
841
|
+
if (sourceMachine && sourceMachine !== machineId() && !explicitPlacement) {
|
|
842
|
+
options.host = sourceMachine;
|
|
843
|
+
}
|
|
844
|
+
else if (!autoHarnessRequested && sourceMachine && sourceMachine !== machineId() && explicitPlacement && !hostTargetGiven(options).includes(sourceMachine)) {
|
|
845
|
+
console.error(chalk.red(`Strict resume must run on ${sourceMachine}, where session ${resolvedResumeSource.shortId} is owned. ` +
|
|
846
|
+
`Use agents run auto --resume ${resolvedResumeSource.id} to hand off elsewhere.`));
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
// On the owning machine, `auto` first asks the existing account router
|
|
850
|
+
// whether the exact source version is healthy. If it is, native resume
|
|
851
|
+
// wins. If not, keep `auto` so the normal harness/account router selects
|
|
852
|
+
// a healthy local target and the later resume block performs /continue.
|
|
853
|
+
if (autoHarnessRequested && (!sourceMachine || sourceMachine === machineId())) {
|
|
854
|
+
const sourceAgent = resolvedResumeSource.agent;
|
|
855
|
+
if (sourceAgent in AGENTS && resolvedResumeSource.version) {
|
|
856
|
+
const { resolveRunVersion } = await import('../lib/rotate.js');
|
|
857
|
+
const health = await resolveRunVersion(sourceAgent, 'available', resolvedResumeSource.cwd ?? process.cwd());
|
|
858
|
+
if (!health.exhausted && health.version === resolvedResumeSource.version) {
|
|
859
|
+
normalizedAgentSpec = `${sourceAgent}@${resolvedResumeSource.version}`;
|
|
860
|
+
autoHarnessRequested = false;
|
|
861
|
+
if (!options.quiet)
|
|
862
|
+
process.stderr.write(chalk.gray(`[agents] auto resume → native ${normalizedAgentSpec} on ${sourceMachine ?? machineId()}\n`));
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
785
867
|
if (autoHarnessRequested) {
|
|
786
868
|
// `auto` is reserved. If a future harness registers that id, the
|
|
787
869
|
// keyword collides — fail loud rather than silently shadow the harness.
|
|
@@ -797,7 +879,7 @@ export function registerRunCommand(program) {
|
|
|
797
879
|
// Host layer: with no explicit --host/--device, default to the
|
|
798
880
|
// affinity pick. Skipped on a host-dispatched run — its dispatcher
|
|
799
881
|
// already resolved this layer (see runAutoDefaultsToAffinity).
|
|
800
|
-
if (runAutoDefaultsToAffinity(options))
|
|
882
|
+
if (!resolvedResumeSource && runAutoDefaultsToAffinity(options))
|
|
801
883
|
options.device = 'auto';
|
|
802
884
|
}
|
|
803
885
|
// --device auto / --host auto (and deprecated --smart): affinity-pick host.
|
|
@@ -2013,7 +2095,8 @@ export function registerRunCommand(program) {
|
|
|
2013
2095
|
// AgentId is wider than SessionAgentId (amp/kiro/goose/copilot keep no transcripts);
|
|
2014
2096
|
// those simply yield no matches and fall through to the not-found error.
|
|
2015
2097
|
const sessionAgent = agent;
|
|
2016
|
-
|
|
2098
|
+
if (!resolvedResumeSource)
|
|
2099
|
+
await discoverSessions({ agent: sessionAgent, version });
|
|
2017
2100
|
// Resume is interactive unless a follow-on prompt makes it headless.
|
|
2018
2101
|
const wantsInteractive = resolveInteractive({ interactive: options.interactive, headless: options.headless, prompt });
|
|
2019
2102
|
const idArg = typeof options.resume === 'string' ? options.resume.trim() : '';
|
|
@@ -2024,9 +2107,9 @@ export function registerRunCommand(program) {
|
|
|
2024
2107
|
catch {
|
|
2025
2108
|
scopeCwd = cwd;
|
|
2026
2109
|
}
|
|
2027
|
-
let session;
|
|
2110
|
+
let session = resolvedResumeSource;
|
|
2028
2111
|
if (idArg) {
|
|
2029
|
-
let matches = findSessionsById(idArg, { agent: sessionAgent, version, cwd: scopeCwd });
|
|
2112
|
+
let matches = session ? [session] : findSessionsById(idArg, { agent: sessionAgent, version, cwd: scopeCwd });
|
|
2030
2113
|
if (matches.length === 0) {
|
|
2031
2114
|
const wide = findSessionsById(idArg, { agent: sessionAgent, version });
|
|
2032
2115
|
if (wide.length > 0) {
|
|
@@ -2076,10 +2159,12 @@ export function registerRunCommand(program) {
|
|
|
2076
2159
|
session = picked.session;
|
|
2077
2160
|
forceInteractive = true; // bare resume always lands in the agent's TUI
|
|
2078
2161
|
}
|
|
2079
|
-
//
|
|
2080
|
-
//
|
|
2081
|
-
version
|
|
2082
|
-
|
|
2162
|
+
// Native resume is valid only for the source harness + exact isolated
|
|
2163
|
+
// version. `auto` may have selected another healthy harness/account; in
|
|
2164
|
+
// that case keep the target version and hand off through /continue.
|
|
2165
|
+
const canResumeNatively = session.agent === agent && nativeResume(agent, session.version);
|
|
2166
|
+
if (canResumeNatively) {
|
|
2167
|
+
version = session.version;
|
|
2083
2168
|
resumeNative = true;
|
|
2084
2169
|
resumeSessionId = session.id;
|
|
2085
2170
|
// Native `--resume` (claude/codex) resolves the transcript relative to the
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents humans` — owner identity and notification channel management.
|
|
3
|
+
*
|
|
4
|
+
* Reads from ~/.agents/humans.yaml (created by migration from owner.md and
|
|
5
|
+
* agents.yaml notify.owner). Provides inspection commands for the current
|
|
6
|
+
* owner config.
|
|
7
|
+
*/
|
|
8
|
+
import type { Command } from 'commander';
|
|
9
|
+
/** Register the `agents humans` command tree. */
|
|
10
|
+
export declare function registerHumansCommands(program: Command): void;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `agents humans` — owner identity and notification channel management.
|
|
3
|
+
*
|
|
4
|
+
* Reads from ~/.agents/humans.yaml (created by migration from owner.md and
|
|
5
|
+
* agents.yaml notify.owner). Provides inspection commands for the current
|
|
6
|
+
* owner config.
|
|
7
|
+
*/
|
|
8
|
+
import { setHelpSections } from '../lib/help.js';
|
|
9
|
+
import { readHumans, getOwnerFromHumans } from '../lib/humans.js';
|
|
10
|
+
/** Register the `agents humans` command tree. */
|
|
11
|
+
export function registerHumansCommands(program) {
|
|
12
|
+
const humansCmd = program
|
|
13
|
+
.command('humans')
|
|
14
|
+
.description('Inspect owner identity and notification channel config (humans.yaml)');
|
|
15
|
+
setHelpSections(humansCmd, {
|
|
16
|
+
examples: `
|
|
17
|
+
# Show owner identity and channel config as JSON
|
|
18
|
+
agents humans show owner --json
|
|
19
|
+
|
|
20
|
+
# Show owner identity in human-readable form
|
|
21
|
+
agents humans show owner
|
|
22
|
+
`,
|
|
23
|
+
notes: `
|
|
24
|
+
humans.yaml is the canonical owner identity file at ~/.agents/humans.yaml.
|
|
25
|
+
It is populated by migrating notify.owner from agents.yaml and frontmatter
|
|
26
|
+
from owner.md. Use \`agents humans show owner\` to inspect the current config.
|
|
27
|
+
`,
|
|
28
|
+
});
|
|
29
|
+
const showCmd = humansCmd
|
|
30
|
+
.command('show')
|
|
31
|
+
.description('Show config from humans.yaml');
|
|
32
|
+
setHelpSections(showCmd, {
|
|
33
|
+
examples: `
|
|
34
|
+
agents humans show owner
|
|
35
|
+
agents humans show owner --json
|
|
36
|
+
`,
|
|
37
|
+
});
|
|
38
|
+
showCmd
|
|
39
|
+
.command('owner')
|
|
40
|
+
.description('Show the configured owner identity and notification channels')
|
|
41
|
+
.option('--json', 'Output as JSON')
|
|
42
|
+
.action((opts) => {
|
|
43
|
+
const humans = readHumans();
|
|
44
|
+
if (!humans) {
|
|
45
|
+
if (opts.json) {
|
|
46
|
+
process.stdout.write(JSON.stringify(null) + '\n');
|
|
47
|
+
}
|
|
48
|
+
else {
|
|
49
|
+
process.stderr.write('humans.yaml not found — run `agents` once to trigger migration\n');
|
|
50
|
+
process.exitCode = 1;
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const owner = getOwnerFromHumans();
|
|
55
|
+
if (opts.json) {
|
|
56
|
+
process.stdout.write(JSON.stringify(owner ?? null, null, 2) + '\n');
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
if (!owner) {
|
|
60
|
+
process.stdout.write('No owner configured in humans.yaml\n');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
if (owner.name)
|
|
64
|
+
process.stdout.write(`Name: ${owner.name}\n`);
|
|
65
|
+
if (owner.timezone)
|
|
66
|
+
process.stdout.write(`Timezone: ${owner.timezone}\n`);
|
|
67
|
+
if (owner.quiet_hours)
|
|
68
|
+
process.stdout.write(`Quiet: ${owner.quiet_hours}\n`);
|
|
69
|
+
if (owner.default_severity)
|
|
70
|
+
process.stdout.write(`Severity: ${owner.default_severity}\n`);
|
|
71
|
+
if (owner.notify) {
|
|
72
|
+
process.stdout.write(`Notify: channel=${owner.notify.channel} to=${owner.notify.to}\n`);
|
|
73
|
+
}
|
|
74
|
+
if (owner.channels?.length) {
|
|
75
|
+
process.stdout.write(`Channels:\n`);
|
|
76
|
+
for (const ch of owner.channels) {
|
|
77
|
+
const extra = [ch.transport, ch.intrusive ? 'intrusive' : null].filter(Boolean).join(', ');
|
|
78
|
+
process.stdout.write(` - ${ch.id}${extra ? ` (${extra})` : ''}\n`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (owner.policy) {
|
|
82
|
+
process.stdout.write(`Policy:\n`);
|
|
83
|
+
for (const [sev, channels] of Object.entries(owner.policy)) {
|
|
84
|
+
if (Array.isArray(channels) && channels.length) {
|
|
85
|
+
process.stdout.write(` ${sev}: ${channels.join(', ')}\n`);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
}
|
|
@@ -22,7 +22,7 @@ import { discoverWorkflowsFromRepo, installWorkflowCentrally, } from '../lib/wor
|
|
|
22
22
|
import { discoverSubagentsFromRepo, installSubagentCentrally, } from '../lib/subagents.js';
|
|
23
23
|
import { discoverPermissionsFromRepo, installPermissionSet, } from '../lib/permissions.js';
|
|
24
24
|
import { listInstalledVersions, resolveConfiguredAgentTargets, syncResourcesToVersion, } from '../lib/versions.js';
|
|
25
|
-
import { isInteractiveTerminal, isPromptCancelled, parseCommaSeparatedList, requireDestructiveArg, requireInteractiveSelection, resolveInstalledAgentTargetsAutoInstalling, } from './utils.js';
|
|
25
|
+
import { formatPath, isInteractiveTerminal, isPromptCancelled, parseCommaSeparatedList, requireDestructiveArg, requireInteractiveSelection, resolveInstalledAgentTargetsAutoInstalling, } from './utils.js';
|
|
26
26
|
import { itemPicker } from '../lib/picker.js';
|
|
27
27
|
import { registerMcpCommandToTargets, discoverMcpConfigsFromRepo, installMcpConfigCentrally, } from '../lib/mcp.js';
|
|
28
28
|
export function buildMcpPackageCommand(pkg) {
|
|
@@ -435,37 +435,30 @@ the sha256 in the index and abort on mismatch.
|
|
|
435
435
|
// ==========================================================================
|
|
436
436
|
program
|
|
437
437
|
.command('install <identifier>')
|
|
438
|
-
.description('Install a package
|
|
438
|
+
.description('Install a package: mcp:, skill:, plugin:, or GitHub (gh:user/repo) — one install path (Phase 5)')
|
|
439
439
|
.option('-a, --agents <list>', 'Targets: claude, codex@0.116.0, or cursor@default')
|
|
440
440
|
.option('--types <list>', 'When source is a repo: comma-separated resource types to install (skills,workflows,commands,hooks,permissions,subagents,mcp)')
|
|
441
441
|
.option('--names <list>', 'When source is a repo: comma-separated resource names within the selected types')
|
|
442
442
|
.option('-y, --yes', 'Auto-install any missing agent versions without prompting')
|
|
443
|
+
.option('--allow-exec-surfaces', 'With plugin: allow plugins that ship hooks/mcp/bin (same as agents plugins install)')
|
|
443
444
|
.addHelpText('after', `
|
|
444
|
-
Install
|
|
445
|
+
Install is the unified add path (Phase 5 packaging). Prefix the identifier:
|
|
446
|
+
|
|
447
|
+
mcp:<name> MCP server from a registry
|
|
448
|
+
skill:<name> skill from a registry (or gh: fallback)
|
|
449
|
+
plugin:<spec> plugin — same grammar as agents plugins install (name@url or path)
|
|
450
|
+
gh:user/repo clone a DotAgents / multi-resource repo and install selected types
|
|
445
451
|
|
|
446
452
|
Examples:
|
|
447
|
-
# Install an MCP server from a registry
|
|
448
453
|
agents install mcp:notion --agents claude
|
|
449
|
-
|
|
450
|
-
|
|
454
|
+
agents install skill:animator --agents claude,codex
|
|
455
|
+
agents install plugin:my-plugin@https://github.com/user/my-plugin.git
|
|
456
|
+
agents install plugin:~/Projects/rush-toolkit
|
|
451
457
|
agents install gh:anthropics/skills --agents codex,claude
|
|
452
|
-
|
|
453
|
-
# Install using GitHub shorthand
|
|
454
|
-
agents install gh:user/repo --agents claude@2.1.112
|
|
455
|
-
|
|
456
|
-
# Install only specific resource types from a multi-resource repo
|
|
457
458
|
agents install gh:phnx-labs/.agents-system --types skills,workflows --agents claude@all
|
|
458
459
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
# Install to all installed agents (uses defaults or prompts)
|
|
463
|
-
agents install mcp:postgres
|
|
464
|
-
|
|
465
|
-
When to use:
|
|
466
|
-
- After search: 'agents search notion' then 'agents install mcp:notion'
|
|
467
|
-
- Team setup: 'agents install gh:team/resources' to sync everyone's tooling
|
|
468
|
-
- Quick MCP add: 'agents install mcp:<name>' when you know the package name
|
|
460
|
+
Specialized verbs still work (agents plugins install, agents skills add) and
|
|
461
|
+
delegate to the same underlying installers.
|
|
469
462
|
`)
|
|
470
463
|
.action(async (identifier, options) => {
|
|
471
464
|
const spinner = ora('Resolving package...').start();
|
|
@@ -473,10 +466,85 @@ When to use:
|
|
|
473
466
|
const resolved = await resolvePackage(identifier);
|
|
474
467
|
if (!resolved) {
|
|
475
468
|
spinner.fail('Package not found');
|
|
476
|
-
console.log(chalk.gray('\nTip: Use explicit prefix (mcp:, skill:, gh:) or check the identifier.'));
|
|
469
|
+
console.log(chalk.gray('\nTip: Use explicit prefix (mcp:, skill:, plugin:, gh:) or check the identifier.'));
|
|
477
470
|
process.exit(1);
|
|
478
471
|
}
|
|
479
472
|
spinner.succeed(`Found ${resolved.type} package`);
|
|
473
|
+
if (resolved.type === 'plugin') {
|
|
474
|
+
spinner.stop();
|
|
475
|
+
const spec = resolved.pluginSpec ?? resolved.source;
|
|
476
|
+
console.log(chalk.gray(`Installing plugin from: ${spec}`));
|
|
477
|
+
const { installPlugin, getPlugin, inspectPluginCapabilities, pluginCapabilityLabels, parseInstallSpec, checkPluginDependencies, hasPluginExecSurfaces, pluginSupportsAgent, syncPluginToVersion, pluginResourceGroups, } = await import('../lib/plugins.js');
|
|
478
|
+
const { listInstalledVersions, getGlobalDefault, getVersionHomePath, syncResourcesToVersion } = await import('../lib/versions.js');
|
|
479
|
+
const { agentLabel } = await import('../lib/agents.js');
|
|
480
|
+
let name;
|
|
481
|
+
let root;
|
|
482
|
+
try {
|
|
483
|
+
const result = await installPlugin(spec);
|
|
484
|
+
name = result.name;
|
|
485
|
+
root = result.root;
|
|
486
|
+
}
|
|
487
|
+
catch (err) {
|
|
488
|
+
console.log(chalk.red(`Install failed: ${err.message}`));
|
|
489
|
+
process.exit(1);
|
|
490
|
+
}
|
|
491
|
+
const plugin = getPlugin(name);
|
|
492
|
+
if (!plugin) {
|
|
493
|
+
console.log(chalk.red(`Installed but could not load plugin '${name}'`));
|
|
494
|
+
process.exit(1);
|
|
495
|
+
}
|
|
496
|
+
const capabilities = inspectPluginCapabilities(root);
|
|
497
|
+
const allowExec = options.allowExecSurfaces === true;
|
|
498
|
+
if (hasPluginExecSurfaces(capabilities) && !allowExec) {
|
|
499
|
+
const source = parseInstallSpec(spec).source;
|
|
500
|
+
console.error(chalk.red('Install refused: plugin ships executable surfaces:'));
|
|
501
|
+
for (const label of pluginCapabilityLabels(capabilities)) {
|
|
502
|
+
console.error(` ${label}`);
|
|
503
|
+
}
|
|
504
|
+
console.error(`Re-run with --allow-exec-surfaces if you trust the source: ${source}@HEAD`);
|
|
505
|
+
fs.rmSync(root, { recursive: true, force: true });
|
|
506
|
+
process.exit(1);
|
|
507
|
+
}
|
|
508
|
+
const missingDeps = checkPluginDependencies(plugin.manifest);
|
|
509
|
+
if (missingDeps.length > 0) {
|
|
510
|
+
console.log(chalk.yellow(`Warning: missing dependencies: ${missingDeps.join(', ')}`));
|
|
511
|
+
console.log(chalk.gray('Install them with: agents install plugin:<name>@<source>'));
|
|
512
|
+
}
|
|
513
|
+
// Same sync loop as `agents plugins install` (default version per harness).
|
|
514
|
+
console.log();
|
|
515
|
+
let synced = 0;
|
|
516
|
+
for (const agentId of capableAgents('plugins')) {
|
|
517
|
+
if (!pluginSupportsAgent(plugin, agentId))
|
|
518
|
+
continue;
|
|
519
|
+
const versions = listInstalledVersions(agentId);
|
|
520
|
+
if (versions.length === 0)
|
|
521
|
+
continue;
|
|
522
|
+
const defaultVer = getGlobalDefault(agentId);
|
|
523
|
+
const targetVersions = defaultVer ? [defaultVer] : [versions[versions.length - 1]];
|
|
524
|
+
for (const version of targetVersions) {
|
|
525
|
+
const didSync = allowExec
|
|
526
|
+
? syncPluginToVersion(plugin, agentId, getVersionHomePath(agentId, version), {
|
|
527
|
+
allowExecSurfaces: true,
|
|
528
|
+
}).success
|
|
529
|
+
: syncResourcesToVersion(agentId, version, { plugins: [name] }).plugins.length > 0;
|
|
530
|
+
if (didSync) {
|
|
531
|
+
console.log(chalk.green(` Synced to ${agentLabel(agentId)}@${version}`));
|
|
532
|
+
synced++;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
if (synced === 0) {
|
|
537
|
+
console.log(chalk.gray(' No supported agent versions installed — run "agents use <agent>@<version>" to sync.'));
|
|
538
|
+
}
|
|
539
|
+
console.log(chalk.bold(`\nInstalled ${plugin.name} v${plugin.manifest.version} to ${formatPath(root)}`));
|
|
540
|
+
const groups = pluginResourceGroups(plugin);
|
|
541
|
+
if (groups.length > 0) {
|
|
542
|
+
for (const g of groups) {
|
|
543
|
+
console.log(chalk.gray(` ${g.label}: ${g.items.slice(0, 8).join(', ')}${g.items.length > 8 ? '…' : ''}`));
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
480
548
|
if (resolved.type === 'mcp') {
|
|
481
549
|
// Install MCP server
|
|
482
550
|
const entry = resolved.mcpEntry;
|
|
@@ -24,11 +24,8 @@ export declare function formatFleetSkippedNote(skipped: string[]): string;
|
|
|
24
24
|
*
|
|
25
25
|
* `stderr: 'ignore'` is load-bearing, not tidiness. A checkout with no origin
|
|
26
26
|
* makes git print `error: No such remote 'origin'` on ITS stderr, which is the
|
|
27
|
-
* terminal's — the catch below never sees it.
|
|
28
|
-
*
|
|
29
|
-
* `import --from-factory` started calling it per row: importing 12 registry
|
|
30
|
-
* rows printed two raw git errors between the progress lines. Absence of a
|
|
31
|
-
* remote is an expected answer here (`undefined`), not something to report.
|
|
27
|
+
* terminal's — the catch below never sees it. Absence of a remote is an
|
|
28
|
+
* expected answer here (`undefined`), not something to report.
|
|
32
29
|
*/
|
|
33
30
|
export declare function originSlug(cwd: string): string | undefined;
|
|
34
31
|
/** One `projects list` row, pre-render. */
|