@pellux/goodvibes-agent 0.1.10 → 0.1.11
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 +27 -0
- package/package.json +1 -1
- package/src/cli/agent-knowledge-command.ts +30 -3
- package/src/cli/help.ts +2 -2
- package/src/input/commands/cloudflare-runtime.ts +20 -5
- package/src/input/commands/confirmation.ts +24 -0
- package/src/input/commands/discovery-runtime.ts +16 -7
- package/src/input/commands/eval.ts +27 -14
- package/src/input/commands/experience-runtime.ts +65 -26
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +50 -19
- package/src/input/commands/incident-runtime.ts +17 -6
- package/src/input/commands/integration-runtime.ts +93 -50
- package/src/input/commands/knowledge.ts +38 -12
- package/src/input/commands/local-auth-runtime.ts +36 -13
- package/src/input/commands/local-provider-runtime.ts +22 -11
- package/src/input/commands/local-runtime.ts +21 -11
- package/src/input/commands/local-setup.ts +35 -16
- package/src/input/commands/managed-runtime.ts +51 -20
- package/src/input/commands/marketplace-runtime.ts +31 -16
- package/src/input/commands/mcp-runtime.ts +65 -34
- package/src/input/commands/memory-product-runtime.ts +72 -35
- package/src/input/commands/memory.ts +9 -9
- package/src/input/commands/notify-runtime.ts +27 -8
- package/src/input/commands/operator-runtime.ts +85 -17
- package/src/input/commands/planning-runtime.ts +14 -2
- package/src/input/commands/platform-access-runtime.ts +88 -45
- package/src/input/commands/platform-services-runtime.ts +51 -25
- package/src/input/commands/product-runtime.ts +54 -27
- package/src/input/commands/profile-sync-runtime.ts +17 -6
- package/src/input/commands/recall-bundle.ts +38 -17
- package/src/input/commands/recall-query.ts +15 -4
- package/src/input/commands/recall-review.ts +9 -3
- package/src/input/commands/remote-runtime-setup.ts +45 -18
- package/src/input/commands/remote-runtime.ts +25 -9
- package/src/input/commands/replay-runtime.ts +9 -2
- package/src/input/commands/services-runtime.ts +21 -10
- package/src/input/commands/session-content.ts +53 -51
- package/src/input/commands/session-workflow.ts +10 -4
- package/src/input/commands/session.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +40 -17
- package/src/input/commands/share-runtime.ts +12 -4
- package/src/input/commands/shell-core.ts +3 -3
- package/src/input/commands/subscription-runtime.ts +35 -20
- package/src/input/commands/teleport-runtime.ts +16 -5
- package/src/input/commands/work-plan-runtime.ts +23 -12
- package/src/input/handler-content-actions.ts +11 -62
- package/src/input/handler-interactions.ts +1 -1
- package/src/input/handler-onboarding-cloudflare.ts +48 -117
- package/src/input/keybindings.ts +1 -1
- package/src/input/mcp-workspace.ts +25 -49
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +8 -8
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -6
- package/src/input/profile-picker-modal.ts +13 -31
- package/src/input/session-picker-modal.ts +4 -30
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/panels/incident-review-panel.ts +1 -1
- package/src/panels/local-auth-panel.ts +4 -4
- package/src/panels/provider-account-snapshot.ts +1 -1
- package/src/panels/provider-health-domains.ts +2 -2
- package/src/panels/settings-sync-panel.ts +2 -2
- package/src/panels/subscription-panel.ts +7 -7
- package/src/renderer/block-actions.ts +1 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/mcp-workspace.ts +12 -12
- package/src/renderer/profile-picker-modal.ts +3 -11
- package/src/renderer/session-picker-modal.ts +2 -10
- package/src/version.ts +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
3
3
|
import { requireHookApi } from './runtime-services.ts';
|
|
4
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
4
5
|
|
|
5
6
|
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
6
7
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -26,26 +27,36 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
26
27
|
name: 'hooks',
|
|
27
28
|
aliases: [],
|
|
28
29
|
description: 'Inspect, author, simulate, and reload managed hook workflows',
|
|
29
|
-
usage: '[contracts [filter] | reload | scaffold <name> <match> <type> | chain <name> <event1,event2,...> | remove <name> | enable <name> | disable <name> | simulate <eventPath> | inspect <path> | import <path> [merge|replace] | export [path]]',
|
|
30
|
+
usage: '[contracts [filter] | reload --yes | scaffold <name> <match> <type> --yes | chain <name> <event1,event2,...> --yes | remove <name> --yes | enable <name> --yes | disable <name> --yes | simulate <eventPath> | inspect <path> | import <path> [merge|replace] --yes | export [path] --yes]',
|
|
30
31
|
argsHint: '[subcommand]',
|
|
31
32
|
async handler(args, ctx) {
|
|
33
|
+
const parsed = stripYesFlag(args);
|
|
34
|
+
const commandArgs = [...parsed.rest];
|
|
32
35
|
const hookApi = requireHookApi(ctx);
|
|
33
36
|
const workbench = hookApi.workbench;
|
|
34
|
-
if (
|
|
37
|
+
if (commandArgs.length === 0 && ctx.openHooksPanel) {
|
|
35
38
|
ctx.openHooksPanel();
|
|
36
39
|
return;
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
const subcommand = (
|
|
42
|
+
const subcommand = (commandArgs[0] ?? 'contracts').toLowerCase();
|
|
40
43
|
if (subcommand === 'reload') {
|
|
44
|
+
if (!parsed.yes) {
|
|
45
|
+
requireYesFlag(ctx, 'reload managed hook workflows', '/hooks reload --yes');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
41
48
|
await workbench.reload();
|
|
42
49
|
ctx.print(`Reloaded managed hooks from ${workbench.getFilePath()}`);
|
|
43
50
|
return;
|
|
44
51
|
}
|
|
45
52
|
if (subcommand === 'scaffold') {
|
|
46
|
-
const [name, match, type] =
|
|
53
|
+
const [name, match, type] = commandArgs.slice(1);
|
|
47
54
|
if (!name || !match || !type) {
|
|
48
|
-
ctx.print('Usage: /hooks scaffold <name> <match> <command|prompt|http|ts>');
|
|
55
|
+
ctx.print('Usage: /hooks scaffold <name> <match> <command|prompt|http|ts> --yes');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (!parsed.yes) {
|
|
59
|
+
requireYesFlag(ctx, `scaffold managed hook ${name}`, '/hooks scaffold <name> <match> <command|prompt|http|ts> --yes');
|
|
49
60
|
return;
|
|
50
61
|
}
|
|
51
62
|
if (type === 'agent') {
|
|
@@ -61,10 +72,14 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
61
72
|
return;
|
|
62
73
|
}
|
|
63
74
|
if (subcommand === 'chain') {
|
|
64
|
-
const name =
|
|
65
|
-
const matches =
|
|
75
|
+
const name = commandArgs[1];
|
|
76
|
+
const matches = commandArgs[2]?.split(',').map((entry) => entry.trim()).filter(Boolean) ?? [];
|
|
66
77
|
if (!name || matches.length === 0) {
|
|
67
|
-
ctx.print('Usage: /hooks chain <name> <event1,event2,...>');
|
|
78
|
+
ctx.print('Usage: /hooks chain <name> <event1,event2,...> --yes');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
if (!parsed.yes) {
|
|
82
|
+
requireYesFlag(ctx, `scaffold managed hook chain ${name}`, '/hooks chain <name> <event1,event2,...> --yes');
|
|
68
83
|
return;
|
|
69
84
|
}
|
|
70
85
|
const chain = await workbench.scaffoldChain(name, matches);
|
|
@@ -72,9 +87,13 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
72
87
|
return;
|
|
73
88
|
}
|
|
74
89
|
if (subcommand === 'remove') {
|
|
75
|
-
const name =
|
|
90
|
+
const name = commandArgs[1];
|
|
76
91
|
if (!name) {
|
|
77
|
-
ctx.print('Usage: /hooks remove <name>');
|
|
92
|
+
ctx.print('Usage: /hooks remove <name> --yes');
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
if (!parsed.yes) {
|
|
96
|
+
requireYesFlag(ctx, `remove managed hook workflow ${name}`, '/hooks remove <name> --yes');
|
|
78
97
|
return;
|
|
79
98
|
}
|
|
80
99
|
const removed = await workbench.remove(name);
|
|
@@ -86,9 +105,13 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
86
105
|
return;
|
|
87
106
|
}
|
|
88
107
|
if (subcommand === 'enable' || subcommand === 'disable') {
|
|
89
|
-
const name =
|
|
108
|
+
const name = commandArgs[1];
|
|
90
109
|
if (!name) {
|
|
91
|
-
ctx.print(`Usage: /hooks ${subcommand} <name
|
|
110
|
+
ctx.print(`Usage: /hooks ${subcommand} <name> --yes`);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
if (!parsed.yes) {
|
|
114
|
+
requireYesFlag(ctx, `${subcommand} managed hook ${name}`, `/hooks ${subcommand} <name> --yes`);
|
|
92
115
|
return;
|
|
93
116
|
}
|
|
94
117
|
const changed = await workbench.toggle(name, subcommand === 'enable');
|
|
@@ -100,7 +123,7 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
100
123
|
return;
|
|
101
124
|
}
|
|
102
125
|
if (subcommand === 'simulate') {
|
|
103
|
-
const eventPath =
|
|
126
|
+
const eventPath = commandArgs[1];
|
|
104
127
|
if (!eventPath) {
|
|
105
128
|
ctx.print('Usage: /hooks simulate <eventPath>');
|
|
106
129
|
return;
|
|
@@ -116,12 +139,16 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
116
139
|
return;
|
|
117
140
|
}
|
|
118
141
|
if (subcommand === 'export') {
|
|
119
|
-
|
|
142
|
+
if (!parsed.yes) {
|
|
143
|
+
requireYesFlag(ctx, 'export managed hook workflows', '/hooks export [path] --yes');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const path = await workbench.export(commandArgs[1] ?? workbench.getFilePath());
|
|
120
147
|
ctx.print(`Exported managed hooks to ${path}`);
|
|
121
148
|
return;
|
|
122
149
|
}
|
|
123
150
|
if (subcommand === 'inspect') {
|
|
124
|
-
const path =
|
|
151
|
+
const path = commandArgs[1];
|
|
125
152
|
if (!path) {
|
|
126
153
|
ctx.print('Usage: /hooks inspect <path>');
|
|
127
154
|
return;
|
|
@@ -136,10 +163,14 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
136
163
|
return;
|
|
137
164
|
}
|
|
138
165
|
if (subcommand === 'import') {
|
|
139
|
-
const path =
|
|
140
|
-
const strategy =
|
|
166
|
+
const path = commandArgs[1];
|
|
167
|
+
const strategy = commandArgs[2] === 'replace' ? 'replace' : 'merge';
|
|
141
168
|
if (!path) {
|
|
142
|
-
ctx.print('Usage: /hooks import <path> [merge|replace]');
|
|
169
|
+
ctx.print('Usage: /hooks import <path> [merge|replace] --yes');
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
if (!parsed.yes) {
|
|
173
|
+
requireYesFlag(ctx, `import managed hook workflows from ${path}`, '/hooks import <path> [merge|replace] --yes');
|
|
143
174
|
return;
|
|
144
175
|
}
|
|
145
176
|
if (fileContainsAgentHookType(path)) {
|
|
@@ -151,7 +182,7 @@ export function registerHooksRuntimeCommands(registry: CommandRegistry): void {
|
|
|
151
182
|
return;
|
|
152
183
|
}
|
|
153
184
|
|
|
154
|
-
const filter = (subcommand === 'contracts' ?
|
|
185
|
+
const filter = (subcommand === 'contracts' ? commandArgs.slice(1) : commandArgs).join(' ').trim().toLowerCase();
|
|
155
186
|
const contracts = hookApi.contracts(filter);
|
|
156
187
|
|
|
157
188
|
if (contracts.length === 0) {
|
|
@@ -4,16 +4,19 @@ import type { CommandRegistry } from '../command-registry.ts';
|
|
|
4
4
|
import { buildIncidentMemoryAddOptions } from '@pellux/goodvibes-sdk/platform/state';
|
|
5
5
|
import { requireShellPaths } from './runtime-services.ts';
|
|
6
6
|
import { getMemoryApi } from './recall-query.ts';
|
|
7
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
7
8
|
|
|
8
9
|
export function registerIncidentRuntimeCommands(registry: CommandRegistry): void {
|
|
9
10
|
registry.register({
|
|
10
11
|
name: 'incident',
|
|
11
12
|
aliases: [],
|
|
12
13
|
description: 'Open, export, and capture incident review bundles',
|
|
13
|
-
usage: '[open | latest | show <id|latest> | export <id|latest> <path> | capture <id|latest>]',
|
|
14
|
+
usage: '[open | latest | show <id|latest> | export <id|latest> <path> --yes | capture <id|latest> --yes]',
|
|
14
15
|
async handler(args, ctx) {
|
|
16
|
+
const parsed = stripYesFlag(args);
|
|
17
|
+
const commandArgs = [...parsed.rest];
|
|
15
18
|
const shellPaths = requireShellPaths(ctx);
|
|
16
|
-
const subcommand = (
|
|
19
|
+
const subcommand = (commandArgs[0] ?? 'open').toLowerCase();
|
|
17
20
|
const forensicRegistry = ctx.extensions.forensicsRegistry;
|
|
18
21
|
if (subcommand === 'open') {
|
|
19
22
|
if (ctx.openIncidentPanel) {
|
|
@@ -27,7 +30,7 @@ export function registerIncidentRuntimeCommands(registry: CommandRegistry): void
|
|
|
27
30
|
ctx.print('Forensics registry is not available in this runtime.');
|
|
28
31
|
return;
|
|
29
32
|
}
|
|
30
|
-
const requestedId =
|
|
33
|
+
const requestedId = commandArgs[1];
|
|
31
34
|
const report = !requestedId || requestedId === 'latest'
|
|
32
35
|
? forensicRegistry.latest()
|
|
33
36
|
: forensicRegistry.getById(requestedId);
|
|
@@ -53,9 +56,13 @@ export function registerIncidentRuntimeCommands(registry: CommandRegistry): void
|
|
|
53
56
|
return;
|
|
54
57
|
}
|
|
55
58
|
if (subcommand === 'export') {
|
|
56
|
-
const pathArg =
|
|
59
|
+
const pathArg = commandArgs[2];
|
|
57
60
|
if (!requestedId || !pathArg) {
|
|
58
|
-
ctx.print('Usage: /incident export <id|latest> <path>');
|
|
61
|
+
ctx.print('Usage: /incident export <id|latest> <path> --yes');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (!parsed.yes) {
|
|
65
|
+
requireYesFlag(ctx, `export incident bundle ${requestedId}`, '/incident export <id|latest> <path> --yes');
|
|
59
66
|
return;
|
|
60
67
|
}
|
|
61
68
|
if (!report) {
|
|
@@ -74,6 +81,10 @@ export function registerIncidentRuntimeCommands(registry: CommandRegistry): void
|
|
|
74
81
|
return;
|
|
75
82
|
}
|
|
76
83
|
if (subcommand === 'capture') {
|
|
84
|
+
if (!parsed.yes) {
|
|
85
|
+
requireYesFlag(ctx, `capture incident ${requestedId ?? 'latest'} into durable memory`, '/incident capture <id|latest> --yes');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
77
88
|
const memory = getMemoryApi(ctx);
|
|
78
89
|
if (!memory) return;
|
|
79
90
|
if (!report) {
|
|
@@ -89,7 +100,7 @@ export function registerIncidentRuntimeCommands(registry: CommandRegistry): void
|
|
|
89
100
|
ctx.print(`Captured incident ${report.id} into durable memory as ${record.id}`);
|
|
90
101
|
return;
|
|
91
102
|
}
|
|
92
|
-
ctx.print('Usage: /incident [open | latest | show <id|latest> | export <id|latest> <path> | capture <id|latest>]');
|
|
103
|
+
ctx.print('Usage: /incident [open | latest | show <id|latest> | export <id|latest> <path> --yes | capture <id|latest> --yes]');
|
|
93
104
|
},
|
|
94
105
|
});
|
|
95
106
|
}
|
|
@@ -14,15 +14,18 @@ import {
|
|
|
14
14
|
uninstallEcosystemCatalogEntry,
|
|
15
15
|
} from '@/runtime/index.ts';
|
|
16
16
|
import { requireEcosystemCatalogPaths, requirePluginPathOptions } from './runtime-services.ts';
|
|
17
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
17
18
|
|
|
18
19
|
export function registerIntegrationRuntimeCommands(registry: CommandRegistry): void {
|
|
19
20
|
registry.register({
|
|
20
21
|
name: 'plugin',
|
|
21
22
|
aliases: [],
|
|
22
23
|
description: 'Manage plugins, trust, review, and ecosystem paths',
|
|
23
|
-
usage: 'list | dirs | inspect <name> | review | installed | catalog-review <id> | publish-local <id> <path> <summary...> | unpublish <id> | install <id> [project|user] | update <id> [project|user] | uninstall <id> [project|user] | enable <name> | disable <name> | reload',
|
|
24
|
-
argsHint: 'list | dirs | inspect | review | installed | catalog-review | publish-local | unpublish | install | update | uninstall | enable | disable | reload',
|
|
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',
|
|
25
26
|
async handler(args, ctx) {
|
|
27
|
+
const parsed = stripYesFlag(args);
|
|
28
|
+
const commandArgs = [...parsed.rest];
|
|
26
29
|
const pluginManager = ctx.extensions.pluginManager;
|
|
27
30
|
const ecosystemPaths = requireEcosystemCatalogPaths(ctx);
|
|
28
31
|
const pluginPaths = requirePluginPathOptions(ctx);
|
|
@@ -30,7 +33,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
30
33
|
ctx.print('Plugin manager is not available in this runtime.');
|
|
31
34
|
return;
|
|
32
35
|
}
|
|
33
|
-
const sub =
|
|
36
|
+
const sub = commandArgs[0];
|
|
34
37
|
|
|
35
38
|
if (!sub || sub === 'open' || sub === 'panel') {
|
|
36
39
|
if (ctx.showPanel) ctx.showPanel('plugins');
|
|
@@ -55,7 +58,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
55
58
|
if (p.author) lines.push(` by ${p.author}`);
|
|
56
59
|
}
|
|
57
60
|
lines.push('');
|
|
58
|
-
lines.push('Use /plugin enable <name> or /plugin disable <name> to toggle plugins.');
|
|
61
|
+
lines.push('Use /plugin enable <name> --yes or /plugin disable <name> --yes to toggle plugins.');
|
|
59
62
|
ctx.print(lines.join('\n'));
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
@@ -70,7 +73,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
70
73
|
return;
|
|
71
74
|
}
|
|
72
75
|
if (sub === 'inspect') {
|
|
73
|
-
const name =
|
|
76
|
+
const name = commandArgs[1];
|
|
74
77
|
if (!name) {
|
|
75
78
|
ctx.print('Usage: /plugin inspect <name>');
|
|
76
79
|
return;
|
|
@@ -111,7 +114,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
111
114
|
return;
|
|
112
115
|
}
|
|
113
116
|
if (sub === 'browse' || sub === 'catalog') {
|
|
114
|
-
const query =
|
|
117
|
+
const query = commandArgs.slice(1).join(' ');
|
|
115
118
|
const entries = query
|
|
116
119
|
? searchEcosystemCatalog('plugin', query, ecosystemPaths)
|
|
117
120
|
: loadEcosystemCatalog('plugin', ecosystemPaths);
|
|
@@ -140,7 +143,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
140
143
|
return;
|
|
141
144
|
}
|
|
142
145
|
if (sub === 'catalog-review') {
|
|
143
|
-
const entryId =
|
|
146
|
+
const entryId = commandArgs[1];
|
|
144
147
|
if (!entryId) {
|
|
145
148
|
ctx.print('Usage: /plugin catalog-review <catalog-id>');
|
|
146
149
|
return;
|
|
@@ -166,7 +169,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
166
169
|
return;
|
|
167
170
|
}
|
|
168
171
|
if (sub === 'install-hint') {
|
|
169
|
-
const entryId =
|
|
172
|
+
const entryId = commandArgs[1];
|
|
170
173
|
if (!entryId) {
|
|
171
174
|
ctx.print('Usage: /plugin install-hint <catalog-id>');
|
|
172
175
|
return;
|
|
@@ -182,16 +185,20 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
182
185
|
` source: ${entry.source}`,
|
|
183
186
|
` tags: ${entry.tags.join(', ') || '(none)'}`,
|
|
184
187
|
` trust notes: ${entry.trustNotes ?? '(none)'}`,
|
|
185
|
-
` install hint: ${entry.installHint ?? 'Place the plugin under a configured plugin search directory and use /plugin reload.'}`,
|
|
188
|
+
` install hint: ${entry.installHint ?? 'Place the plugin under a configured plugin search directory and use /plugin reload --yes.'}`,
|
|
186
189
|
].join('\n'));
|
|
187
190
|
return;
|
|
188
191
|
}
|
|
189
192
|
if (sub === 'publish-local') {
|
|
190
|
-
const entryId =
|
|
191
|
-
const sourcePath =
|
|
192
|
-
const summary =
|
|
193
|
+
const entryId = commandArgs[1];
|
|
194
|
+
const sourcePath = commandArgs[2];
|
|
195
|
+
const summary = commandArgs.slice(3).join(' ').trim();
|
|
193
196
|
if (!entryId || !sourcePath || !summary) {
|
|
194
|
-
ctx.print('Usage: /plugin publish-local <catalog-id> <path> <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');
|
|
195
202
|
return;
|
|
196
203
|
}
|
|
197
204
|
const result = upsertEcosystemCatalogEntry({
|
|
@@ -210,9 +217,13 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
210
217
|
return;
|
|
211
218
|
}
|
|
212
219
|
if (sub === 'unpublish') {
|
|
213
|
-
const entryId =
|
|
220
|
+
const entryId = commandArgs[1];
|
|
214
221
|
if (!entryId) {
|
|
215
|
-
ctx.print('Usage: /plugin unpublish <catalog-id>');
|
|
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');
|
|
216
227
|
return;
|
|
217
228
|
}
|
|
218
229
|
const result = removeEcosystemCatalogEntry('plugin', entryId, ecosystemPaths);
|
|
@@ -222,10 +233,14 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
222
233
|
return;
|
|
223
234
|
}
|
|
224
235
|
if (sub === 'install') {
|
|
225
|
-
const entryId =
|
|
226
|
-
const scopeArg =
|
|
236
|
+
const entryId = commandArgs[1];
|
|
237
|
+
const scopeArg = commandArgs[2];
|
|
227
238
|
if (!entryId) {
|
|
228
|
-
ctx.print('Usage: /plugin install <catalog-id> [project|user]');
|
|
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');
|
|
229
244
|
return;
|
|
230
245
|
}
|
|
231
246
|
const scope = scopeArg === 'user' ? 'user' : 'project';
|
|
@@ -236,10 +251,14 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
236
251
|
return;
|
|
237
252
|
}
|
|
238
253
|
if (sub === 'update') {
|
|
239
|
-
const entryId =
|
|
240
|
-
const scopeArg =
|
|
254
|
+
const entryId = commandArgs[1];
|
|
255
|
+
const scopeArg = commandArgs[2];
|
|
241
256
|
if (!entryId) {
|
|
242
|
-
ctx.print('Usage: /plugin update <catalog-id> [project|user]');
|
|
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');
|
|
243
262
|
return;
|
|
244
263
|
}
|
|
245
264
|
const scope = scopeArg === 'user' ? 'user' : 'project';
|
|
@@ -250,10 +269,14 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
250
269
|
return;
|
|
251
270
|
}
|
|
252
271
|
if (sub === 'uninstall') {
|
|
253
|
-
const entryId =
|
|
254
|
-
const scopeArg =
|
|
272
|
+
const entryId = commandArgs[1];
|
|
273
|
+
const scopeArg = commandArgs[2];
|
|
255
274
|
if (!entryId) {
|
|
256
|
-
ctx.print('Usage: /plugin uninstall <catalog-id> [project|user]');
|
|
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');
|
|
257
280
|
return;
|
|
258
281
|
}
|
|
259
282
|
const scope = scopeArg === 'user' ? 'user' : 'project';
|
|
@@ -264,30 +287,46 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
264
287
|
return;
|
|
265
288
|
}
|
|
266
289
|
if (sub === 'enable') {
|
|
267
|
-
const name =
|
|
268
|
-
if (!name) { ctx.print('Usage: /plugin enable <name>'); return; }
|
|
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
|
+
}
|
|
269
296
|
const result = await pluginManager.enable(name);
|
|
270
297
|
ctx.print(result.ok ? `Plugin '${name}' enabled and activated.` : `Error: ${result.error}`);
|
|
271
298
|
return;
|
|
272
299
|
}
|
|
273
300
|
if (sub === 'disable') {
|
|
274
|
-
const name =
|
|
275
|
-
if (!name) { ctx.print('Usage: /plugin disable <name>'); return; }
|
|
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
|
+
}
|
|
276
307
|
const result = await pluginManager.disable(name);
|
|
277
308
|
ctx.print(result.ok ? `Plugin '${name}' disabled.` : `Error: ${result.error}`);
|
|
278
309
|
return;
|
|
279
310
|
}
|
|
280
311
|
if (sub === 'reload') {
|
|
312
|
+
if (!parsed.yes) {
|
|
313
|
+
requireYesFlag(ctx, 'reload plugins', '/plugin reload --yes');
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
281
316
|
ctx.print('Reloading plugins...');
|
|
282
317
|
const { reloaded, failed } = await pluginManager.reload();
|
|
283
318
|
ctx.print(`Done. ${reloaded} plugin(s) reloaded${failed > 0 ? `, ${failed} failed` : ''}.`);
|
|
284
319
|
return;
|
|
285
320
|
}
|
|
286
321
|
if (sub === 'trust') {
|
|
287
|
-
const name =
|
|
288
|
-
const rawTier =
|
|
322
|
+
const name = commandArgs[1];
|
|
323
|
+
const rawTier = commandArgs[2];
|
|
289
324
|
if (!name || !rawTier) {
|
|
290
|
-
ctx.print('Usage: /plugin trust <name> <untrusted|limited|trusted> [note]');
|
|
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');
|
|
291
330
|
return;
|
|
292
331
|
}
|
|
293
332
|
if (rawTier !== 'untrusted' && rawTier !== 'limited' && rawTier !== 'trusted') {
|
|
@@ -295,7 +334,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
295
334
|
return;
|
|
296
335
|
}
|
|
297
336
|
const tier = rawTier as 'untrusted' | 'limited' | 'trusted';
|
|
298
|
-
const note =
|
|
337
|
+
const note = commandArgs.slice(3).join(' ') || undefined;
|
|
299
338
|
if (tier === 'trusted') {
|
|
300
339
|
const sigResult = pluginManager.trustSigned(name);
|
|
301
340
|
if (sigResult.ok) {
|
|
@@ -311,7 +350,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
311
350
|
return;
|
|
312
351
|
}
|
|
313
352
|
if (sub === 'verify') {
|
|
314
|
-
const name =
|
|
353
|
+
const name = commandArgs[1];
|
|
315
354
|
if (!name) { ctx.print('Usage: /plugin verify <name>'); return; }
|
|
316
355
|
const result = pluginManager.verify(name);
|
|
317
356
|
if (!result.ok && result.reason?.includes('not found')) {
|
|
@@ -324,7 +363,7 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
324
363
|
return;
|
|
325
364
|
}
|
|
326
365
|
if (sub === 'capabilities') {
|
|
327
|
-
const name =
|
|
366
|
+
const name = commandArgs[1];
|
|
328
367
|
if (!name) { ctx.print('Usage: /plugin capabilities <name>'); return; }
|
|
329
368
|
const info = pluginManager.capabilities(name);
|
|
330
369
|
if (!info) {
|
|
@@ -344,16 +383,20 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
344
383
|
if (info.blocked.length > 0) {
|
|
345
384
|
lines.push('');
|
|
346
385
|
lines.push(`${info.blocked.length} high-risk capability/capabilities blocked by trust tier '${info.tier}'.`);
|
|
347
|
-
lines.push(`Use /plugin trust ${name} trusted to escalate.`);
|
|
386
|
+
lines.push(`Use /plugin trust ${name} trusted --yes to escalate.`);
|
|
348
387
|
}
|
|
349
388
|
ctx.print(lines.join('\n'));
|
|
350
389
|
return;
|
|
351
390
|
}
|
|
352
391
|
if (sub === 'quarantine') {
|
|
353
|
-
const name =
|
|
354
|
-
const action =
|
|
392
|
+
const name = commandArgs[1];
|
|
393
|
+
const action = commandArgs[2] ?? 'add';
|
|
355
394
|
if (!name) {
|
|
356
|
-
ctx.print('Usage: /plugin quarantine <name> [add|lift] [reason]');
|
|
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');
|
|
357
400
|
return;
|
|
358
401
|
}
|
|
359
402
|
if (action === 'lift') {
|
|
@@ -361,10 +404,10 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
361
404
|
ctx.print(result.ok ? `Plugin '${name}' quarantine lifted. Reload to restore safe capabilities.` : `Error: ${result.error}`);
|
|
362
405
|
return;
|
|
363
406
|
}
|
|
364
|
-
const reason =
|
|
407
|
+
const reason = (action === 'add' ? commandArgs.slice(3) : commandArgs.slice(2)).join(' ') || 'quarantined by operator';
|
|
365
408
|
const result = pluginManager.quarantine(name, reason);
|
|
366
409
|
ctx.print(result.ok
|
|
367
|
-
? `Plugin '${name}' quarantined.\nReason: ${reason}\nHigh-risk capabilities revoked. Reload to fully apply. Use /plugin quarantine <name> lift to restore.`
|
|
410
|
+
? `Plugin '${name}' quarantined.\nReason: ${reason}\nHigh-risk capabilities revoked. Reload to fully apply. Use /plugin quarantine <name> lift --yes to restore.`
|
|
368
411
|
: `Error: ${result.error}`);
|
|
369
412
|
return;
|
|
370
413
|
}
|
|
@@ -372,22 +415,22 @@ export function registerIntegrationRuntimeCommands(registry: CommandRegistry): v
|
|
|
372
415
|
ctx.print(
|
|
373
416
|
'Usage: /plugin <subcommand>\n'
|
|
374
417
|
+ ' list — show installed plugins and their status\n'
|
|
375
|
-
+ ' enable <name>
|
|
376
|
-
+ ' disable <name>
|
|
377
|
-
+ ' reload
|
|
378
|
-
+ ' trust <name> <tier> [note] — set trust tier (untrusted|limited|trusted)\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'
|
|
379
422
|
+ ' verify <name> — inspect a plugin manifest signature\n'
|
|
380
423
|
+ ' capabilities <name> — show capability grants and blocks\n'
|
|
381
424
|
+ ' browse [query] — browse curated local-first plugin catalog entries\n'
|
|
382
425
|
+ ' installed — list curated catalog installs with provenance receipts\n'
|
|
383
426
|
+ ' catalog-review <id> — review source, provenance, and risk for a curated plugin\n'
|
|
384
|
-
+ ' publish-local <id> <path> <summary...> — publish a local plugin directory into the curated catalog\n'
|
|
385
|
-
+ ' unpublish <id>
|
|
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'
|
|
386
429
|
+ ' install-hint <catalog-id> — show install guidance for a curated plugin entry\n'
|
|
387
|
-
+ ' install <catalog-id> [scope] — install a local-path curated plugin into project|user scope\n'
|
|
388
|
-
+ ' uninstall <catalog-id> [scope] — remove a curated plugin install receipt and target path\n'
|
|
389
|
-
+ ' quarantine <name> [reason] — quarantine a plugin (revoke high-risk caps)\n'
|
|
390
|
-
+ ' quarantine <name> lift — lift quarantine from a plugin'
|
|
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'
|
|
391
434
|
);
|
|
392
435
|
},
|
|
393
436
|
});
|