@phnx-labs/agents-cli 1.20.12 → 1.20.14
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 +30 -0
- package/README.md +3 -0
- package/dist/commands/computer-actions.d.ts +3 -0
- package/dist/commands/computer-actions.js +16 -0
- package/dist/commands/doctor.js +51 -7
- package/dist/commands/exec.js +25 -4
- package/dist/commands/import.js +17 -6
- package/dist/commands/inspect.d.ts +28 -1
- package/dist/commands/inspect.js +330 -47
- package/dist/commands/mcp.js +3 -3
- package/dist/commands/plugins.d.ts +2 -0
- package/dist/commands/plugins.js +69 -26
- package/dist/commands/prune.js +8 -5
- package/dist/commands/sync.js +1 -1
- package/dist/commands/teams.js +1 -0
- package/dist/commands/trash.d.ts +11 -0
- package/dist/commands/trash.js +57 -41
- package/dist/commands/versions.js +68 -20
- package/dist/commands/view.d.ts +1 -0
- package/dist/commands/view.js +56 -12
- package/dist/commands/wallet.d.ts +14 -0
- package/dist/commands/wallet.js +199 -0
- package/dist/index.js +4 -1
- package/dist/lib/agents.js +70 -22
- package/dist/lib/browser/ipc.d.ts +7 -0
- package/dist/lib/browser/ipc.js +43 -27
- package/dist/lib/capabilities.js +7 -1
- package/dist/lib/command-skills.d.ts +1 -0
- package/dist/lib/command-skills.js +23 -7
- package/dist/lib/exec.d.ts +32 -1
- package/dist/lib/exec.js +79 -7
- package/dist/lib/hooks.d.ts +21 -1
- package/dist/lib/hooks.js +69 -7
- package/dist/lib/mcp.js +33 -0
- package/dist/lib/models.js +5 -0
- package/dist/lib/picker.d.ts +2 -0
- package/dist/lib/picker.js +96 -6
- package/dist/lib/platform/index.d.ts +1 -0
- package/dist/lib/platform/index.js +1 -0
- package/dist/lib/platform/winpath.d.ts +35 -0
- package/dist/lib/platform/winpath.js +86 -0
- package/dist/lib/plugins.d.ts +24 -0
- package/dist/lib/plugins.js +37 -2
- package/dist/lib/project-launch.js +110 -5
- package/dist/lib/registry.js +15 -2
- package/dist/lib/rotate.d.ts +7 -0
- package/dist/lib/rotate.js +17 -7
- package/dist/lib/runner.js +14 -0
- package/dist/lib/sandbox.js +5 -2
- package/dist/lib/settings-manifest.d.ts +39 -0
- package/dist/lib/settings-manifest.js +163 -0
- package/dist/lib/shims.d.ts +1 -1
- package/dist/lib/shims.js +16 -31
- package/dist/lib/staleness/detectors/subagents.js +16 -0
- package/dist/lib/staleness/writers/subagents.js +11 -3
- package/dist/lib/subagents.d.ts +9 -0
- package/dist/lib/subagents.js +33 -0
- package/dist/lib/teams/agents.js +1 -1
- package/dist/lib/teams/parsers.d.ts +1 -1
- package/dist/lib/teams/parsers.js +6 -0
- package/dist/lib/types.d.ts +1 -1
- package/dist/lib/versions.d.ts +15 -3
- package/dist/lib/versions.js +88 -19
- package/dist/lib/wallet/index.d.ts +78 -0
- package/dist/lib/wallet/index.js +253 -0
- package/package.json +3 -3
- package/scripts/postinstall.js +35 -7
package/dist/commands/inspect.js
CHANGED
|
@@ -23,7 +23,10 @@ import { readMeta, getUserAgentsDir, getSystemAgentsDir, getProjectAgentsDir, ge
|
|
|
23
23
|
import { getVersionHomePath } from '../lib/versions.js';
|
|
24
24
|
import { getShimsDir, getVersionedAliasPath } from '../lib/shims.js';
|
|
25
25
|
import { getAgentResources, listResources, } from '../lib/resources.js';
|
|
26
|
-
import {
|
|
26
|
+
import { listHookEntriesFromDir } from '../lib/hooks.js';
|
|
27
|
+
import { listMcpServerConfigs, discoverMcpConfigsFromRepo } from '../lib/mcp.js';
|
|
28
|
+
import { discoverPlugins, discoverPluginsInDir, pluginResourceGroups } from '../lib/plugins.js';
|
|
29
|
+
import { PLUGIN_GROUP_COLORS } from './plugins.js';
|
|
27
30
|
import { countSessionsInScope } from '../lib/session/discover.js';
|
|
28
31
|
import { damerauLevenshtein } from '../lib/fuzzy.js';
|
|
29
32
|
/** Resource kinds the inspect command can drill into. */
|
|
@@ -37,6 +40,29 @@ const DRILLABLE_KINDS = [
|
|
|
37
40
|
'workflows',
|
|
38
41
|
'subagents',
|
|
39
42
|
];
|
|
43
|
+
/**
|
|
44
|
+
* Summary-view partition. SIMPLE kinds render as a one-line count + name preview;
|
|
45
|
+
* RICH kinds (hooks/plugins/mcp) get their own expanded section showing each
|
|
46
|
+
* item's key detail (events/predicates, bundle contents, transport/url). Together
|
|
47
|
+
* they cover every DrillableKind.
|
|
48
|
+
*/
|
|
49
|
+
const SIMPLE_KINDS = ['commands', 'skills', 'rules', 'subagents', 'workflows'];
|
|
50
|
+
const RICH_KINDS = ['hooks', 'plugins', 'mcp'];
|
|
51
|
+
/**
|
|
52
|
+
* Singular aliases for the plural drill-down flags. `--plugin code` reads as
|
|
53
|
+
* "show the one plugin named code" — a required-value flag that always lands in
|
|
54
|
+
* detail mode, the natural counterpart to `--plugins` (list). `mcp` has no
|
|
55
|
+
* distinct singular, so it is intentionally absent.
|
|
56
|
+
*/
|
|
57
|
+
const SINGULAR_DRILL_ALIASES = {
|
|
58
|
+
command: 'commands',
|
|
59
|
+
skill: 'skills',
|
|
60
|
+
hook: 'hooks',
|
|
61
|
+
rule: 'rules',
|
|
62
|
+
plugin: 'plugins',
|
|
63
|
+
workflow: 'workflows',
|
|
64
|
+
subagent: 'subagents',
|
|
65
|
+
};
|
|
40
66
|
const CAPABILITY_NAMES = [
|
|
41
67
|
'hooks', 'mcp', 'skills', 'commands', 'subagents', 'plugins', 'workflows', 'rules', 'allowlist',
|
|
42
68
|
];
|
|
@@ -50,6 +76,9 @@ export function registerInspectCommand(program) {
|
|
|
50
76
|
for (const kind of DRILLABLE_KINDS) {
|
|
51
77
|
cmd.option(`--${kind} [query]`, `list ${kind}; pass a name (fuzzy) to show detail`);
|
|
52
78
|
}
|
|
79
|
+
for (const singular of Object.keys(SINGULAR_DRILL_ALIASES)) {
|
|
80
|
+
cmd.option(`--${singular} <query>`, `show detail for one ${singular} by name (fuzzy)`);
|
|
81
|
+
}
|
|
53
82
|
cmd.action(async (target, options) => {
|
|
54
83
|
await inspectAction(target, options);
|
|
55
84
|
});
|
|
@@ -121,15 +150,21 @@ function pickDrillKind(options) {
|
|
|
121
150
|
for (const kind of DRILLABLE_KINDS) {
|
|
122
151
|
const value = options[kind];
|
|
123
152
|
if (value !== undefined)
|
|
124
|
-
active.push({ kind, query: value });
|
|
153
|
+
active.push({ flag: `--${kind}`, kind, query: value });
|
|
154
|
+
}
|
|
155
|
+
// Singular aliases (`--plugin code`) always carry a name → detail mode.
|
|
156
|
+
for (const [singular, plural] of Object.entries(SINGULAR_DRILL_ALIASES)) {
|
|
157
|
+
const value = options[singular];
|
|
158
|
+
if (typeof value === 'string')
|
|
159
|
+
active.push({ flag: `--${singular}`, kind: plural, query: value });
|
|
125
160
|
}
|
|
126
161
|
if (active.length === 0)
|
|
127
162
|
return null;
|
|
128
163
|
if (active.length > 1) {
|
|
129
|
-
console.error(chalk.red(`Pick at most one drill-down flag. Got: ${active.map(a =>
|
|
164
|
+
console.error(chalk.red(`Pick at most one drill-down flag. Got: ${active.map(a => a.flag).join(', ')}`));
|
|
130
165
|
process.exit(1);
|
|
131
166
|
}
|
|
132
|
-
return active[0];
|
|
167
|
+
return { kind: active[0].kind, query: active[0].query };
|
|
133
168
|
}
|
|
134
169
|
/** Files at a DotAgents root that mark it as one, beyond the per-kind dirs. */
|
|
135
170
|
const REPO_MARKER_FILES = ['agents.yaml', 'hooks.yaml'];
|
|
@@ -330,6 +365,9 @@ function renderRepoSummary(repo, options) {
|
|
|
330
365
|
const manifest = repoManifestSummary(repo.root);
|
|
331
366
|
const kindData = {};
|
|
332
367
|
let totalBytes = 0, totalFiles = 0;
|
|
368
|
+
let repoHookByScript = new Map();
|
|
369
|
+
let repoHookItemList = [];
|
|
370
|
+
let repoMcpConfigs = new Map();
|
|
333
371
|
if (!options.brief) {
|
|
334
372
|
for (const kind of DRILLABLE_KINDS) {
|
|
335
373
|
const items = collectRepoKind(repo, kind);
|
|
@@ -338,6 +376,9 @@ function renderRepoSummary(repo, options) {
|
|
|
338
376
|
totalBytes += size.bytes;
|
|
339
377
|
totalFiles += size.files;
|
|
340
378
|
}
|
|
379
|
+
repoHookByScript = hookManifestByScript(hookManifestFromFile(path.join(repo.root, 'agents.yaml')));
|
|
380
|
+
repoHookItemList = repoHookItems(repo);
|
|
381
|
+
repoMcpConfigs = new Map(discoverMcpConfigsFromRepo(repo.root).map(s => [s.name, s.config]));
|
|
341
382
|
}
|
|
342
383
|
if (options.json) {
|
|
343
384
|
console.log(JSON.stringify({
|
|
@@ -347,12 +388,34 @@ function renderRepoSummary(repo, options) {
|
|
|
347
388
|
manifests,
|
|
348
389
|
manifest,
|
|
349
390
|
size: options.brief ? null : { bytes: totalBytes, files: totalFiles },
|
|
350
|
-
resources: options.brief ? null : Object.fromEntries(DRILLABLE_KINDS.map(kind =>
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
391
|
+
resources: options.brief ? null : Object.fromEntries(DRILLABLE_KINDS.map(kind => {
|
|
392
|
+
const size = kindData[kind].size;
|
|
393
|
+
// Hooks use the grouped reader (clean names) instead of the raw readdir.
|
|
394
|
+
const items = kind === 'hooks' ? repoHookItemList : kindData[kind].items;
|
|
395
|
+
const base = {
|
|
396
|
+
count: items.length,
|
|
397
|
+
bytes: size.bytes,
|
|
398
|
+
files: size.files,
|
|
399
|
+
names: items.map(i => i.name),
|
|
400
|
+
};
|
|
401
|
+
if (kind === 'hooks')
|
|
402
|
+
return [kind, { ...base, items: items.map(i => {
|
|
403
|
+
const h = repoHookByScript.get(i.name);
|
|
404
|
+
return { name: i.name, events: h?.events ?? [], matcher: h?.matcher, matches: h?.matches, cache: h?.cache };
|
|
405
|
+
}) }];
|
|
406
|
+
if (kind === 'mcp')
|
|
407
|
+
return [kind, { ...base, items: items.map(i => {
|
|
408
|
+
const c = repoMcpConfigs.get(i.name);
|
|
409
|
+
return { name: i.name, transport: c?.transport, url: c?.url, command: c?.command, args: c?.args };
|
|
410
|
+
}) }];
|
|
411
|
+
if (kind === 'plugins')
|
|
412
|
+
return [kind, { ...base, items: items.map(i => ({
|
|
413
|
+
name: i.name,
|
|
414
|
+
version: i.extra?.find(([k]) => k === 'version')?.[1],
|
|
415
|
+
groups: Object.fromEntries((i.groups ?? []).map(g => [g.label, g.items.length])),
|
|
416
|
+
})) }];
|
|
417
|
+
return [kind, base];
|
|
418
|
+
})),
|
|
356
419
|
}, null, 2));
|
|
357
420
|
return;
|
|
358
421
|
}
|
|
@@ -391,13 +454,16 @@ function renderRepoSummary(repo, options) {
|
|
|
391
454
|
if (!options.brief) {
|
|
392
455
|
console.log(` ${'size'.padEnd(10)} ${formatBytes(totalBytes)} ${chalk.gray('·')} ${totalFiles} files`);
|
|
393
456
|
console.log('\n' + chalk.bold('Resources'));
|
|
394
|
-
for (const kind of
|
|
457
|
+
for (const kind of SIMPLE_KINDS) {
|
|
395
458
|
const { items, size } = kindData[kind];
|
|
396
459
|
const count = String(items.length).padStart(4);
|
|
397
460
|
const sz = items.length > 0 ? formatBytes(size.bytes).padStart(8) : ''.padEnd(8);
|
|
398
461
|
const preview = items.length > 0 ? chalk.gray(truncate(previewNames(items, 4), 60)) : '';
|
|
399
462
|
console.log(` ${kind.padEnd(10)} ${count} ${sz} ${preview}`.trimEnd());
|
|
400
463
|
}
|
|
464
|
+
printExpandedSection('Hooks', hookRows(repoHookItemList, repoHookByScript));
|
|
465
|
+
printExpandedSection('Plugins', pluginRows(kindData.plugins.items));
|
|
466
|
+
printExpandedSection('MCP', mcpRows(kindData.mcp.items, repoMcpConfigs));
|
|
401
467
|
}
|
|
402
468
|
console.log('');
|
|
403
469
|
console.log(chalk.gray(`Drill in: agents inspect ${repo.label} --skills <query>`));
|
|
@@ -455,7 +521,9 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
455
521
|
const shimPath = path.join(getShimsDir(), AGENTS[agent].cliCommand);
|
|
456
522
|
const aliasPath = getVersionedAliasPath(agent, version);
|
|
457
523
|
const capabilities = collectCapabilities(agent, version);
|
|
458
|
-
const
|
|
524
|
+
const itemsByKind = options.brief ? null : collectItemsByKind(agent, versionHome);
|
|
525
|
+
const hookByScript = options.brief ? null : hookManifestByScript(loadCentralHookManifest());
|
|
526
|
+
const mcpConfigs = options.brief ? null : new Map(listMcpServerConfigs().map(s => [s.name, s.config]));
|
|
459
527
|
const sessions = options.brief ? null : {
|
|
460
528
|
total: safeCountSessions(agent),
|
|
461
529
|
};
|
|
@@ -471,7 +539,7 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
471
539
|
strategy,
|
|
472
540
|
installedShim: cliState?.installed === true ? cliState.path : null,
|
|
473
541
|
capabilities,
|
|
474
|
-
resources:
|
|
542
|
+
resources: itemsByKind ? summaryResourcesJson(itemsByKind, hookByScript, mcpConfigs) : null,
|
|
475
543
|
sessions,
|
|
476
544
|
};
|
|
477
545
|
console.log(JSON.stringify(json, null, 2));
|
|
@@ -496,15 +564,14 @@ async function renderSummary(agent, version, versionHome, options) {
|
|
|
496
564
|
const reason = res.ok ? '' : chalk.gray(`(${res.reason}${res.need ? ' ' + res.need : ''})`);
|
|
497
565
|
console.log(` ${cap.padEnd(10)} ${mark} ${reason}`);
|
|
498
566
|
}
|
|
499
|
-
if (
|
|
567
|
+
if (itemsByKind) {
|
|
500
568
|
console.log('\n' + chalk.bold('Resources'));
|
|
501
|
-
for (const kind of
|
|
502
|
-
|
|
503
|
-
if (!c)
|
|
504
|
-
continue;
|
|
505
|
-
const breakdown = formatScopeBreakdown(c.bySource);
|
|
506
|
-
console.log(` ${kind.padEnd(10)} ${String(c.total).padStart(4)} ${chalk.gray(breakdown)}`);
|
|
569
|
+
for (const kind of SIMPLE_KINDS) {
|
|
570
|
+
printSimpleResourceRow(kind, itemsByKind[kind]);
|
|
507
571
|
}
|
|
572
|
+
printExpandedSection('Hooks', hookRows(itemsByKind.hooks, hookByScript));
|
|
573
|
+
printExpandedSection('Plugins', pluginRows(itemsByKind.plugins));
|
|
574
|
+
printExpandedSection('MCP', mcpRows(itemsByKind.mcp, mcpConfigs));
|
|
508
575
|
}
|
|
509
576
|
if (sessions) {
|
|
510
577
|
console.log('\n' + chalk.bold('Sessions'));
|
|
@@ -526,7 +593,7 @@ function renderItemList(header, jsonHead, kind, items, options) {
|
|
|
526
593
|
...jsonHead,
|
|
527
594
|
kind,
|
|
528
595
|
count: items.length,
|
|
529
|
-
items: items.map(i => ({ name: i.name, source: i.source, path: i.path, description: i.description })),
|
|
596
|
+
items: items.map(i => ({ name: i.name, source: i.source, path: i.path, description: i.description, ...(i.groups ? { groups: i.groups } : {}) })),
|
|
530
597
|
}, null, 2));
|
|
531
598
|
return;
|
|
532
599
|
}
|
|
@@ -542,9 +609,23 @@ function renderItemList(header, jsonHead, kind, items, options) {
|
|
|
542
609
|
if (item.description) {
|
|
543
610
|
console.log(` ${chalk.gray(truncate(item.description, 90))}`);
|
|
544
611
|
}
|
|
612
|
+
if (item.groups)
|
|
613
|
+
printGroupRows(item.groups);
|
|
545
614
|
}
|
|
546
615
|
console.log('');
|
|
547
616
|
}
|
|
617
|
+
/** Print a plugin's resource breakdown as aligned `label items` rows under a list entry. */
|
|
618
|
+
function printGroupRows(groups) {
|
|
619
|
+
if (groups.length === 0)
|
|
620
|
+
return;
|
|
621
|
+
const width = Math.max(...groups.map(g => g.label.length));
|
|
622
|
+
for (const g of groups) {
|
|
623
|
+
const colorFn = PLUGIN_GROUP_COLORS[g.label] ?? chalk.white;
|
|
624
|
+
const label = chalk.gray(g.label.padEnd(width));
|
|
625
|
+
const value = g.items.map((s) => colorFn(s)).join(chalk.gray(', '));
|
|
626
|
+
console.log(` ${label} ${value}`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
548
629
|
// ─── Detail mode (fuzzy) ─────────────────────────────────────────────────────
|
|
549
630
|
async function renderDetail(agent, version, versionHome, kind, query, options) {
|
|
550
631
|
const items = collectKind(agent, versionHome, kind);
|
|
@@ -610,14 +691,52 @@ function collectCapabilities(agent, version) {
|
|
|
610
691
|
}
|
|
611
692
|
return out;
|
|
612
693
|
}
|
|
613
|
-
function
|
|
694
|
+
function collectItemsByKind(agent, versionHome) {
|
|
695
|
+
const out = {};
|
|
696
|
+
for (const kind of DRILLABLE_KINDS)
|
|
697
|
+
out[kind] = collectKind(agent, versionHome, kind);
|
|
698
|
+
return out;
|
|
699
|
+
}
|
|
700
|
+
/** A simple-kind count row: `kind N user:30 system:12 name, name …(+K)`. */
|
|
701
|
+
function printSimpleResourceRow(kind, items) {
|
|
702
|
+
const count = String(items.length).padStart(4);
|
|
703
|
+
const breakdown = chalk.gray(scopeBreakdownPlain(countBySource(items.map(i => i.source))).padEnd(18));
|
|
704
|
+
const preview = items.length > 0 ? chalk.gray(truncate(previewNames(items, 3), 48)) : '';
|
|
705
|
+
console.log(` ${kind.padEnd(10)} ${count} ${breakdown} ${preview}`.trimEnd());
|
|
706
|
+
}
|
|
707
|
+
/**
|
|
708
|
+
* Build the `resources` JSON: every kind keeps `total` + `bySource` (back-compat),
|
|
709
|
+
* simple kinds add `names`, and the rich kinds add structured `items` (hook
|
|
710
|
+
* events/predicates, mcp transport/url/command, plugin version + group counts).
|
|
711
|
+
*/
|
|
712
|
+
function summaryResourcesJson(itemsByKind, hookByScript, mcpConfigs) {
|
|
614
713
|
const out = {};
|
|
615
714
|
for (const kind of DRILLABLE_KINDS) {
|
|
616
|
-
const items =
|
|
617
|
-
const
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
715
|
+
const items = itemsByKind[kind];
|
|
716
|
+
const base = { total: items.length, bySource: countBySource(items.map(i => i.source)) };
|
|
717
|
+
if (kind === 'hooks') {
|
|
718
|
+
out[kind] = { ...base, items: items.map(i => {
|
|
719
|
+
const h = hookByScript.get(i.name);
|
|
720
|
+
return { name: i.name, source: i.source, events: h?.events ?? [], matcher: h?.matcher, matches: h?.matches, cache: h?.cache };
|
|
721
|
+
}) };
|
|
722
|
+
}
|
|
723
|
+
else if (kind === 'mcp') {
|
|
724
|
+
out[kind] = { ...base, items: items.map(i => {
|
|
725
|
+
const c = mcpConfigs.get(i.name);
|
|
726
|
+
return { name: i.name, source: i.source, transport: c?.transport, url: c?.url, command: c?.command, args: c?.args };
|
|
727
|
+
}) };
|
|
728
|
+
}
|
|
729
|
+
else if (kind === 'plugins') {
|
|
730
|
+
out[kind] = { ...base, items: items.map(i => ({
|
|
731
|
+
name: i.name,
|
|
732
|
+
source: i.source,
|
|
733
|
+
version: i.extra?.find(([k]) => k === 'version')?.[1],
|
|
734
|
+
groups: Object.fromEntries((i.groups ?? []).map(g => [g.label, g.items.length])),
|
|
735
|
+
})) };
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
out[kind] = { ...base, names: items.map(i => i.name) };
|
|
739
|
+
}
|
|
621
740
|
}
|
|
622
741
|
return out;
|
|
623
742
|
}
|
|
@@ -653,17 +772,6 @@ function pluginItems() {
|
|
|
653
772
|
*/
|
|
654
773
|
function pluginToItem(plugin, source) {
|
|
655
774
|
const extra = [];
|
|
656
|
-
const list = (names) => names.length <= 8 ? names.join(', ') : `${names.slice(0, 8).join(', ')}, +${names.length - 8} more`;
|
|
657
|
-
if (plugin.skills.length)
|
|
658
|
-
extra.push(['skills', `${plugin.skills.length} (${list(plugin.skills)})`]);
|
|
659
|
-
if (plugin.commands.length)
|
|
660
|
-
extra.push(['commands', `${plugin.commands.length} (${list(plugin.commands)})`]);
|
|
661
|
-
if (plugin.agentDefs.length)
|
|
662
|
-
extra.push(['subagents', `${plugin.agentDefs.length} (${list(plugin.agentDefs)})`]);
|
|
663
|
-
if (plugin.hooks.length)
|
|
664
|
-
extra.push(['hooks', String(plugin.hooks.length)]);
|
|
665
|
-
if (plugin.mcpServers.length)
|
|
666
|
-
extra.push(['mcp', list(plugin.mcpServers)]);
|
|
667
775
|
if (plugin.manifest.version)
|
|
668
776
|
extra.push(['version', plugin.manifest.version]);
|
|
669
777
|
return {
|
|
@@ -673,6 +781,7 @@ function pluginToItem(plugin, source) {
|
|
|
673
781
|
linkTarget: linkTarget(plugin.root),
|
|
674
782
|
description: plugin.manifest.description ?? '',
|
|
675
783
|
extra,
|
|
784
|
+
groups: pluginResourceGroups(plugin),
|
|
676
785
|
};
|
|
677
786
|
}
|
|
678
787
|
function entriesFromAgentResources(agent, versionHome, kind) {
|
|
@@ -742,12 +851,192 @@ function buildDetailRows(item, kind) {
|
|
|
742
851
|
rows.push(['tools', fm.tools.join(', ')]);
|
|
743
852
|
}
|
|
744
853
|
}
|
|
745
|
-
// Plugin bundles
|
|
746
|
-
|
|
747
|
-
|
|
854
|
+
// Plugin bundles surface their nested resources (skills, commands, …) plus
|
|
855
|
+
// scalar rows (version).
|
|
856
|
+
if (kind === 'plugins') {
|
|
857
|
+
if (item.groups)
|
|
858
|
+
for (const g of item.groups)
|
|
859
|
+
rows.push([g.label, g.items.join(', ')]);
|
|
860
|
+
if (item.extra)
|
|
861
|
+
rows.push(...item.extra);
|
|
748
862
|
}
|
|
749
863
|
return rows;
|
|
750
864
|
}
|
|
865
|
+
/** `system` → `sys`; everything else unchanged. Keeps the tag column narrow. */
|
|
866
|
+
function abbrevSource(s) {
|
|
867
|
+
return s === 'system' ? 'sys' : s;
|
|
868
|
+
}
|
|
869
|
+
/**
|
|
870
|
+
* Compact one-liner for a hook from its manifest entry: the firing events (with
|
|
871
|
+
* the matcher/tool-name in parens), then a `·`-separated predicate summary, then
|
|
872
|
+
* an optional cache tail. Plain text — the caller applies color.
|
|
873
|
+
*/
|
|
874
|
+
export function summarizeHook(hook) {
|
|
875
|
+
const events = (hook.events ?? []).join('/') || '(no event)';
|
|
876
|
+
let matcher = hook.matcher;
|
|
877
|
+
if (!matcher && hook.matches?.tool_name) {
|
|
878
|
+
const tn = hook.matches.tool_name;
|
|
879
|
+
matcher = Array.isArray(tn) ? tn.join('|') : tn;
|
|
880
|
+
}
|
|
881
|
+
const head = matcher ? `${events}(${matcher})` : events;
|
|
882
|
+
const parts = [head];
|
|
883
|
+
const preds = summarizeMatches(hook.matches);
|
|
884
|
+
if (preds)
|
|
885
|
+
parts.push(preds);
|
|
886
|
+
let line = parts.join(' · ');
|
|
887
|
+
const ttl = hookCacheTtl(hook.cache);
|
|
888
|
+
if (ttl)
|
|
889
|
+
line += ` (${ttl} cache)`;
|
|
890
|
+
return line;
|
|
891
|
+
}
|
|
892
|
+
/** `·`-separated predicate summary from a hook's `matches:` block (tool_name omitted — shown in the matcher parens). */
|
|
893
|
+
function summarizeMatches(m) {
|
|
894
|
+
if (!m)
|
|
895
|
+
return '';
|
|
896
|
+
const bits = [];
|
|
897
|
+
if (m.git_dirty)
|
|
898
|
+
bits.push('git_dirty');
|
|
899
|
+
if (m.prompt_contains)
|
|
900
|
+
bits.push(`prompt~"${truncate(m.prompt_contains, 24)}"`);
|
|
901
|
+
if (m.prompt_matches)
|
|
902
|
+
bits.push(`prompt=/${truncate(m.prompt_matches, 24)}/`);
|
|
903
|
+
if (m.tool_args_match)
|
|
904
|
+
bits.push(`args=/${truncate(m.tool_args_match, 20)}/`);
|
|
905
|
+
if (m.cwd_includes) {
|
|
906
|
+
const c = Array.isArray(m.cwd_includes) ? m.cwd_includes.join('|') : m.cwd_includes;
|
|
907
|
+
bits.push(`cwd~${truncate(c, 24)}`);
|
|
908
|
+
}
|
|
909
|
+
if (m.project_has)
|
|
910
|
+
bits.push(`has ${m.project_has}`);
|
|
911
|
+
return bits.join(' · ');
|
|
912
|
+
}
|
|
913
|
+
/** Normalize a hook cache shorthand/object to a display ttl ("5m", "1h"); null when uncached. */
|
|
914
|
+
function hookCacheTtl(cache) {
|
|
915
|
+
if (cache === undefined || cache === null)
|
|
916
|
+
return null;
|
|
917
|
+
if (typeof cache === 'string')
|
|
918
|
+
return cache.replace(/-bg$/, '');
|
|
919
|
+
return String(cache.ttl);
|
|
920
|
+
}
|
|
921
|
+
/** Compact one-liner for an MCP server: padded transport + the url (http) or command line (stdio). */
|
|
922
|
+
export function summarizeMcp(cfg) {
|
|
923
|
+
const target = cfg.transport === 'http'
|
|
924
|
+
? (cfg.url ?? '')
|
|
925
|
+
: [cfg.command, ...(cfg.args ?? [])].filter(Boolean).join(' ');
|
|
926
|
+
return `${cfg.transport.padEnd(5)} ${truncate(target, 60)}`.trimEnd();
|
|
927
|
+
}
|
|
928
|
+
/** Print `Title (N)` then up to `max` aligned `[source] name detail` rows with a `…(+K)` tail. */
|
|
929
|
+
function printExpandedSection(title, rows, max = 6) {
|
|
930
|
+
console.log('\n' + chalk.bold(title) + chalk.gray(` (${rows.length})`));
|
|
931
|
+
if (rows.length === 0) {
|
|
932
|
+
console.log(chalk.gray(' (none)'));
|
|
933
|
+
return;
|
|
934
|
+
}
|
|
935
|
+
const shown = rows.slice(0, max);
|
|
936
|
+
const nameW = Math.max(...shown.map(r => r.name.length));
|
|
937
|
+
for (const r of shown) {
|
|
938
|
+
const tag = chalk.gray(`[${abbrevSource(r.source)}]`.padEnd(8));
|
|
939
|
+
const padded = r.name.padEnd(nameW);
|
|
940
|
+
const name = r.linkTarget ? termLink(chalk.cyan(padded), r.linkTarget) : chalk.cyan(padded);
|
|
941
|
+
const detail = r.detail ? ' ' + chalk.gray(r.detail) : '';
|
|
942
|
+
console.log(` ${tag} ${name}${detail}`);
|
|
943
|
+
}
|
|
944
|
+
if (rows.length > max)
|
|
945
|
+
console.log(chalk.gray(` …(+${rows.length - max})`));
|
|
946
|
+
}
|
|
947
|
+
/** Tally a source list into `{user: n, system: m}`. */
|
|
948
|
+
function countBySource(sources) {
|
|
949
|
+
const out = {};
|
|
950
|
+
for (const s of sources)
|
|
951
|
+
out[s] = (out[s] || 0) + 1;
|
|
952
|
+
return out;
|
|
953
|
+
}
|
|
954
|
+
/** Unbracketed scope breakdown for the simple count rows: `user:30 system:12`. */
|
|
955
|
+
function scopeBreakdownPlain(bySource) {
|
|
956
|
+
return Object.entries(bySource).map(([k, v]) => `${k}:${v}`).join(' ');
|
|
957
|
+
}
|
|
958
|
+
/**
|
|
959
|
+
* Index a hook manifest by script basename (no extension). Installed hooks are
|
|
960
|
+
* named after their script file (`04-capture-…`), while the manifest is keyed by
|
|
961
|
+
* logical name (`capture-…`) with the filename in `script:` — so we join on the
|
|
962
|
+
* script basename, not the manifest key.
|
|
963
|
+
*/
|
|
964
|
+
export function hookManifestByScript(manifest) {
|
|
965
|
+
const out = new Map();
|
|
966
|
+
for (const hook of Object.values(manifest)) {
|
|
967
|
+
if (hook && typeof hook.script === 'string') {
|
|
968
|
+
out.set(path.basename(hook.script).replace(/\.[^.]+$/, ''), hook);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
return out;
|
|
972
|
+
}
|
|
973
|
+
/** Build hook rows by enriching the installed hook items with manifest events/predicates. */
|
|
974
|
+
function hookRows(items, byScript) {
|
|
975
|
+
return items.map(item => {
|
|
976
|
+
const hook = byScript.get(item.name);
|
|
977
|
+
return {
|
|
978
|
+
source: item.source,
|
|
979
|
+
name: item.name,
|
|
980
|
+
linkTarget: item.linkTarget,
|
|
981
|
+
// Hooks are shell scripts with no human description — show events/predicates
|
|
982
|
+
// from the manifest, or nothing rather than a meaningless shebang line.
|
|
983
|
+
detail: hook ? summarizeHook(hook) : '',
|
|
984
|
+
};
|
|
985
|
+
});
|
|
986
|
+
}
|
|
987
|
+
/** Build plugin rows: `vVERSION skills:6 commands:5 …` from the bundle's groups. */
|
|
988
|
+
function pluginRows(items) {
|
|
989
|
+
return items.map(item => {
|
|
990
|
+
const version = item.extra?.find(([k]) => k === 'version')?.[1];
|
|
991
|
+
const counts = (item.groups ?? []).map(g => `${g.label}:${g.items.length}`).join(' ');
|
|
992
|
+
const detail = [version ? `v${version}` : '', counts].filter(Boolean).join(' ');
|
|
993
|
+
return { source: item.source, name: item.name, detail, linkTarget: item.linkTarget };
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
/** Build MCP rows by joining the installed mcp items with their full configs (transport/url/command). */
|
|
997
|
+
function mcpRows(items, configs) {
|
|
998
|
+
return items.map(item => {
|
|
999
|
+
const cfg = configs.get(item.name);
|
|
1000
|
+
return { source: item.source, name: item.name, detail: cfg ? summarizeMcp(cfg) : item.description };
|
|
1001
|
+
});
|
|
1002
|
+
}
|
|
1003
|
+
/** Read a repo/agents.yaml `hooks:` section into a name→ManifestHook map (best-effort). */
|
|
1004
|
+
function hookManifestFromFile(agentsYamlPath) {
|
|
1005
|
+
try {
|
|
1006
|
+
const meta = yaml.parse(fs.readFileSync(agentsYamlPath, 'utf-8'));
|
|
1007
|
+
return meta?.hooks ?? {};
|
|
1008
|
+
}
|
|
1009
|
+
catch {
|
|
1010
|
+
return {};
|
|
1011
|
+
}
|
|
1012
|
+
}
|
|
1013
|
+
/**
|
|
1014
|
+
* Merge the system + user `agents.yaml` hook manifests (user wins on key
|
|
1015
|
+
* collision). Built directly from the two layer files rather than via
|
|
1016
|
+
* `parseHookManifest()` so inspecting never emits the shadow/override warnings
|
|
1017
|
+
* that the registrar path prints.
|
|
1018
|
+
*/
|
|
1019
|
+
function loadCentralHookManifest() {
|
|
1020
|
+
return {
|
|
1021
|
+
...hookManifestFromFile(path.join(getSystemAgentsDir(), 'agents.yaml')),
|
|
1022
|
+
...hookManifestFromFile(path.join(getUserAgentsDir(), 'agents.yaml')),
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
/**
|
|
1026
|
+
* Hook items for a repo's Hooks section. Uses the grouped hook reader (script +
|
|
1027
|
+
* data file collapsed into one entry, non-hook files like promptcuts.yaml or
|
|
1028
|
+
* README.md filtered out) rather than a naive readdir, so names are clean and
|
|
1029
|
+
* join cleanly against the manifest by script basename.
|
|
1030
|
+
*/
|
|
1031
|
+
function repoHookItems(repo) {
|
|
1032
|
+
return listHookEntriesFromDir(path.join(repo.root, 'hooks')).map(h => ({
|
|
1033
|
+
name: h.name,
|
|
1034
|
+
source: repo.label,
|
|
1035
|
+
path: h.scriptPath,
|
|
1036
|
+
linkTarget: h.scriptPath,
|
|
1037
|
+
description: '',
|
|
1038
|
+
}));
|
|
1039
|
+
}
|
|
751
1040
|
function findMatches(items, query) {
|
|
752
1041
|
const q = query.toLowerCase();
|
|
753
1042
|
const out = [];
|
|
@@ -945,9 +1234,3 @@ function truncate(s, n) {
|
|
|
945
1234
|
return s;
|
|
946
1235
|
return s.slice(0, n - 1) + '…';
|
|
947
1236
|
}
|
|
948
|
-
function formatScopeBreakdown(bySource) {
|
|
949
|
-
const entries = Object.entries(bySource);
|
|
950
|
-
if (entries.length === 0)
|
|
951
|
-
return '';
|
|
952
|
-
return '[' + entries.map(([k, v]) => `${k}:${v}`).join(' ') + ']';
|
|
953
|
-
}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -92,9 +92,9 @@ function parseMcpAgentTargets(value) {
|
|
|
92
92
|
}
|
|
93
93
|
continue;
|
|
94
94
|
}
|
|
95
|
-
const resolvedVersion = versionToken === 'latest'
|
|
96
|
-
? installedVersions[
|
|
97
|
-
|
|
95
|
+
const resolvedVersion = versionToken === 'latest' ? installedVersions[installedVersions.length - 1]
|
|
96
|
+
: versionToken === 'oldest' ? installedVersions[0]
|
|
97
|
+
: versionToken;
|
|
98
98
|
if (!installedVersions.includes(resolvedVersion)) {
|
|
99
99
|
throw new VersionNotInstalledError(agentId, resolvedVersion, installedVersions);
|
|
100
100
|
}
|
|
@@ -22,4 +22,6 @@ interface MarketplaceRow {
|
|
|
22
22
|
* default Claude version's settings.json#enabledPlugins keyed on @<marketplace>.
|
|
23
23
|
*/
|
|
24
24
|
export declare function collectMarketplaceRows(): MarketplaceRow[];
|
|
25
|
+
/** Per-category color for a plugin resource breakdown (shared with `agents inspect`). */
|
|
26
|
+
export declare const PLUGIN_GROUP_COLORS: Record<string, (s: string) => string>;
|
|
25
27
|
export {};
|