@phnx-labs/agents-cli 1.14.7 → 1.16.0
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 +78 -39
- package/README.md +74 -7
- package/dist/commands/alias.js +2 -2
- package/dist/commands/beta.js +6 -1
- package/dist/commands/browser-picker.d.ts +21 -0
- package/dist/commands/browser-picker.js +114 -0
- package/dist/commands/browser.js +546 -75
- package/dist/commands/commands.js +72 -22
- package/dist/commands/daemon.js +2 -2
- package/dist/commands/exec.js +9 -2
- package/dist/commands/fork.js +2 -2
- package/dist/commands/hooks.js +71 -26
- package/dist/commands/mcp.js +85 -43
- package/dist/commands/plugins.js +48 -15
- package/dist/commands/prune.d.ts +0 -20
- package/dist/commands/prune.js +291 -16
- package/dist/commands/pull.js +3 -3
- package/dist/commands/repo.js +1 -1
- package/dist/commands/routines.js +2 -2
- package/dist/commands/secrets.js +37 -1
- package/dist/commands/sessions.js +62 -19
- package/dist/commands/{init.d.ts → setup.d.ts} +7 -6
- package/dist/commands/{init.js → setup.js} +32 -21
- package/dist/commands/skills.js +60 -19
- package/dist/commands/subagents.js +41 -13
- package/dist/commands/teams.js +2 -3
- package/dist/commands/usage.js +6 -0
- package/dist/commands/utils.d.ts +16 -0
- package/dist/commands/utils.js +32 -0
- package/dist/commands/versions.js +8 -6
- package/dist/commands/view.js +61 -16
- package/dist/index.d.ts +1 -1
- package/dist/index.js +17 -20
- package/dist/lib/agents.js +2 -2
- package/dist/lib/auto-pull-worker.js +2 -3
- package/dist/lib/auto-pull.js +2 -2
- package/dist/lib/browser/cdp.d.ts +7 -1
- package/dist/lib/browser/cdp.js +29 -1
- package/dist/lib/browser/chrome.js +6 -3
- package/dist/lib/browser/devices.d.ts +4 -0
- package/dist/lib/browser/devices.js +27 -0
- package/dist/lib/browser/drivers/local.js +9 -4
- package/dist/lib/browser/drivers/ssh.d.ts +1 -0
- package/dist/lib/browser/drivers/ssh.js +32 -4
- package/dist/lib/browser/ipc.js +145 -23
- package/dist/lib/browser/profiles.d.ts +5 -2
- package/dist/lib/browser/profiles.js +77 -37
- package/dist/lib/browser/service.d.ts +84 -13
- package/dist/lib/browser/service.js +806 -122
- package/dist/lib/browser/types.d.ts +81 -3
- package/dist/lib/browser/types.js +16 -0
- package/dist/lib/cloud/rush.js +2 -2
- package/dist/lib/cloud/store.js +2 -2
- package/dist/lib/commands.d.ts +1 -0
- package/dist/lib/commands.js +6 -2
- package/dist/lib/daemon.js +6 -7
- package/dist/lib/doctor-diff.js +4 -4
- package/dist/lib/events.d.ts +94 -1
- package/dist/lib/events.js +264 -6
- package/dist/lib/exec.js +16 -10
- package/dist/lib/hooks.d.ts +11 -7
- package/dist/lib/hooks.js +125 -49
- package/dist/lib/migrate.d.ts +1 -1
- package/dist/lib/migrate.js +1178 -21
- package/dist/lib/models.js +2 -2
- package/dist/lib/permissions.d.ts +14 -11
- package/dist/lib/permissions.js +46 -42
- package/dist/lib/plugins.d.ts +30 -1
- package/dist/lib/plugins.js +75 -3
- package/dist/lib/pty-server.js +9 -10
- package/dist/lib/resources/hooks.d.ts +5 -1
- package/dist/lib/resources/hooks.js +21 -4
- package/dist/lib/rotate.js +3 -4
- package/dist/lib/routines.d.ts +15 -0
- package/dist/lib/routines.js +68 -0
- package/dist/lib/runner.js +9 -5
- package/dist/lib/secrets/index.d.ts +14 -11
- package/dist/lib/secrets/index.js +49 -21
- package/dist/lib/secrets/linux.d.ts +27 -0
- package/dist/lib/secrets/linux.js +161 -0
- package/dist/lib/session/active.d.ts +3 -0
- package/dist/lib/session/active.js +92 -6
- package/dist/lib/session/cloud.js +2 -2
- package/dist/lib/session/db.d.ts +4 -0
- package/dist/lib/session/db.js +34 -3
- package/dist/lib/session/discover.js +30 -15
- package/dist/lib/session/team-filter.js +2 -2
- package/dist/lib/shims.d.ts +2 -2
- package/dist/lib/shims.js +6 -6
- package/dist/lib/skills.js +6 -2
- package/dist/lib/state.d.ts +86 -14
- package/dist/lib/state.js +150 -23
- package/dist/lib/subagents.d.ts +28 -0
- package/dist/lib/subagents.js +98 -1
- package/dist/lib/sync-manifest.d.ts +1 -1
- package/dist/lib/sync-manifest.js +3 -3
- package/dist/lib/teams/persistence.js +15 -5
- package/dist/lib/teams/registry.js +2 -2
- package/dist/lib/types.d.ts +32 -3
- package/dist/lib/types.js +3 -3
- package/dist/lib/usage.d.ts +1 -1
- package/dist/lib/usage.js +15 -48
- package/dist/lib/versions.js +31 -21
- package/package.json +1 -1
- package/scripts/postinstall.js +37 -9
|
@@ -4,14 +4,14 @@ import * as fs from 'fs';
|
|
|
4
4
|
import * as os from 'os';
|
|
5
5
|
import * as path from 'path';
|
|
6
6
|
import { checkbox } from '@inquirer/prompts';
|
|
7
|
-
import { ALL_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
7
|
+
import { AGENTS, ALL_AGENT_IDS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
8
8
|
import { cloneRepo } from '../lib/git.js';
|
|
9
|
-
import { discoverCommands, resolveCommandSource, installCommandCentrally,
|
|
9
|
+
import { discoverCommands, resolveCommandSource, installCommandCentrally, listCentralCommands, listInstalledCommandsWithScope, getCommandInfo, diffVersionCommands, iterCommandsCapableVersions, removeCommandFromVersion, } from '../lib/commands.js';
|
|
10
10
|
import { getCommandsDir } from '../lib/state.js';
|
|
11
11
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
12
|
-
import { getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, resolveAgentVersionTargets, } from '../lib/versions.js';
|
|
12
|
+
import { getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, resolveAgentVersionTargets, } from '../lib/versions.js';
|
|
13
13
|
import { recordVersionResources } from '../lib/state.js';
|
|
14
|
-
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, } from './utils.js';
|
|
14
|
+
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, promptRemovalTargets, } from './utils.js';
|
|
15
15
|
/** Register the `agents commands` command tree (list, add, remove, sync, prune, view). */
|
|
16
16
|
export function registerCommandsCommands(program) {
|
|
17
17
|
const commandsCmd = program
|
|
@@ -271,15 +271,29 @@ Examples:
|
|
|
271
271
|
agents commands remove
|
|
272
272
|
`)
|
|
273
273
|
.action(async (name, options) => {
|
|
274
|
+
const cmdTargetMap = new Map();
|
|
275
|
+
for (const { agent, version } of iterCommandsCapableVersions()) {
|
|
276
|
+
const home = getVersionHomePath(agent, version);
|
|
277
|
+
const commands = listInstalledCommandsWithScope(agent, process.cwd(), { home });
|
|
278
|
+
for (const cmd of commands) {
|
|
279
|
+
if (cmd.scope !== 'user')
|
|
280
|
+
continue;
|
|
281
|
+
const existing = cmdTargetMap.get(cmd.name);
|
|
282
|
+
if (existing) {
|
|
283
|
+
existing.targets.push({ agent, version });
|
|
284
|
+
}
|
|
285
|
+
else {
|
|
286
|
+
cmdTargetMap.set(cmd.name, { name: cmd.name, targets: [{ agent, version }] });
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
}
|
|
274
290
|
let commandsToRemove;
|
|
275
291
|
if (name) {
|
|
276
292
|
commandsToRemove = [name];
|
|
277
293
|
}
|
|
278
294
|
else {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
if (centralCommands.length === 0) {
|
|
282
|
-
console.log(chalk.yellow('No commands installed.'));
|
|
295
|
+
if (cmdTargetMap.size === 0) {
|
|
296
|
+
console.log(chalk.yellow('No commands installed in any version.'));
|
|
283
297
|
return;
|
|
284
298
|
}
|
|
285
299
|
if (!isInteractiveTerminal()) {
|
|
@@ -288,12 +302,16 @@ Examples:
|
|
|
288
302
|
]);
|
|
289
303
|
}
|
|
290
304
|
try {
|
|
305
|
+
const choices = Array.from(cmdTargetMap.values()).map((cmd) => {
|
|
306
|
+
const agents = [...new Set(cmd.targets.map((t) => AGENTS[t.agent].name))];
|
|
307
|
+
return {
|
|
308
|
+
value: cmd.name,
|
|
309
|
+
name: `${cmd.name} (${agents.join(', ')})`,
|
|
310
|
+
};
|
|
311
|
+
});
|
|
291
312
|
const selected = await checkbox({
|
|
292
313
|
message: 'Select commands to remove',
|
|
293
|
-
choices
|
|
294
|
-
value: cmd,
|
|
295
|
-
name: cmd,
|
|
296
|
-
})),
|
|
314
|
+
choices,
|
|
297
315
|
});
|
|
298
316
|
if (selected.length === 0) {
|
|
299
317
|
console.log(chalk.gray('No commands selected.'));
|
|
@@ -309,20 +327,52 @@ Examples:
|
|
|
309
327
|
throw err;
|
|
310
328
|
}
|
|
311
329
|
}
|
|
312
|
-
|
|
313
|
-
? options.agents.split(',')
|
|
314
|
-
: ALL_AGENT_IDS;
|
|
330
|
+
let removed = 0;
|
|
315
331
|
for (const cmdName of commandsToRemove) {
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
332
|
+
const cmdInfo = cmdTargetMap.get(cmdName);
|
|
333
|
+
if (!cmdInfo || cmdInfo.targets.length === 0) {
|
|
334
|
+
console.log(chalk.yellow(` Command '${cmdName}' not found in any version.`));
|
|
335
|
+
continue;
|
|
336
|
+
}
|
|
337
|
+
// Filter by --agents if specified
|
|
338
|
+
let availableTargets = cmdInfo.targets;
|
|
339
|
+
if (options?.agents) {
|
|
340
|
+
const requestedAgents = new Set(options.agents.split(','));
|
|
341
|
+
availableTargets = availableTargets.filter((t) => requestedAgents.has(t.agent));
|
|
342
|
+
}
|
|
343
|
+
if (availableTargets.length === 0) {
|
|
344
|
+
console.log(chalk.yellow(` Command '${cmdName}' not found in specified agents.`));
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
const removalTargets = availableTargets.map((t) => ({
|
|
348
|
+
agent: t.agent,
|
|
349
|
+
version: t.version,
|
|
350
|
+
label: `${agentLabel(t.agent)}@${t.version}`,
|
|
351
|
+
}));
|
|
352
|
+
const selectedTargets = await promptRemovalTargets(cmdName, removalTargets, {
|
|
353
|
+
skipPrompt: !!options?.agents,
|
|
354
|
+
});
|
|
355
|
+
if (selectedTargets.length === 0) {
|
|
356
|
+
console.log(chalk.gray(` Skipped '${cmdName}'.`));
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
for (const target of selectedTargets) {
|
|
360
|
+
const result = removeCommandFromVersion(target.agent, target.version, cmdName);
|
|
361
|
+
if (result.success) {
|
|
362
|
+
console.log(` ${chalk.red('-')} ${target.label}: ${cmdName}`);
|
|
320
363
|
removed++;
|
|
321
364
|
}
|
|
365
|
+
else if (result.error) {
|
|
366
|
+
console.log(` ${chalk.yellow('!')} ${target.label}: ${result.error}`);
|
|
367
|
+
}
|
|
322
368
|
}
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
369
|
+
}
|
|
370
|
+
if (removed === 0) {
|
|
371
|
+
console.log(chalk.yellow('No commands removed.'));
|
|
372
|
+
}
|
|
373
|
+
else {
|
|
374
|
+
console.log(chalk.green(`\nRemoved ${removed} command(s) from version homes.`));
|
|
375
|
+
console.log(chalk.gray('Central source unchanged. Commands will re-sync on next agent launch.'));
|
|
326
376
|
}
|
|
327
377
|
});
|
|
328
378
|
// `commands sync` is gone — sync runs automatically when the agent launches.
|
package/dist/commands/daemon.js
CHANGED
|
@@ -91,8 +91,8 @@ you never need to start it manually.
|
|
|
91
91
|
.action(async (options) => {
|
|
92
92
|
warnDeprecated('logs', 'agents routines scheduler-logs');
|
|
93
93
|
if (options.follow) {
|
|
94
|
-
const {
|
|
95
|
-
const logPath = path.join(
|
|
94
|
+
const { getDaemonDir } = await import('../lib/state.js');
|
|
95
|
+
const logPath = path.join(getDaemonDir(), 'logs.jsonl');
|
|
96
96
|
const child = spawn('tail', ['-f', logPath], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
97
97
|
child.stdout.pipe(process.stdout);
|
|
98
98
|
child.stderr.pipe(process.stderr);
|
package/dist/commands/exec.js
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import chalk from 'chalk';
|
|
9
9
|
import { buildExecCommand, parseExecEnv, execAgent, runWithFallback, AGENT_COMMANDS, } from '../lib/exec.js';
|
|
10
10
|
import { profileExists, resolveProfileForRun } from '../lib/profiles.js';
|
|
11
|
-
import { readBundle, resolveBundleEnv } from '../lib/secrets/bundles.js';
|
|
11
|
+
import { readBundle, resolveBundleEnv, describeBundle } from '../lib/secrets/bundles.js';
|
|
12
12
|
import { getConfiguredRunStrategy, normalizeRunStrategy, resolveRunVersion, RUN_STRATEGIES, } from '../lib/rotate.js';
|
|
13
13
|
import { resolveVersionAlias } from '../lib/versions.js';
|
|
14
14
|
const VALID_AGENTS = Object.keys(AGENT_COMMANDS);
|
|
@@ -36,7 +36,7 @@ export function registerRunCommand(program) {
|
|
|
36
36
|
.option('--cwd <dir>', 'Working directory for the agent (defaults to current directory)')
|
|
37
37
|
.option('--add-dir <dir>', 'Grant access to an additional directory outside the project (Claude only, repeatable)', (val, prev) => [...prev, val], [])
|
|
38
38
|
.option('--json', 'Stream events as JSON lines (for parsing by other tools)')
|
|
39
|
-
.option('--headless', 'Non-interactive mode (
|
|
39
|
+
.option('--headless', 'Non-interactive mode (auto-enabled when prompt provided)', false)
|
|
40
40
|
.option('-i, --interactive', 'Force interactive mode even when a prompt is provided')
|
|
41
41
|
.option('--session-id <id>', 'Resume a previous conversation (Claude only)')
|
|
42
42
|
.option('--verbose', 'Show detailed execution logs')
|
|
@@ -197,6 +197,13 @@ Examples:
|
|
|
197
197
|
for (const bundleName of options.secrets) {
|
|
198
198
|
try {
|
|
199
199
|
const bundle = readBundle(bundleName);
|
|
200
|
+
const entries = describeBundle(bundle);
|
|
201
|
+
const counts = {};
|
|
202
|
+
for (const e of entries) {
|
|
203
|
+
counts[e.kind] = (counts[e.kind] || 0) + 1;
|
|
204
|
+
}
|
|
205
|
+
const breakdown = Object.entries(counts).map(([k, v]) => `${v} ${k}`).join(', ');
|
|
206
|
+
console.log(chalk.gray(`[secrets] Resolved ${bundleName}: ${entries.length} keys (${breakdown})`));
|
|
200
207
|
secretsEnv = { ...secretsEnv, ...resolveBundleEnv(bundle) };
|
|
201
208
|
}
|
|
202
209
|
catch (err) {
|
package/dist/commands/fork.js
CHANGED
|
@@ -15,14 +15,14 @@ import { isPromptCancelled } from './utils.js';
|
|
|
15
15
|
export function registerForkCommand(program) {
|
|
16
16
|
program
|
|
17
17
|
.command('fork')
|
|
18
|
-
.description('Copy the default config repo to your own GitHub so you can push changes. Runs once after
|
|
18
|
+
.description('Copy the default config repo to your own GitHub so you can push changes. Runs once after setup.')
|
|
19
19
|
.addHelpText('after', `
|
|
20
20
|
Examples:
|
|
21
21
|
# Fork the default repo to your GitHub account
|
|
22
22
|
agents fork
|
|
23
23
|
|
|
24
24
|
When to use:
|
|
25
|
-
- You
|
|
25
|
+
- You set up with 'agents setup' using the default config
|
|
26
26
|
- You've customized commands, skills, or settings
|
|
27
27
|
- You want to save your changes to your own GitHub repo
|
|
28
28
|
|
package/dist/commands/hooks.js
CHANGED
|
@@ -7,10 +7,10 @@ import { checkbox } from '@inquirer/prompts';
|
|
|
7
7
|
import { AGENTS, HOOKS_CAPABLE_AGENTS, resolveAgentName, formatAgentError, agentLabel, } from '../lib/agents.js';
|
|
8
8
|
import { supports } from '../lib/capabilities.js';
|
|
9
9
|
import { cloneRepo } from '../lib/git.js';
|
|
10
|
-
import { discoverHooksFromRepo, installHooksCentrally, listCentralHooks, listInstalledHooksWithScope,
|
|
10
|
+
import { discoverHooksFromRepo, installHooksCentrally, listCentralHooks, listInstalledHooksWithScope, getHookInfo, parseHookManifest, iterHooksCapableVersions, removeHookFromVersion, } from '../lib/hooks.js';
|
|
11
11
|
import { listInstalledVersions, getGlobalDefault, resolveVersionAlias, syncResourcesToVersion, promptAgentVersionSelection, getVersionHomePath, resolveAgentVersionTargets, } from '../lib/versions.js';
|
|
12
12
|
import { recordVersionResources } from '../lib/state.js';
|
|
13
|
-
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, } from './utils.js';
|
|
13
|
+
import { isPromptCancelled, isInteractiveTerminal, parseCommaSeparatedList, printWithPager, requireInteractiveSelection, promptRemovalTargets, } from './utils.js';
|
|
14
14
|
/** Register the `agents hooks` command tree (list, add, remove, sync, prune, view). */
|
|
15
15
|
export function registerHooksCommands(program) {
|
|
16
16
|
const hooksCmd = program.command('hooks')
|
|
@@ -396,15 +396,29 @@ Examples:
|
|
|
396
396
|
agents hooks remove
|
|
397
397
|
`)
|
|
398
398
|
.action(async (name, options) => {
|
|
399
|
+
const hookTargetMap = new Map();
|
|
400
|
+
for (const { agent, version } of iterHooksCapableVersions()) {
|
|
401
|
+
const home = getVersionHomePath(agent, version);
|
|
402
|
+
const hooks = listInstalledHooksWithScope(agent, process.cwd(), { home });
|
|
403
|
+
for (const hook of hooks) {
|
|
404
|
+
if (hook.scope !== 'user')
|
|
405
|
+
continue;
|
|
406
|
+
const existing = hookTargetMap.get(hook.name);
|
|
407
|
+
if (existing) {
|
|
408
|
+
existing.targets.push({ agent, version });
|
|
409
|
+
}
|
|
410
|
+
else {
|
|
411
|
+
hookTargetMap.set(hook.name, { name: hook.name, targets: [{ agent, version }] });
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
399
415
|
let hooksToRemove;
|
|
400
416
|
if (name) {
|
|
401
417
|
hooksToRemove = [name];
|
|
402
418
|
}
|
|
403
419
|
else {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
if (centralHooks.length === 0) {
|
|
407
|
-
console.log(chalk.yellow('No hooks installed.'));
|
|
420
|
+
if (hookTargetMap.size === 0) {
|
|
421
|
+
console.log(chalk.yellow('No hooks installed in any version.'));
|
|
408
422
|
return;
|
|
409
423
|
}
|
|
410
424
|
if (!isInteractiveTerminal()) {
|
|
@@ -413,12 +427,16 @@ Examples:
|
|
|
413
427
|
]);
|
|
414
428
|
}
|
|
415
429
|
try {
|
|
430
|
+
const choices = Array.from(hookTargetMap.values()).map((hook) => {
|
|
431
|
+
const agents = [...new Set(hook.targets.map((t) => AGENTS[t.agent].name))];
|
|
432
|
+
return {
|
|
433
|
+
value: hook.name,
|
|
434
|
+
name: `${hook.name} (${agents.join(', ')})`,
|
|
435
|
+
};
|
|
436
|
+
});
|
|
416
437
|
const selected = await checkbox({
|
|
417
438
|
message: 'Select hooks to remove',
|
|
418
|
-
choices
|
|
419
|
-
value: hook.name,
|
|
420
|
-
name: hook.name,
|
|
421
|
-
})),
|
|
439
|
+
choices,
|
|
422
440
|
});
|
|
423
441
|
if (selected.length === 0) {
|
|
424
442
|
console.log(chalk.gray('No hooks selected.'));
|
|
@@ -434,26 +452,53 @@ Examples:
|
|
|
434
452
|
throw err;
|
|
435
453
|
}
|
|
436
454
|
}
|
|
437
|
-
|
|
438
|
-
? options.agents.split(',')
|
|
439
|
-
: Array.from(HOOKS_CAPABLE_AGENTS);
|
|
455
|
+
let removed = 0;
|
|
440
456
|
for (const hookName of hooksToRemove) {
|
|
441
|
-
const
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
if (
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
457
|
+
const hookInfo = hookTargetMap.get(hookName);
|
|
458
|
+
if (!hookInfo || hookInfo.targets.length === 0) {
|
|
459
|
+
console.log(chalk.yellow(` Hook '${hookName}' not found in any version.`));
|
|
460
|
+
continue;
|
|
461
|
+
}
|
|
462
|
+
// Filter by --agents if specified
|
|
463
|
+
let availableTargets = hookInfo.targets;
|
|
464
|
+
if (options?.agents) {
|
|
465
|
+
const requestedAgents = new Set(options.agents.split(','));
|
|
466
|
+
availableTargets = availableTargets.filter((t) => requestedAgents.has(t.agent));
|
|
467
|
+
}
|
|
468
|
+
if (availableTargets.length === 0) {
|
|
469
|
+
console.log(chalk.yellow(` Hook '${hookName}' not found in specified agents.`));
|
|
470
|
+
continue;
|
|
471
|
+
}
|
|
472
|
+
const removalTargets = availableTargets.map((t) => ({
|
|
473
|
+
agent: t.agent,
|
|
474
|
+
version: t.version,
|
|
475
|
+
label: `${agentLabel(t.agent)}@${t.version}`,
|
|
476
|
+
}));
|
|
477
|
+
const selectedTargets = await promptRemovalTargets(hookName, removalTargets, {
|
|
478
|
+
skipPrompt: !!options?.agents,
|
|
479
|
+
});
|
|
480
|
+
if (selectedTargets.length === 0) {
|
|
481
|
+
console.log(chalk.gray(` Skipped '${hookName}'.`));
|
|
482
|
+
continue;
|
|
452
483
|
}
|
|
453
|
-
|
|
454
|
-
|
|
484
|
+
for (const target of selectedTargets) {
|
|
485
|
+
const result = removeHookFromVersion(target.agent, target.version, hookName);
|
|
486
|
+
if (result.success) {
|
|
487
|
+
console.log(` ${chalk.red('-')} ${target.label}: ${hookName}`);
|
|
488
|
+
removed++;
|
|
489
|
+
}
|
|
490
|
+
else if (result.error) {
|
|
491
|
+
console.log(` ${chalk.yellow('!')} ${target.label}: ${result.error}`);
|
|
492
|
+
}
|
|
455
493
|
}
|
|
456
494
|
}
|
|
495
|
+
if (removed === 0) {
|
|
496
|
+
console.log(chalk.yellow('No hooks removed.'));
|
|
497
|
+
}
|
|
498
|
+
else {
|
|
499
|
+
console.log(chalk.green(`\nRemoved ${removed} hook(s) from version homes.`));
|
|
500
|
+
console.log(chalk.gray('Central source unchanged. Hooks will re-sync on next agent launch.'));
|
|
501
|
+
}
|
|
457
502
|
});
|
|
458
503
|
// `hooks sync` is gone — sync runs automatically when the agent launches.
|
|
459
504
|
hooksCmd
|
package/dist/commands/mcp.js
CHANGED
|
@@ -6,8 +6,8 @@ import { readManifest, writeManifest, createDefaultManifest } from '../lib/manif
|
|
|
6
6
|
import { listMcpServerConfigs } from '../lib/mcp.js';
|
|
7
7
|
import { getMcpDir } from '../lib/state.js';
|
|
8
8
|
import { getEffectiveHome, getGlobalDefault, listInstalledVersions, getVersionHomePath, resolveInstalledAgentTargets, resolveConfiguredAgentTargets, resolveVersionAlias, } from '../lib/versions.js';
|
|
9
|
-
import {
|
|
10
|
-
import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection } from './utils.js';
|
|
9
|
+
import { getUserAgentsDir } from '../lib/state.js';
|
|
10
|
+
import { isPromptCancelled, isInteractiveTerminal, requireInteractiveSelection, promptRemovalTargets } from './utils.js';
|
|
11
11
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
12
12
|
/** Parse a comma-separated --agents string into validated agent IDs and optional version targets. */
|
|
13
13
|
function parseMcpAgentTargets(value) {
|
|
@@ -163,7 +163,7 @@ Examples:
|
|
|
163
163
|
console.log(chalk.gray('HTTP: agents mcp add <name> <url> --transport http'));
|
|
164
164
|
process.exit(1);
|
|
165
165
|
}
|
|
166
|
-
const localPath =
|
|
166
|
+
const localPath = getUserAgentsDir();
|
|
167
167
|
const manifest = readManifest(localPath) || createDefaultManifest();
|
|
168
168
|
manifest.mcp = manifest.mcp || {};
|
|
169
169
|
const targetConfig = parseMcpAgentTargets(options.agents);
|
|
@@ -219,20 +219,33 @@ Examples:
|
|
|
219
219
|
.action(async (name, options) => {
|
|
220
220
|
const cwd = process.cwd();
|
|
221
221
|
const cliStates = await getAllCliStates();
|
|
222
|
+
const mcpTargetMap = new Map();
|
|
223
|
+
for (const agentId of MCP_CAPABLE_AGENTS) {
|
|
224
|
+
if (!cliStates[agentId]?.installed && listInstalledVersions(agentId).length === 0)
|
|
225
|
+
continue;
|
|
226
|
+
for (const version of listInstalledVersions(agentId)) {
|
|
227
|
+
const home = getVersionHomePath(agentId, version);
|
|
228
|
+
const configPath = getMcpConfigPathForHome(agentId, home);
|
|
229
|
+
const mcps = parseMcpConfig(agentId, configPath);
|
|
230
|
+
for (const mcpName of Object.keys(mcps)) {
|
|
231
|
+
const existing = mcpTargetMap.get(mcpName);
|
|
232
|
+
if (existing) {
|
|
233
|
+
existing.targets.push({ agentId, version, home });
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
mcpTargetMap.set(mcpName, { name: mcpName, targets: [{ agentId, version, home }] });
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
222
241
|
let mcpsToRemove;
|
|
223
|
-
let targets;
|
|
224
242
|
if (name) {
|
|
225
243
|
mcpsToRemove = [name];
|
|
226
|
-
const installedAgents = MCP_CAPABLE_AGENTS.filter((agentId) => cliStates[agentId]?.installed || listInstalledVersions(agentId).length > 0);
|
|
227
|
-
targets = options?.agents
|
|
228
|
-
? resolveInstalledAgentTargets(options.agents, MCP_CAPABLE_AGENTS)
|
|
229
|
-
: resolveConfiguredAgentTargets(installedAgents, undefined, MCP_CAPABLE_AGENTS);
|
|
230
244
|
}
|
|
231
245
|
else {
|
|
232
|
-
// Interactive picker
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
console.log(chalk.yellow('No MCP-capable agents installed.'));
|
|
246
|
+
// Interactive picker for MCP selection
|
|
247
|
+
if (mcpTargetMap.size === 0) {
|
|
248
|
+
console.log(chalk.yellow('No MCP servers configured.'));
|
|
236
249
|
return;
|
|
237
250
|
}
|
|
238
251
|
if (!isInteractiveTerminal()) {
|
|
@@ -241,42 +254,22 @@ Examples:
|
|
|
241
254
|
'agents mcp remove my-server --agents codex,claude',
|
|
242
255
|
]);
|
|
243
256
|
}
|
|
244
|
-
// Gather all unique MCPs across agents (with agent info for display)
|
|
245
|
-
const mcpMap = new Map();
|
|
246
|
-
for (const agentId of installedAgents) {
|
|
247
|
-
const mcps = listInstalledMcpsWithScope(agentId, cwd, { home: getEffectiveHome(agentId) });
|
|
248
|
-
for (const mcp of mcps) {
|
|
249
|
-
const existing = mcpMap.get(mcp.name);
|
|
250
|
-
if (existing) {
|
|
251
|
-
existing.agents.push(AGENTS[agentId].name);
|
|
252
|
-
}
|
|
253
|
-
else {
|
|
254
|
-
mcpMap.set(mcp.name, {
|
|
255
|
-
name: mcp.name,
|
|
256
|
-
agents: [AGENTS[agentId].name],
|
|
257
|
-
command: mcp.command,
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
if (mcpMap.size === 0) {
|
|
263
|
-
console.log(chalk.yellow('No MCP servers configured.'));
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
257
|
try {
|
|
267
258
|
const selected = await checkbox({
|
|
268
259
|
message: 'Select MCP servers to remove',
|
|
269
|
-
choices: Array.from(
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
260
|
+
choices: Array.from(mcpTargetMap.values()).map((mcp) => {
|
|
261
|
+
const agents = [...new Set(mcp.targets.map((t) => AGENTS[t.agentId].name))];
|
|
262
|
+
return {
|
|
263
|
+
value: mcp.name,
|
|
264
|
+
name: `${mcp.name} (${agents.join(', ')})`,
|
|
265
|
+
};
|
|
266
|
+
}),
|
|
273
267
|
});
|
|
274
268
|
if (selected.length === 0) {
|
|
275
269
|
console.log(chalk.gray('No MCPs selected.'));
|
|
276
270
|
return;
|
|
277
271
|
}
|
|
278
272
|
mcpsToRemove = selected;
|
|
279
|
-
targets = resolveConfiguredAgentTargets(installedAgents, undefined, MCP_CAPABLE_AGENTS);
|
|
280
273
|
}
|
|
281
274
|
catch (err) {
|
|
282
275
|
if (isPromptCancelled(err)) {
|
|
@@ -286,10 +279,59 @@ Examples:
|
|
|
286
279
|
throw err;
|
|
287
280
|
}
|
|
288
281
|
}
|
|
289
|
-
// Execute removals
|
|
282
|
+
// Execute removals with target selection
|
|
290
283
|
let removed = 0;
|
|
291
284
|
for (const mcpName of mcpsToRemove) {
|
|
292
|
-
const
|
|
285
|
+
const mcpInfo = mcpTargetMap.get(mcpName);
|
|
286
|
+
if (!mcpInfo || mcpInfo.targets.length === 0) {
|
|
287
|
+
console.log(chalk.yellow(` MCP '${mcpName}' not found in any agent.`));
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
// If --agents was specified, filter targets
|
|
291
|
+
let availableTargets = mcpInfo.targets;
|
|
292
|
+
if (options?.agents) {
|
|
293
|
+
const requestedTargets = resolveInstalledAgentTargets(options.agents, MCP_CAPABLE_AGENTS);
|
|
294
|
+
const requested = new Set();
|
|
295
|
+
for (const aid of requestedTargets.directAgents) {
|
|
296
|
+
for (const ver of listInstalledVersions(aid)) {
|
|
297
|
+
requested.add(`${aid}@${ver}`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
for (const [aid, versions] of requestedTargets.versionSelections) {
|
|
301
|
+
for (const ver of versions) {
|
|
302
|
+
requested.add(`${aid}@${ver}`);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
availableTargets = availableTargets.filter((t) => requested.has(`${t.agentId}@${t.version}`));
|
|
306
|
+
}
|
|
307
|
+
if (availableTargets.length === 0) {
|
|
308
|
+
console.log(chalk.yellow(` MCP '${mcpName}' not found in specified agents.`));
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
// Show target picker if multiple targets and no --agents flag
|
|
312
|
+
const removalTargets = availableTargets.map((t) => ({
|
|
313
|
+
agent: t.agentId,
|
|
314
|
+
version: t.version,
|
|
315
|
+
label: formatTargetLabel(t.agentId, t.version),
|
|
316
|
+
}));
|
|
317
|
+
const selectedTargets = await promptRemovalTargets(mcpName, removalTargets, {
|
|
318
|
+
skipPrompt: !!options?.agents,
|
|
319
|
+
});
|
|
320
|
+
if (selectedTargets.length === 0) {
|
|
321
|
+
console.log(chalk.gray(` Skipped '${mcpName}'.`));
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
// Build targets structure for unregister
|
|
325
|
+
const versionSelections = new Map();
|
|
326
|
+
for (const t of selectedTargets) {
|
|
327
|
+
const versions = versionSelections.get(t.agent) || [];
|
|
328
|
+
if (!versions.includes(t.version)) {
|
|
329
|
+
versions.push(t.version);
|
|
330
|
+
versionSelections.set(t.agent, versions);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
const targetsToRemove = { directAgents: [], versionSelections };
|
|
334
|
+
const results = await unregisterMcpFromTargets(targetsToRemove, mcpName);
|
|
293
335
|
for (const result of results) {
|
|
294
336
|
if (result.success) {
|
|
295
337
|
console.log(` ${chalk.red('-')} ${formatTargetLabel(result.agentId, result.version)}: ${mcpName}`);
|
|
@@ -400,7 +442,7 @@ Examples:
|
|
|
400
442
|
agents mcp register --agents codex@0.116.0
|
|
401
443
|
`)
|
|
402
444
|
.action(async (name, options) => {
|
|
403
|
-
const localPath =
|
|
445
|
+
const localPath = getUserAgentsDir();
|
|
404
446
|
const manifest = readManifest(localPath);
|
|
405
447
|
if (!manifest?.mcp) {
|
|
406
448
|
console.log(chalk.yellow('No MCP servers in manifest'));
|
|
@@ -467,7 +509,7 @@ function buildMcpRows(opts) {
|
|
|
467
509
|
const centralServers = new Map();
|
|
468
510
|
for (const s of listMcpServerConfigs())
|
|
469
511
|
centralServers.set(s.name, s);
|
|
470
|
-
const manifest = readManifest(
|
|
512
|
+
const manifest = readManifest(getUserAgentsDir());
|
|
471
513
|
const manifestEntries = manifest?.mcp || {};
|
|
472
514
|
const targetPairs = iterMcpCapableVersions({
|
|
473
515
|
agent: opts.filterAgent,
|
package/dist/commands/plugins.js
CHANGED
|
@@ -12,7 +12,7 @@ import { PLUGINS_CAPABLE_AGENTS, agentLabel } from '../lib/agents.js';
|
|
|
12
12
|
import { discoverPlugins, getPlugin, pluginSupportsAgent, removePluginFromVersion } from '../lib/plugins.js';
|
|
13
13
|
import { listInstalledVersions, syncResourcesToVersion, getGlobalDefault, getVersionHomePath, } from '../lib/versions.js';
|
|
14
14
|
import { isPluginSynced } from '../lib/plugins.js';
|
|
15
|
-
import { isPromptCancelled, isInteractiveTerminal, requireDestructiveArg, requireInteractiveSelection, } from './utils.js';
|
|
15
|
+
import { isPromptCancelled, isInteractiveTerminal, requireDestructiveArg, requireInteractiveSelection, promptRemovalTargets, } from './utils.js';
|
|
16
16
|
import { itemPicker } from '../lib/picker.js';
|
|
17
17
|
import { showResourceList, buildTargetsSection, } from './resource-view.js';
|
|
18
18
|
import { getPluginsDir } from '../lib/state.js';
|
|
@@ -255,7 +255,7 @@ Examples:
|
|
|
255
255
|
# Unsync but keep source directory
|
|
256
256
|
agents plugins remove rush-toolkit --keep-source
|
|
257
257
|
`)
|
|
258
|
-
.action((nameArg, options) => {
|
|
258
|
+
.action(async (nameArg, options) => {
|
|
259
259
|
if (!nameArg) {
|
|
260
260
|
requireDestructiveArg({
|
|
261
261
|
argName: 'name',
|
|
@@ -275,31 +275,64 @@ Examples:
|
|
|
275
275
|
console.log(chalk.red(`Plugin '${name}' not found`));
|
|
276
276
|
process.exit(1);
|
|
277
277
|
}
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
let totalPerms = 0;
|
|
281
|
-
let versionsTouched = 0;
|
|
278
|
+
// Build list of targets that have this plugin synced
|
|
279
|
+
const availableTargets = [];
|
|
282
280
|
for (const agentId of PLUGINS_CAPABLE_AGENTS) {
|
|
281
|
+
if (plugin && !pluginSupportsAgent(plugin, agentId))
|
|
282
|
+
continue;
|
|
283
283
|
const versions = listInstalledVersions(agentId);
|
|
284
284
|
for (const version of versions) {
|
|
285
285
|
const versionHome = getVersionHomePath(agentId, version);
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
versionsTouched += 1;
|
|
289
|
-
totalSkills += r.skills.length;
|
|
290
|
-
totalHooks += r.hooks.length;
|
|
291
|
-
totalPerms += r.permissions;
|
|
292
|
-
console.log(chalk.gray(` ${agentLabel(agentId)}@${version}: ${r.skills.length} skill(s), ${r.hooks.length} hook(s), ${r.permissions} perm(s)`));
|
|
286
|
+
if (plugin && isPluginSynced(plugin, agentId, versionHome)) {
|
|
287
|
+
availableTargets.push({ agent: agentId, version });
|
|
293
288
|
}
|
|
294
289
|
}
|
|
295
290
|
}
|
|
296
|
-
|
|
297
|
-
|
|
291
|
+
if (availableTargets.length === 0) {
|
|
292
|
+
console.log(chalk.yellow(`Plugin '${name}' not synced to any version.`));
|
|
293
|
+
if (!options.keepSource && fs.existsSync(pluginRoot)) {
|
|
294
|
+
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
295
|
+
console.log(chalk.green(`Deleted ${formatPath(pluginRoot)}`));
|
|
296
|
+
}
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
// Show multi-select picker for targets
|
|
300
|
+
const removalTargets = availableTargets.map((t) => ({
|
|
301
|
+
agent: t.agent,
|
|
302
|
+
version: t.version,
|
|
303
|
+
label: `${agentLabel(t.agent)}@${t.version}`,
|
|
304
|
+
}));
|
|
305
|
+
const selectedTargets = await promptRemovalTargets(name, removalTargets);
|
|
306
|
+
if (selectedTargets.length === 0) {
|
|
307
|
+
console.log(chalk.gray('Cancelled.'));
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
let totalSkills = 0;
|
|
311
|
+
let totalHooks = 0;
|
|
312
|
+
let totalPerms = 0;
|
|
313
|
+
let versionsTouched = 0;
|
|
314
|
+
for (const target of selectedTargets) {
|
|
315
|
+
const versionHome = getVersionHomePath(target.agent, target.version);
|
|
316
|
+
const r = removePluginFromVersion(name, resolvedRoot, target.agent, versionHome);
|
|
317
|
+
if (r.skills.length > 0 || r.hooks.length > 0 || r.permissions > 0) {
|
|
318
|
+
versionsTouched += 1;
|
|
319
|
+
totalSkills += r.skills.length;
|
|
320
|
+
totalHooks += r.hooks.length;
|
|
321
|
+
totalPerms += r.permissions;
|
|
322
|
+
console.log(` ${chalk.red('-')} ${target.label}: ${r.skills.length} skill(s), ${r.hooks.length} hook(s), ${r.permissions} perm(s)`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
console.log(chalk.green(`\nUnsynced ${name} from ${versionsTouched} version(s) — ${totalSkills} skills, ${totalHooks} hooks, ${totalPerms} permissions`));
|
|
326
|
+
// Only delete source if ALL targets were selected
|
|
327
|
+
if (!options.keepSource && selectedTargets.length === availableTargets.length) {
|
|
298
328
|
if (fs.existsSync(pluginRoot)) {
|
|
299
329
|
fs.rmSync(pluginRoot, { recursive: true, force: true });
|
|
300
330
|
console.log(chalk.green(`Deleted ${formatPath(pluginRoot)}`));
|
|
301
331
|
}
|
|
302
332
|
}
|
|
333
|
+
else if (!options.keepSource && selectedTargets.length < availableTargets.length) {
|
|
334
|
+
console.log(chalk.gray(`Source kept — plugin still synced to other versions.`));
|
|
335
|
+
}
|
|
303
336
|
else {
|
|
304
337
|
console.log(chalk.gray(`Kept source at ${formatPath(pluginRoot)}`));
|
|
305
338
|
}
|
package/dist/commands/prune.d.ts
CHANGED
|
@@ -1,22 +1,2 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Top-level `agents prune` — destructive cleanup across the install.
|
|
3
|
-
*
|
|
4
|
-
* Two kinds of cleanup, one verb:
|
|
5
|
-
* - Resource orphans: command/skill/hook files inside a version home that no
|
|
6
|
-
* longer come from any source (deleted from ~/.agents/ but never reconciled
|
|
7
|
-
* into the version install).
|
|
8
|
-
* - Version duplicates: older installed versions of an agent that share an
|
|
9
|
-
* account with a newer installed version of the same agent (the older copy
|
|
10
|
-
* is redundant; the newer one is what's signed in and active).
|
|
11
|
-
*
|
|
12
|
-
* Sync (additive: copy missing/changed files into version homes) is no longer
|
|
13
|
-
* a user-facing verb — `syncResourcesToVersion` runs at agent launch and
|
|
14
|
-
* applies adds/updates automatically. Pruning, however, is destructive, so it
|
|
15
|
-
* stays explicit.
|
|
16
|
-
*
|
|
17
|
-
* Default scope: each agent's currently-pinned default version for orphan
|
|
18
|
-
* cleanup, plus the standard cross-agent version-dedup pass. Pass `--all`
|
|
19
|
-
* to widen orphan cleanup to every installed version.
|
|
20
|
-
*/
|
|
21
1
|
import type { Command } from 'commander';
|
|
22
2
|
export declare function registerPruneCommand(program: Command): void;
|