@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.
Files changed (68) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/package.json +1 -1
  3. package/src/cli/agent-knowledge-command.ts +30 -3
  4. package/src/cli/help.ts +2 -2
  5. package/src/input/commands/cloudflare-runtime.ts +20 -5
  6. package/src/input/commands/confirmation.ts +24 -0
  7. package/src/input/commands/discovery-runtime.ts +16 -7
  8. package/src/input/commands/eval.ts +27 -14
  9. package/src/input/commands/experience-runtime.ts +65 -26
  10. package/src/input/commands/health-runtime.ts +1 -1
  11. package/src/input/commands/hooks-runtime.ts +50 -19
  12. package/src/input/commands/incident-runtime.ts +17 -6
  13. package/src/input/commands/integration-runtime.ts +93 -50
  14. package/src/input/commands/knowledge.ts +38 -12
  15. package/src/input/commands/local-auth-runtime.ts +36 -13
  16. package/src/input/commands/local-provider-runtime.ts +22 -11
  17. package/src/input/commands/local-runtime.ts +21 -11
  18. package/src/input/commands/local-setup.ts +35 -16
  19. package/src/input/commands/managed-runtime.ts +51 -20
  20. package/src/input/commands/marketplace-runtime.ts +31 -16
  21. package/src/input/commands/mcp-runtime.ts +65 -34
  22. package/src/input/commands/memory-product-runtime.ts +72 -35
  23. package/src/input/commands/memory.ts +9 -9
  24. package/src/input/commands/notify-runtime.ts +27 -8
  25. package/src/input/commands/operator-runtime.ts +85 -17
  26. package/src/input/commands/planning-runtime.ts +14 -2
  27. package/src/input/commands/platform-access-runtime.ts +88 -45
  28. package/src/input/commands/platform-services-runtime.ts +51 -25
  29. package/src/input/commands/product-runtime.ts +54 -27
  30. package/src/input/commands/profile-sync-runtime.ts +17 -6
  31. package/src/input/commands/recall-bundle.ts +38 -17
  32. package/src/input/commands/recall-query.ts +15 -4
  33. package/src/input/commands/recall-review.ts +9 -3
  34. package/src/input/commands/remote-runtime-setup.ts +45 -18
  35. package/src/input/commands/remote-runtime.ts +25 -9
  36. package/src/input/commands/replay-runtime.ts +9 -2
  37. package/src/input/commands/services-runtime.ts +21 -10
  38. package/src/input/commands/session-content.ts +53 -51
  39. package/src/input/commands/session-workflow.ts +10 -4
  40. package/src/input/commands/session.ts +1 -1
  41. package/src/input/commands/settings-sync-runtime.ts +40 -17
  42. package/src/input/commands/share-runtime.ts +12 -4
  43. package/src/input/commands/shell-core.ts +3 -3
  44. package/src/input/commands/subscription-runtime.ts +35 -20
  45. package/src/input/commands/teleport-runtime.ts +16 -5
  46. package/src/input/commands/work-plan-runtime.ts +23 -12
  47. package/src/input/handler-content-actions.ts +11 -62
  48. package/src/input/handler-interactions.ts +1 -1
  49. package/src/input/handler-onboarding-cloudflare.ts +48 -117
  50. package/src/input/keybindings.ts +1 -1
  51. package/src/input/mcp-workspace.ts +25 -49
  52. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +8 -8
  53. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -6
  54. package/src/input/profile-picker-modal.ts +13 -31
  55. package/src/input/session-picker-modal.ts +4 -30
  56. package/src/input/settings-modal-subscriptions.ts +3 -3
  57. package/src/panels/incident-review-panel.ts +1 -1
  58. package/src/panels/local-auth-panel.ts +4 -4
  59. package/src/panels/provider-account-snapshot.ts +1 -1
  60. package/src/panels/provider-health-domains.ts +2 -2
  61. package/src/panels/settings-sync-panel.ts +2 -2
  62. package/src/panels/subscription-panel.ts +7 -7
  63. package/src/renderer/block-actions.ts +1 -1
  64. package/src/renderer/help-overlay.ts +2 -2
  65. package/src/renderer/mcp-workspace.ts +12 -12
  66. package/src/renderer/profile-picker-modal.ts +3 -11
  67. package/src/renderer/session-picker-modal.ts +2 -10
  68. 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 (args.length === 0 && ctx.openHooksPanel) {
37
+ if (commandArgs.length === 0 && ctx.openHooksPanel) {
35
38
  ctx.openHooksPanel();
36
39
  return;
37
40
  }
38
41
 
39
- const subcommand = (args[0] ?? 'contracts').toLowerCase();
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] = args.slice(1);
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 = args[1];
65
- const matches = args[2]?.split(',').map((entry) => entry.trim()).filter(Boolean) ?? [];
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 = args[1];
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 = args[1];
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 = args[1];
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
- const path = await workbench.export(args[1] ?? workbench.getFilePath());
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 = args[1];
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 = args[1];
140
- const strategy = args[2] === 'replace' ? 'replace' : 'merge';
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' ? args.slice(1) : args).join(' ').trim().toLowerCase();
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 = (args[0] ?? 'open').toLowerCase();
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 = args[1];
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 = args[2];
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 = args[0];
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 = args[1];
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 = args.slice(1).join(' ');
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 = args[1];
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 = args[1];
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 = args[1];
191
- const sourcePath = args[2];
192
- const summary = args.slice(3).join(' ').trim();
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 = args[1];
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 = args[1];
226
- const scopeArg = args[2];
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 = args[1];
240
- const scopeArg = args[2];
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 = args[1];
254
- const scopeArg = args[2];
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 = args[1];
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 = args[1];
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 = args[1];
288
- const rawTier = args[2];
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 = args.slice(3).join(' ') || undefined;
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 = args[1];
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 = args[1];
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 = args[1];
354
- const action = args[2] ?? 'add';
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 = args.slice(2).join(' ') || 'quarantined by operator';
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> — enable a plugin\n'
376
- + ' disable <name> — disable a plugin\n'
377
- + ' reload — reload all enabled plugins\n'
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> — remove a local curated plugin catalog entry\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'
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
  });