@pellux/goodvibes-agent 0.1.69 → 0.1.71

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.
Files changed (60) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +42 -1
  3. package/src/agent/skill-discovery.ts +119 -0
  4. package/src/input/commands/delegation-runtime.ts +0 -8
  5. package/src/input/commands/experience-runtime.ts +0 -177
  6. package/src/input/commands/guidance-runtime.ts +9 -77
  7. package/src/input/commands/local-runtime.ts +1 -57
  8. package/src/input/commands/local-setup-review.ts +1 -1
  9. package/src/input/commands/operator-runtime.ts +1 -145
  10. package/src/input/commands/platform-access-runtime.ts +2 -195
  11. package/src/input/commands/product-runtime.ts +0 -116
  12. package/src/input/commands/security-runtime.ts +88 -0
  13. package/src/input/commands/session-content.ts +0 -97
  14. package/src/input/commands/shell-core.ts +1 -22
  15. package/src/input/commands.ts +2 -43
  16. package/src/panels/builtin/operations.ts +3 -184
  17. package/src/panels/index.ts +0 -11
  18. package/src/version.ts +1 -1
  19. package/src/input/commands/branch-runtime.ts +0 -72
  20. package/src/input/commands/control-room-runtime.ts +0 -234
  21. package/src/input/commands/discovery-runtime.ts +0 -61
  22. package/src/input/commands/hooks-runtime.ts +0 -207
  23. package/src/input/commands/incident-runtime.ts +0 -106
  24. package/src/input/commands/integration-runtime.ts +0 -437
  25. package/src/input/commands/local-setup.ts +0 -288
  26. package/src/input/commands/managed-runtime.ts +0 -240
  27. package/src/input/commands/marketplace-runtime.ts +0 -305
  28. package/src/input/commands/memory-product-runtime.ts +0 -148
  29. package/src/input/commands/operator-panel-runtime.ts +0 -146
  30. package/src/input/commands/platform-services-runtime.ts +0 -271
  31. package/src/input/commands/profile-sync-runtime.ts +0 -110
  32. package/src/input/commands/provider.ts +0 -363
  33. package/src/input/commands/remote-runtime-pool.ts +0 -89
  34. package/src/input/commands/remote-runtime-setup.ts +0 -226
  35. package/src/input/commands/remote-runtime.ts +0 -432
  36. package/src/input/commands/replay-runtime.ts +0 -25
  37. package/src/input/commands/services-runtime.ts +0 -220
  38. package/src/input/commands/settings-sync-runtime.ts +0 -197
  39. package/src/input/commands/share-runtime.ts +0 -127
  40. package/src/input/commands/skills-runtime.ts +0 -226
  41. package/src/input/commands/teleport-runtime.ts +0 -68
  42. package/src/panels/cockpit-panel.ts +0 -183
  43. package/src/panels/communication-panel.ts +0 -153
  44. package/src/panels/control-plane-panel.ts +0 -211
  45. package/src/panels/forensics-panel.ts +0 -364
  46. package/src/panels/hooks-panel.ts +0 -239
  47. package/src/panels/incident-review-panel.ts +0 -197
  48. package/src/panels/marketplace-panel.ts +0 -212
  49. package/src/panels/ops-control-panel.ts +0 -150
  50. package/src/panels/ops-strategy-panel.ts +0 -235
  51. package/src/panels/orchestration-panel.ts +0 -272
  52. package/src/panels/plugins-panel.ts +0 -178
  53. package/src/panels/remote-panel.ts +0 -449
  54. package/src/panels/routes-panel.ts +0 -178
  55. package/src/panels/services-panel.ts +0 -231
  56. package/src/panels/settings-sync-panel.ts +0 -120
  57. package/src/panels/skills-panel.ts +0 -431
  58. package/src/panels/watchers-panel.ts +0 -193
  59. package/src/verification/live-verifier.ts +0 -588
  60. package/src/verification/verification-ledger.ts +0 -239
@@ -1,437 +0,0 @@
1
- import { resolve } from 'path';
2
- import type { PluginStatus } from '@pellux/goodvibes-sdk/platform/plugins';
3
- import { getPluginDirectories, getUserPluginDirectory } from '../../plugins/loader';
4
- import type { CommandRegistry } from '../command-registry.ts';
5
- import {
6
- installEcosystemCatalogEntry,
7
- listInstalledEcosystemEntries,
8
- loadEcosystemCatalog,
9
- removeEcosystemCatalogEntry,
10
- reviewEcosystemCatalogEntry,
11
- searchEcosystemCatalog,
12
- updateInstalledEcosystemEntry,
13
- upsertEcosystemCatalogEntry,
14
- uninstallEcosystemCatalogEntry,
15
- } from '@/runtime/index.ts';
16
- import { requireEcosystemCatalogPaths, requirePluginPathOptions } from './runtime-services.ts';
17
- import { requireYesFlag, stripYesFlag } from './confirmation.ts';
18
-
19
- export function registerIntegrationRuntimeCommands(registry: CommandRegistry): void {
20
- registry.register({
21
- name: 'plugin',
22
- aliases: [],
23
- description: 'Manage plugins, trust, review, and ecosystem paths',
24
- usage: 'list | dirs | inspect <name> | review | installed | catalog-review <id> | publish-local <id> <path> <summary...> --yes | unpublish <id> --yes | install <id> [project|user] --yes | update <id> [project|user] --yes | uninstall <id> [project|user] --yes | enable <name> --yes | disable <name> --yes | reload --yes',
25
- argsHint: 'list | dirs | inspect | review | installed | catalog-review | publish-local --yes | unpublish --yes | install --yes | update --yes | uninstall --yes | enable --yes | disable --yes | reload --yes',
26
- async handler(args, ctx) {
27
- const parsed = stripYesFlag(args);
28
- const commandArgs = [...parsed.rest];
29
- const pluginManager = ctx.extensions.pluginManager;
30
- const ecosystemPaths = requireEcosystemCatalogPaths(ctx);
31
- const pluginPaths = requirePluginPathOptions(ctx);
32
- if (!pluginManager) {
33
- ctx.print('Plugin manager is not available in this runtime.');
34
- return;
35
- }
36
- const sub = commandArgs[0];
37
-
38
- if (!sub || sub === 'open' || sub === 'panel') {
39
- if (ctx.showPanel) ctx.showPanel('plugins');
40
- return;
41
- }
42
-
43
- if (sub === 'list') {
44
- const plugins = pluginManager.list() as PluginStatus[];
45
- if (plugins.length === 0) {
46
- const directories = getPluginDirectories(pluginPaths)
47
- .map((dir) => ` ${dir}`)
48
- .join('\n');
49
- ctx.print(
50
- `No plugins installed.\nPlugin search directories:\n${directories}\nPlace a plugin folder in one of those locations with manifest.json and index.ts.`
51
- );
52
- return;
53
- }
54
- const lines: string[] = ['Installed plugins:'];
55
- for (const p of plugins) {
56
- const statusIcon = p.active ? '[active] ' : p.enabled ? '[loading] ' : '[disabled]';
57
- lines.push(` ${statusIcon} ${p.name.padEnd(24)} v${p.version} — ${p.description}`);
58
- if (p.author) lines.push(` by ${p.author}`);
59
- }
60
- lines.push('');
61
- lines.push('Use /plugin enable <name> --yes or /plugin disable <name> --yes to toggle plugins.');
62
- ctx.print(lines.join('\n'));
63
- return;
64
- }
65
- if (sub === 'dirs') {
66
- const directories = getPluginDirectories(pluginPaths);
67
- ctx.print([
68
- 'Plugin Search Directories',
69
- ...directories.map((dir) => ` ${dir}`),
70
- '',
71
- `User plugin directory: ${getUserPluginDirectory(pluginPaths)}`,
72
- ].join('\n'));
73
- return;
74
- }
75
- if (sub === 'inspect') {
76
- const name = commandArgs[1];
77
- if (!name) {
78
- ctx.print('Usage: /plugin inspect <name>');
79
- return;
80
- }
81
- const status = pluginManager.list().find((plugin) => plugin.name === name);
82
- if (!status) {
83
- ctx.print(`Error: Plugin '${name}' not found.`);
84
- return;
85
- }
86
- const capabilities = pluginManager.capabilities(name);
87
- const trust = pluginManager.getTrustRecord(name);
88
- const quarantine = pluginManager.getQuarantineRecord(name);
89
- ctx.print([
90
- `Plugin ${name}`,
91
- ` version: ${status.version}`,
92
- ` state: ${status.active ? 'active' : status.enabled ? 'enabled' : 'disabled'}`,
93
- ` trustTier: ${status.trustTier}`,
94
- ` quarantined: ${status.quarantined ? 'yes' : 'no'}`,
95
- ` requestedCapabilities: ${capabilities?.requested.length ?? 0}`,
96
- ` highRiskCapabilities: ${capabilities?.highRisk.length ?? 0}`,
97
- ` blockedCapabilities: ${capabilities?.blocked.length ?? 0}`,
98
- ` signedFingerprint: ${trust?.signatureFingerprint ?? 'n/a'}`,
99
- ` quarantineReason: ${quarantine?.reason ?? 'n/a'}`,
100
- ].join('\n'));
101
- return;
102
- }
103
- if (sub === 'review') {
104
- const plugins = pluginManager.list();
105
- ctx.print([
106
- 'Plugin Security Review',
107
- ` total: ${plugins.length}`,
108
- ` active: ${plugins.filter((plugin) => plugin.active).length}`,
109
- ` trusted: ${plugins.filter((plugin) => plugin.trustTier === 'trusted').length}`,
110
- ` limited: ${plugins.filter((plugin) => plugin.trustTier === 'limited').length}`,
111
- ` untrusted: ${plugins.filter((plugin) => plugin.trustTier === 'untrusted').length}`,
112
- ` quarantined: ${plugins.filter((plugin) => plugin.quarantined).length}`,
113
- ].join('\n'));
114
- return;
115
- }
116
- if (sub === 'browse' || sub === 'catalog') {
117
- const query = commandArgs.slice(1).join(' ');
118
- const entries = query
119
- ? searchEcosystemCatalog('plugin', query, ecosystemPaths)
120
- : loadEcosystemCatalog('plugin', ecosystemPaths);
121
- if (entries.length === 0) {
122
- ctx.print(query
123
- ? `No curated plugin catalog entries matched "${query}".`
124
- : 'No curated plugin catalog entries found. Add .goodvibes/agent/ecosystem/plugins.json to publish a local-first plugin catalog.');
125
- return;
126
- }
127
- ctx.print([
128
- `Curated Plugin Catalog (${entries.length})`,
129
- ...entries.map((entry) => ` ${entry.id} ${entry.name} [${entry.tags.join(', ') || 'untagged'}] ${entry.summary}`),
130
- ].join('\n'));
131
- return;
132
- }
133
- if (sub === 'installed') {
134
- const receipts = listInstalledEcosystemEntries('plugin', ecosystemPaths);
135
- if (receipts.length === 0) {
136
- ctx.print('No curated plugins installed from local catalogs yet.');
137
- return;
138
- }
139
- ctx.print([
140
- `Installed Curated Plugins (${receipts.length})`,
141
- ...receipts.map((receipt) => ` ${receipt.entry.id} ${receipt.scope} ${receipt.targetPath}`),
142
- ].join('\n'));
143
- return;
144
- }
145
- if (sub === 'catalog-review') {
146
- const entryId = commandArgs[1];
147
- if (!entryId) {
148
- ctx.print('Usage: /plugin catalog-review <catalog-id>');
149
- return;
150
- }
151
- const entry = loadEcosystemCatalog('plugin', ecosystemPaths).find((candidate) => candidate.id === entryId);
152
- if (!entry) {
153
- ctx.print(`Unknown curated plugin entry: ${entryId}`);
154
- return;
155
- }
156
- const review = reviewEcosystemCatalogEntry(entry, ecosystemPaths);
157
- ctx.print([
158
- `Plugin Catalog Review: ${entry.name}`,
159
- ` id: ${entry.id}`,
160
- ` source: ${entry.source}`,
161
- ` sourceKind: ${review.sourceKind}`,
162
- ` sourceExists: ${review.sourceExists ? 'yes' : 'no'}`,
163
- ` recommendedScope: ${review.recommendedScope}`,
164
- ` risk: ${review.riskLevel}`,
165
- ` trust notes: ${entry.trustNotes ?? '(none)'}`,
166
- ` provenance: ${entry.provenance ?? '(none)'}`,
167
- ` update hint: ${entry.updateHint ?? '(none)'}`,
168
- ].join('\n'));
169
- return;
170
- }
171
- if (sub === 'install-hint') {
172
- const entryId = commandArgs[1];
173
- if (!entryId) {
174
- ctx.print('Usage: /plugin install-hint <catalog-id>');
175
- return;
176
- }
177
- const entry = loadEcosystemCatalog('plugin', ecosystemPaths).find((candidate) => candidate.id === entryId);
178
- if (!entry) {
179
- ctx.print(`Unknown curated plugin entry: ${entryId}`);
180
- return;
181
- }
182
- ctx.print([
183
- `Plugin Install Guidance: ${entry.name}`,
184
- ` id: ${entry.id}`,
185
- ` source: ${entry.source}`,
186
- ` tags: ${entry.tags.join(', ') || '(none)'}`,
187
- ` trust notes: ${entry.trustNotes ?? '(none)'}`,
188
- ` install hint: ${entry.installHint ?? 'Place the plugin under a configured plugin search directory and use /plugin reload --yes.'}`,
189
- ].join('\n'));
190
- return;
191
- }
192
- if (sub === 'publish-local') {
193
- const entryId = commandArgs[1];
194
- const sourcePath = commandArgs[2];
195
- const summary = commandArgs.slice(3).join(' ').trim();
196
- if (!entryId || !sourcePath || !summary) {
197
- ctx.print('Usage: /plugin publish-local <catalog-id> <path> <summary...> --yes');
198
- return;
199
- }
200
- if (!parsed.yes) {
201
- requireYesFlag(ctx, `publish curated plugin ${entryId}`, '/plugin publish-local <catalog-id> <path> <summary...> --yes');
202
- return;
203
- }
204
- const result = upsertEcosystemCatalogEntry({
205
- id: entryId,
206
- kind: 'plugin',
207
- name: entryId.replace(/[-_]/g, ' ').replace(/\b\w/g, (char) => char.toUpperCase()),
208
- summary,
209
- source: sourcePath,
210
- tags: ['local-first', 'published'],
211
- provenance: 'operator-published',
212
- updateHint: 'Use /plugin publish-local again to refresh catalog metadata after edits.',
213
- }, ecosystemPaths);
214
- ctx.print(result.ok
215
- ? `Published curated plugin ${entryId} into ${result.path}`
216
- : `Error: ${result.error}`);
217
- return;
218
- }
219
- if (sub === 'unpublish') {
220
- const entryId = commandArgs[1];
221
- if (!entryId) {
222
- ctx.print('Usage: /plugin unpublish <catalog-id> --yes');
223
- return;
224
- }
225
- if (!parsed.yes) {
226
- requireYesFlag(ctx, `unpublish curated plugin ${entryId}`, '/plugin unpublish <catalog-id> --yes');
227
- return;
228
- }
229
- const result = removeEcosystemCatalogEntry('plugin', entryId, ecosystemPaths);
230
- ctx.print(result.ok
231
- ? `Removed curated plugin ${entryId} from ${result.path}`
232
- : `Error: ${result.error}`);
233
- return;
234
- }
235
- if (sub === 'install') {
236
- const entryId = commandArgs[1];
237
- const scopeArg = commandArgs[2];
238
- if (!entryId) {
239
- ctx.print('Usage: /plugin install <catalog-id> [project|user] --yes');
240
- return;
241
- }
242
- if (!parsed.yes) {
243
- requireYesFlag(ctx, `install curated plugin ${entryId}`, '/plugin install <catalog-id> [project|user] --yes');
244
- return;
245
- }
246
- const scope = scopeArg === 'user' ? 'user' : 'project';
247
- const result = installEcosystemCatalogEntry('plugin', entryId, { ...ecosystemPaths, scope });
248
- ctx.print(result.ok
249
- ? `Installed curated plugin ${entryId} into ${result.receipt.targetPath}`
250
- : `Error: ${result.error}`);
251
- return;
252
- }
253
- if (sub === 'update') {
254
- const entryId = commandArgs[1];
255
- const scopeArg = commandArgs[2];
256
- if (!entryId) {
257
- ctx.print('Usage: /plugin update <catalog-id> [project|user] --yes');
258
- return;
259
- }
260
- if (!parsed.yes) {
261
- requireYesFlag(ctx, `update curated plugin ${entryId}`, '/plugin update <catalog-id> [project|user] --yes');
262
- return;
263
- }
264
- const scope = scopeArg === 'user' ? 'user' : 'project';
265
- const result = updateInstalledEcosystemEntry('plugin', entryId, { ...ecosystemPaths, scope });
266
- ctx.print(result.ok
267
- ? `Updated curated plugin ${entryId} in ${result.receipt.targetPath}`
268
- : `Error: ${result.error}`);
269
- return;
270
- }
271
- if (sub === 'uninstall') {
272
- const entryId = commandArgs[1];
273
- const scopeArg = commandArgs[2];
274
- if (!entryId) {
275
- ctx.print('Usage: /plugin uninstall <catalog-id> [project|user] --yes');
276
- return;
277
- }
278
- if (!parsed.yes) {
279
- requireYesFlag(ctx, `uninstall curated plugin ${entryId}`, '/plugin uninstall <catalog-id> [project|user] --yes');
280
- return;
281
- }
282
- const scope = scopeArg === 'user' ? 'user' : 'project';
283
- const result = uninstallEcosystemCatalogEntry('plugin', entryId, { ...ecosystemPaths, scope });
284
- ctx.print(result.ok
285
- ? `Uninstalled curated plugin ${entryId} from ${result.removedPath}`
286
- : `Error: ${result.error}`);
287
- return;
288
- }
289
- if (sub === 'enable') {
290
- const name = commandArgs[1];
291
- if (!name) { ctx.print('Usage: /plugin enable <name> --yes'); return; }
292
- if (!parsed.yes) {
293
- requireYesFlag(ctx, `enable plugin ${name}`, '/plugin enable <name> --yes');
294
- return;
295
- }
296
- const result = await pluginManager.enable(name);
297
- ctx.print(result.ok ? `Plugin '${name}' enabled and activated.` : `Error: ${result.error}`);
298
- return;
299
- }
300
- if (sub === 'disable') {
301
- const name = commandArgs[1];
302
- if (!name) { ctx.print('Usage: /plugin disable <name> --yes'); return; }
303
- if (!parsed.yes) {
304
- requireYesFlag(ctx, `disable plugin ${name}`, '/plugin disable <name> --yes');
305
- return;
306
- }
307
- const result = await pluginManager.disable(name);
308
- ctx.print(result.ok ? `Plugin '${name}' disabled.` : `Error: ${result.error}`);
309
- return;
310
- }
311
- if (sub === 'reload') {
312
- if (!parsed.yes) {
313
- requireYesFlag(ctx, 'reload plugins', '/plugin reload --yes');
314
- return;
315
- }
316
- ctx.print('Reloading plugins...');
317
- const { reloaded, failed } = await pluginManager.reload();
318
- ctx.print(`Done. ${reloaded} plugin(s) reloaded${failed > 0 ? `, ${failed} failed` : ''}.`);
319
- return;
320
- }
321
- if (sub === 'trust') {
322
- const name = commandArgs[1];
323
- const rawTier = commandArgs[2];
324
- if (!name || !rawTier) {
325
- ctx.print('Usage: /plugin trust <name> <untrusted|limited|trusted> [note] --yes');
326
- return;
327
- }
328
- if (!parsed.yes) {
329
- requireYesFlag(ctx, `set plugin ${name} trust tier`, '/plugin trust <name> <untrusted|limited|trusted> [note] --yes');
330
- return;
331
- }
332
- if (rawTier !== 'untrusted' && rawTier !== 'limited' && rawTier !== 'trusted') {
333
- ctx.print(`Error: Invalid trust tier '${rawTier}'. Must be: untrusted, limited, or trusted.`);
334
- return;
335
- }
336
- const tier = rawTier as 'untrusted' | 'limited' | 'trusted';
337
- const note = commandArgs.slice(3).join(' ') || undefined;
338
- if (tier === 'trusted') {
339
- const sigResult = pluginManager.trustSigned(name);
340
- if (sigResult.ok) {
341
- ctx.print(`Plugin '${name}' elevated to 'trusted' via signed manifest${sigResult.fingerprint ? ` (fingerprint: ${sigResult.fingerprint})` : ''}.\nReload the plugin to apply updated capability grants.`);
342
- return;
343
- }
344
- ctx.print(`Warning: Signature validation failed (${sigResult.error}).\nGranting 'trusted' tier by operator override. High-risk capabilities will be available on next reload.`);
345
- }
346
- const result = pluginManager.trust(name, tier, note);
347
- ctx.print(result.ok
348
- ? `Plugin '${name}' trust tier set to '${tier}'.${tier === 'trusted' ? '\nReload the plugin to apply high-risk capability grants.' : ''}`
349
- : `Error: ${result.error}`);
350
- return;
351
- }
352
- if (sub === 'verify') {
353
- const name = commandArgs[1];
354
- if (!name) { ctx.print('Usage: /plugin verify <name>'); return; }
355
- const result = pluginManager.verify(name);
356
- if (!result.ok && result.reason?.includes('not found')) {
357
- ctx.print(`Error: ${result.reason}`);
358
- return;
359
- }
360
- ctx.print(result.valid
361
- ? `Plugin '${name}' manifest signature is VALID.${result.fingerprint ? `\nFingerprint: ${result.fingerprint}` : ''}`
362
- : `Plugin '${name}' manifest signature is INVALID.\nReason: ${result.reason ?? 'Unknown'}`);
363
- return;
364
- }
365
- if (sub === 'capabilities') {
366
- const name = commandArgs[1];
367
- if (!name) { ctx.print('Usage: /plugin capabilities <name>'); return; }
368
- const info = pluginManager.capabilities(name);
369
- if (!info) {
370
- ctx.print(`Error: Plugin '${name}' not found.`);
371
- return;
372
- }
373
- const lines: string[] = [`Plugin: ${name}`, `Trust tier: ${info.tier}`, '', `Requested capabilities (${info.requested.length}):`];
374
- if (info.requested.length === 0) lines.push(' (none)');
375
- else {
376
- for (const cap of info.requested) {
377
- const tag = info.blocked.includes(cap)
378
- ? '[BLOCKED - requires trusted tier]'
379
- : info.highRisk.includes(cap) ? '[high-risk, granted]' : '[safe]';
380
- lines.push(` ${cap.padEnd(32)} ${tag}`);
381
- }
382
- }
383
- if (info.blocked.length > 0) {
384
- lines.push('');
385
- lines.push(`${info.blocked.length} high-risk capability/capabilities blocked by trust tier '${info.tier}'.`);
386
- lines.push(`Use /plugin trust ${name} trusted --yes to escalate.`);
387
- }
388
- ctx.print(lines.join('\n'));
389
- return;
390
- }
391
- if (sub === 'quarantine') {
392
- const name = commandArgs[1];
393
- const action = commandArgs[2] ?? 'add';
394
- if (!name) {
395
- ctx.print('Usage: /plugin quarantine <name> [add|lift] [reason] --yes');
396
- return;
397
- }
398
- if (!parsed.yes) {
399
- requireYesFlag(ctx, `${action === 'lift' ? 'lift quarantine for' : 'quarantine'} plugin ${name}`, '/plugin quarantine <name> [add|lift] [reason] --yes');
400
- return;
401
- }
402
- if (action === 'lift') {
403
- const result = pluginManager.liftQuarantine(name);
404
- ctx.print(result.ok ? `Plugin '${name}' quarantine lifted. Reload to restore safe capabilities.` : `Error: ${result.error}`);
405
- return;
406
- }
407
- const reason = (action === 'add' ? commandArgs.slice(3) : commandArgs.slice(2)).join(' ') || 'quarantined by operator';
408
- const result = pluginManager.quarantine(name, reason);
409
- ctx.print(result.ok
410
- ? `Plugin '${name}' quarantined.\nReason: ${reason}\nHigh-risk capabilities revoked. Reload to fully apply. Use /plugin quarantine <name> lift --yes to restore.`
411
- : `Error: ${result.error}`);
412
- return;
413
- }
414
-
415
- ctx.print(
416
- 'Usage: /plugin <subcommand>\n'
417
- + ' list — show installed plugins and their status\n'
418
- + ' enable <name> --yes — enable a plugin\n'
419
- + ' disable <name> --yes — disable a plugin\n'
420
- + ' reload --yes — reload all enabled plugins\n'
421
- + ' trust <name> <tier> [note] --yes — set trust tier (untrusted|limited|trusted)\n'
422
- + ' verify <name> — inspect a plugin manifest signature\n'
423
- + ' capabilities <name> — show capability grants and blocks\n'
424
- + ' browse [query] — browse curated local-first plugin catalog entries\n'
425
- + ' installed — list curated catalog installs with provenance receipts\n'
426
- + ' catalog-review <id> — review source, provenance, and risk for a curated plugin\n'
427
- + ' publish-local <id> <path> <summary...> --yes — publish a local plugin directory into the curated catalog\n'
428
- + ' unpublish <id> --yes — remove a local curated plugin catalog entry\n'
429
- + ' install-hint <catalog-id> — show install guidance for a curated plugin entry\n'
430
- + ' install <catalog-id> [scope] --yes — install a local-path curated plugin into project|user scope\n'
431
- + ' uninstall <catalog-id> [scope] --yes — remove a curated plugin install receipt and target path\n'
432
- + ' quarantine <name> [reason] --yes — quarantine a plugin (revoke high-risk caps)\n'
433
- + ' quarantine <name> lift --yes — lift quarantine from a plugin'
434
- );
435
- },
436
- });
437
- }