@pellux/goodvibes-agent 0.1.9 → 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 (97) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +1 -1
  3. package/docs/getting-started.md +1 -1
  4. package/docs/release-and-publishing.md +2 -2
  5. package/package.json +4 -1
  6. package/src/cli/agent-knowledge-command.ts +46 -20
  7. package/src/cli/help.ts +15 -2
  8. package/src/cli/management-commands.ts +3 -3
  9. package/src/cli/management.ts +7 -1
  10. package/src/cli/parser.ts +3 -0
  11. package/src/cli/service-posture.ts +6 -6
  12. package/src/cli/status.ts +9 -9
  13. package/src/cli/surface-command.ts +3 -3
  14. package/src/cli/types.ts +2 -0
  15. package/src/input/commands/cloudflare-runtime.ts +20 -5
  16. package/src/input/commands/confirmation.ts +24 -0
  17. package/src/input/commands/discovery-runtime.ts +16 -7
  18. package/src/input/commands/eval.ts +27 -14
  19. package/src/input/commands/experience-runtime.ts +66 -27
  20. package/src/input/commands/health-runtime.ts +1 -1
  21. package/src/input/commands/hooks-runtime.ts +79 -20
  22. package/src/input/commands/incident-runtime.ts +17 -6
  23. package/src/input/commands/integration-runtime.ts +93 -50
  24. package/src/input/commands/knowledge.ts +38 -12
  25. package/src/input/commands/local-auth-runtime.ts +36 -13
  26. package/src/input/commands/local-provider-runtime.ts +22 -11
  27. package/src/input/commands/local-runtime.ts +21 -11
  28. package/src/input/commands/local-setup.ts +35 -16
  29. package/src/input/commands/managed-runtime.ts +51 -20
  30. package/src/input/commands/marketplace-runtime.ts +31 -16
  31. package/src/input/commands/mcp-runtime.ts +65 -34
  32. package/src/input/commands/memory-product-runtime.ts +72 -35
  33. package/src/input/commands/memory.ts +9 -9
  34. package/src/input/commands/notify-runtime.ts +27 -8
  35. package/src/input/commands/operator-runtime.ts +85 -17
  36. package/src/input/commands/planning-runtime.ts +14 -2
  37. package/src/input/commands/platform-access-runtime.ts +88 -45
  38. package/src/input/commands/platform-services-runtime.ts +51 -25
  39. package/src/input/commands/product-runtime.ts +54 -27
  40. package/src/input/commands/profile-sync-runtime.ts +17 -6
  41. package/src/input/commands/recall-bundle.ts +38 -17
  42. package/src/input/commands/recall-query.ts +15 -4
  43. package/src/input/commands/recall-review.ts +9 -3
  44. package/src/input/commands/remote-runtime-setup.ts +45 -18
  45. package/src/input/commands/remote-runtime.ts +25 -9
  46. package/src/input/commands/replay-runtime.ts +9 -2
  47. package/src/input/commands/services-runtime.ts +21 -10
  48. package/src/input/commands/session-content.ts +53 -51
  49. package/src/input/commands/session-workflow.ts +10 -4
  50. package/src/input/commands/session.ts +1 -1
  51. package/src/input/commands/settings-sync-runtime.ts +40 -17
  52. package/src/input/commands/share-runtime.ts +12 -4
  53. package/src/input/commands/shell-core.ts +3 -3
  54. package/src/input/commands/subscription-runtime.ts +35 -20
  55. package/src/input/commands/teleport-runtime.ts +16 -5
  56. package/src/input/commands/work-plan-runtime.ts +23 -12
  57. package/src/input/handler-content-actions.ts +11 -62
  58. package/src/input/handler-interactions.ts +1 -1
  59. package/src/input/handler-onboarding-cloudflare.ts +48 -117
  60. package/src/input/handler.ts +1 -0
  61. package/src/input/keybindings.ts +1 -1
  62. package/src/input/mcp-workspace.ts +25 -49
  63. package/src/input/onboarding/onboarding-runtime-status.ts +8 -8
  64. package/src/input/onboarding/onboarding-wizard-apply.ts +13 -53
  65. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +12 -12
  66. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +2 -7
  67. package/src/input/onboarding/onboarding-wizard-constants.ts +7 -7
  68. package/src/input/onboarding/onboarding-wizard-external-surface-extra-specs.ts +4 -4
  69. package/src/input/onboarding/onboarding-wizard-steps.ts +13 -13
  70. package/src/input/profile-picker-modal.ts +13 -31
  71. package/src/input/session-picker-modal.ts +4 -30
  72. package/src/input/settings-modal-agent-policy.ts +18 -0
  73. package/src/input/settings-modal-subscriptions.ts +3 -3
  74. package/src/input/settings-modal-types.ts +17 -0
  75. package/src/input/settings-modal.ts +30 -29
  76. package/src/main.ts +3 -26
  77. package/src/panels/incident-review-panel.ts +1 -1
  78. package/src/panels/local-auth-panel.ts +4 -4
  79. package/src/panels/provider-account-snapshot.ts +1 -1
  80. package/src/panels/provider-health-domains.ts +2 -2
  81. package/src/panels/settings-sync-panel.ts +2 -2
  82. package/src/panels/subscription-panel.ts +7 -7
  83. package/src/renderer/block-actions.ts +1 -1
  84. package/src/renderer/help-overlay.ts +2 -2
  85. package/src/renderer/mcp-workspace.ts +12 -12
  86. package/src/renderer/process-modal.ts +17 -8
  87. package/src/renderer/profile-picker-modal.ts +3 -11
  88. package/src/renderer/session-picker-modal.ts +2 -10
  89. package/src/renderer/settings-modal.ts +12 -8
  90. package/src/renderer/ui-factory.ts +4 -32
  91. package/src/runtime/bootstrap-shell.ts +0 -13
  92. package/src/runtime/bootstrap.ts +0 -10
  93. package/src/runtime/onboarding/derivation.ts +6 -6
  94. package/src/verification/live-verifier.ts +148 -13
  95. package/src/version.ts +10 -3
  96. package/src/input/commands/quit-shared.ts +0 -162
  97. package/src/renderer/git-status.ts +0 -89
@@ -8,6 +8,7 @@ import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils';
8
8
  import { BUILTIN_SECRET_PROVIDER_SOURCES, describeSecretRef, isSecretRefInput, resolveSecretRef } from '@pellux/goodvibes-sdk/platform/config';
9
9
  import { openCommandPanel, requireBookmarkManager, requireProviderApi, requireSecretsManager } from './runtime-services.ts';
10
10
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
11
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
11
12
 
12
13
  function isGoodVibesSecretRefInput(value: string): boolean {
13
14
  const normalized = value.trim();
@@ -143,16 +144,17 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
143
144
  registry.register({
144
145
  name: 'secrets',
145
146
  description: 'Manage hierarchy-aware secrets, external secret refs, and secure/plaintext storage policy controls',
146
- usage: 'set <KEY> <value> [--user|--project] [--secure|--plaintext] | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext]',
147
+ usage: 'set <KEY> <value> [--user|--project] [--secure|--plaintext] --yes | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] --yes | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext] --yes',
147
148
  argsHint: '<set|link|get|test|providers|list|delete> [KEY]',
148
149
  async handler(args, ctx) {
149
150
  const mgr = requireSecretsManager(ctx);
150
- const [sub, ...rest] = args;
151
+ const parsed = stripYesFlag(args);
152
+ const [sub, ...rest] = parsed.rest;
151
153
  if (!sub || sub === 'list') {
152
154
  const records = await mgr.listDetailed();
153
155
  const storedRecords = records.filter((record) => record.source !== 'env');
154
156
  ctx.print(storedRecords.length === 0
155
- ? '[secrets] No secrets stored. Use: /secrets set <KEY> <value>'
157
+ ? '[secrets] No secrets stored. Use: /secrets set <KEY> <value> --yes'
156
158
  : [
157
159
  '[secrets] Stored keys:',
158
160
  ...storedRecords.map((record) => ` ${record.key} (${record.source}${record.refSource ? `, ref:${record.refSource}` : ''}${record.overriddenByEnv ? ', env override' : ''})`),
@@ -165,11 +167,11 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
165
167
  ...BUILTIN_SECRET_PROVIDER_SOURCES.map((source) => ` ${source}`),
166
168
  '',
167
169
  'Examples:',
168
- ' /secrets link OPENAI_API_KEY goodvibes://secrets/env/OPENAI_API_KEY',
169
- ' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/bitwarden?item=GoodVibes%20Slack&field=password&sessionEnv=BW_SESSION',
170
- ' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/vaultwarden?item=GoodVibes%20Slack&field=password&server=https%3A%2F%2Fvault.example.test',
171
- ' /secrets link STRIPE_TOKEN goodvibes://secrets/bws/00000000-0000-0000-0000-000000000000?field=value&accessTokenEnv=BWS_ACCESS_TOKEN',
172
- ' /secrets link OPENAI_API_KEY goodvibes://secrets/1password?vault=Private&item=GoodVibes%20OpenAI&field=API%20Key',
170
+ ' /secrets link OPENAI_API_KEY goodvibes://secrets/env/OPENAI_API_KEY --yes',
171
+ ' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/bitwarden?item=GoodVibes%20Slack&field=password&sessionEnv=BW_SESSION --yes',
172
+ ' /secrets link SLACK_BOT_TOKEN goodvibes://secrets/vaultwarden?item=GoodVibes%20Slack&field=password&server=https%3A%2F%2Fvault.example.test --yes',
173
+ ' /secrets link STRIPE_TOKEN goodvibes://secrets/bws/00000000-0000-0000-0000-000000000000?field=value&accessTokenEnv=BWS_ACCESS_TOKEN --yes',
174
+ ' /secrets link OPENAI_API_KEY goodvibes://secrets/1password?vault=Private&item=GoodVibes%20OpenAI&field=API%20Key --yes',
173
175
  ].join('\n'));
174
176
  return;
175
177
  }
@@ -196,10 +198,14 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
196
198
  const valueParts = rest.filter((value) => !value.startsWith('--'));
197
199
  const [key, ...rawValueParts] = valueParts;
198
200
  if (!key || valueParts.length === 0) {
199
- ctx.print(`[secrets] Usage: /secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext]`);
201
+ ctx.print(`[secrets] Usage: /secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext] --yes`);
200
202
  return;
201
203
  }
202
204
  const value = rawValueParts.join(' ');
205
+ if (!parsed.yes) {
206
+ requireYesFlag(ctx, `${sub === 'link' ? 'link secret reference for' : 'store secret value for'} ${key}`, `/secrets ${sub} <KEY> <${sub === 'link' ? 'secret-ref' : 'value'}> [--user|--project] [--secure|--plaintext] --yes`);
207
+ return;
208
+ }
203
209
  if (sub === 'link' && !isGoodVibesSecretRefInput(value)) {
204
210
  ctx.print('[secrets] Invalid secret reference. Use /secrets providers for examples.');
205
211
  return;
@@ -230,7 +236,11 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
230
236
  const flags = new Set(rest.filter((value) => value.startsWith('--')));
231
237
  const [key] = rest.filter((value) => !value.startsWith('--'));
232
238
  if (!key) {
233
- ctx.print('[secrets] Usage: /secrets delete <KEY> [--user|--project] [--secure|--plaintext]');
239
+ ctx.print('[secrets] Usage: /secrets delete <KEY> [--user|--project] [--secure|--plaintext] --yes');
240
+ return;
241
+ }
242
+ if (!parsed.yes) {
243
+ requireYesFlag(ctx, `delete secret ${key}`, '/secrets delete <KEY> [--user|--project] [--secure|--plaintext] --yes');
234
244
  return;
235
245
  }
236
246
  await mgr.delete(key, {
@@ -240,7 +250,7 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
240
250
  ctx.print(`[secrets] Deleted: ${key}`);
241
251
  return;
242
252
  }
243
- ctx.print('[secrets] Usage: /secrets set <KEY> <value> [--user|--project] [--secure|--plaintext] | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext]');
253
+ ctx.print('[secrets] Usage: /secrets set <KEY> <value> [--user|--project] [--secure|--plaintext] --yes | link <KEY> <secret-ref> [--user|--project] [--secure|--plaintext] --yes | get <KEY> | test <secret-ref> | providers | list | delete <KEY> [--user|--project] [--secure|--plaintext] --yes');
244
254
  },
245
255
  });
246
256
 
@@ -16,6 +16,7 @@ import { buildSetupReviewSnapshot, exportSetupSupportBundle } from './local-setu
16
16
  import { openOnboardingWizard, requirePanelManager, requireShellPaths } from './runtime-services.ts';
17
17
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
18
18
  import { GOODVIBES_AGENT_SURFACE_ROOT } from '../../config/surface.ts';
19
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
19
20
 
20
21
  type SetupSnapshot = Awaited<ReturnType<typeof buildSetupReviewSnapshot>>;
21
22
 
@@ -24,9 +25,11 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
24
25
  name: 'setup',
25
26
  aliases: ['startup'],
26
27
  description: 'Launch the onboarding wizard and review Agent startup readiness',
27
- usage: '[review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir>|export <path>|transfer <export|inspect|import> <path>|link <surface> [target]|open-link <uri>]',
28
+ usage: '[review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir> --yes|export <path> --yes|transfer <export|inspect|import> <path> [--yes]|link <surface> [target]|open-link <uri>]',
28
29
  async handler(args, ctx) {
29
- const sub = args[0] ?? 'review';
30
+ const parsed = stripYesFlag(args);
31
+ const commandArgs = [...parsed.rest];
32
+ const sub = commandArgs[0] ?? 'review';
30
33
  let shellPaths: ReturnType<typeof requireShellPaths> | null = null;
31
34
  let snapshotPromise: Promise<SetupSnapshot> | null = null;
32
35
  const getShellPaths = () => (shellPaths ??= requireShellPaths(ctx));
@@ -134,12 +137,16 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
134
137
  }
135
138
 
136
139
  if (sub === 'support-bundle') {
137
- const snapshot = await getSnapshot();
138
- const dirArg = args[1];
140
+ const dirArg = commandArgs[1];
139
141
  if (!dirArg) {
140
- ctx.print('Usage: /setup support-bundle <dir>');
142
+ ctx.print('Usage: /setup support-bundle <dir> --yes');
143
+ return;
144
+ }
145
+ if (!parsed.yes) {
146
+ requireYesFlag(ctx, `export setup support bundle to ${dirArg}`, '/setup support-bundle <dir> --yes');
141
147
  return;
142
148
  }
149
+ const snapshot = await getSnapshot();
143
150
  const targetDir = exportSetupSupportBundle(dirArg, snapshot, ctx);
144
151
  writeFileSync(join(targetDir, 'remote-summary.json'), JSON.stringify({
145
152
  runners: ctx.ops.remoteRuntime?.listContracts() ?? [],
@@ -155,12 +162,16 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
155
162
  }
156
163
 
157
164
  if (sub === 'export') {
158
- const snapshot = await getSnapshot();
159
- const pathArg = args[1];
165
+ const pathArg = commandArgs[1];
160
166
  if (!pathArg) {
161
- ctx.print('Usage: /setup export <path>');
167
+ ctx.print('Usage: /setup export <path> --yes');
162
168
  return;
163
169
  }
170
+ if (!parsed.yes) {
171
+ requireYesFlag(ctx, `export startup review to ${pathArg}`, '/setup export <path> --yes');
172
+ return;
173
+ }
174
+ const snapshot = await getSnapshot();
164
175
  const targetPath = getShellPaths().resolveWorkspacePath(pathArg);
165
176
  mkdirSync(dirname(targetPath), { recursive: true });
166
177
  writeFileSync(targetPath, JSON.stringify(snapshot, null, 2) + '\n', 'utf-8');
@@ -169,14 +180,18 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
169
180
  }
170
181
 
171
182
  if (sub === 'transfer') {
172
- const mode = args[1]?.toLowerCase();
173
- const pathArg = args[2];
183
+ const mode = commandArgs[1]?.toLowerCase();
184
+ const pathArg = commandArgs[2];
174
185
  if (!mode || !pathArg) {
175
- ctx.print('Usage: /setup transfer <export|inspect|import> <path>');
186
+ ctx.print('Usage: /setup transfer <export|inspect|import> <path> [--yes]');
176
187
  return;
177
188
  }
178
189
  const targetPath = getShellPaths().resolveWorkspacePath(pathArg);
179
190
  if (mode === 'export') {
191
+ if (!parsed.yes) {
192
+ requireYesFlag(ctx, `export setup transfer bundle to ${pathArg}`, '/setup transfer export <path> --yes');
193
+ return;
194
+ }
180
195
  const snapshot = await getSnapshot();
181
196
  const bundle = buildSetupTransferBundle(ctx, snapshot);
182
197
  ctx.print(`Exported setup transfer bundle to ${exportSetupTransferBundle(ctx, pathArg, bundle)}`);
@@ -192,6 +207,10 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
192
207
  return;
193
208
  }
194
209
  if (mode === 'import') {
210
+ if (!parsed.yes) {
211
+ requireYesFlag(ctx, `import setup transfer bundle from ${pathArg}`, '/setup transfer import <path> --yes');
212
+ return;
213
+ }
195
214
  try {
196
215
  const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as SetupTransferBundle;
197
216
  for (const entry of CONFIG_SCHEMA) {
@@ -220,13 +239,13 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
220
239
  }
221
240
  return;
222
241
  }
223
- ctx.print('Usage: /setup transfer <export|inspect|import> <path>');
242
+ ctx.print('Usage: /setup transfer <export|inspect|import> <path> [--yes]');
224
243
  return;
225
244
  }
226
245
 
227
246
  if (sub === 'link') {
228
- const surface = args[1];
229
- const target = args[2];
247
+ const surface = commandArgs[1];
248
+ const target = commandArgs[2];
230
249
  if (!surface) {
231
250
  ctx.print('Usage: /setup link <cockpit|security|remote|knowledge|incident|hooks|orchestration|tasks> [target]');
232
251
  return;
@@ -236,7 +255,7 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
236
255
  }
237
256
 
238
257
  if (sub === 'open-link') {
239
- const link = args[1];
258
+ const link = commandArgs[1];
240
259
  if (!link) {
241
260
  ctx.print('Usage: /setup open-link <goodvibes://...>');
242
261
  return;
@@ -276,7 +295,7 @@ export function registerLocalSetupCommands(registry: CommandRegistry): void {
276
295
  return;
277
296
  }
278
297
 
279
- ctx.print('Usage: /setup [review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir>|export <path>|transfer <export|inspect|import> <path>|link <surface> [target]|open-link <uri>]');
298
+ ctx.print('Usage: /setup [review|doctor|services|hooks|remote|sandbox|onboarding|support-bundle <dir> --yes|export <path> --yes|transfer <export|inspect|import> <path> [--yes]|link <surface> [target]|open-link <uri>]');
280
299
  },
281
300
  });
282
301
  }
@@ -20,6 +20,7 @@ import {
20
20
  } from '@/runtime/index.ts';
21
21
  import { requireProfileManager, requireShellPaths } from './runtime-services.ts';
22
22
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
23
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
23
24
 
24
25
  function buildConfigSnapshot(
25
26
  manager: { get: (key: ConfigKey) => unknown },
@@ -39,11 +40,13 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
39
40
  registry.register({
40
41
  name: 'managed',
41
42
  description: 'Export, inspect, and apply managed settings bundles',
42
- usage: '[review|staged|rollback-history|export <profile> <path>|inspect <path>|stage <path>|apply <path> [key ...]|apply-staged [key ...]|rollback <token>|lock <key> <source> <reason...>|unlock <key>]',
43
+ usage: '[review|staged|rollback-history|export <profile> <path> --yes|inspect <path>|stage <path> --yes|apply <path> [key ...] --yes|apply-staged [key ...] --yes|rollback <token> --yes|lock <key> <source> <reason...> --yes|unlock <key> --yes]',
43
44
  handler(args, ctx) {
45
+ const parsed = stripYesFlag(args);
46
+ const commandArgs = [...parsed.rest];
44
47
  const shellPaths = requireShellPaths(ctx);
45
48
  const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
46
- const sub = args[0] ?? 'review';
49
+ const sub = commandArgs[0] ?? 'review';
47
50
  const pm = requireProfileManager(ctx);
48
51
  if (sub === 'review') {
49
52
  const profiles = pm.list();
@@ -78,11 +81,15 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
78
81
  }
79
82
 
80
83
  if (sub === 'lock') {
81
- const key = args[1] as ConfigKey | undefined;
82
- const source = args[2];
83
- const reason = args.slice(3).join(' ').trim();
84
+ const key = commandArgs[1] as ConfigKey | undefined;
85
+ const source = commandArgs[2];
86
+ const reason = commandArgs.slice(3).join(' ').trim();
84
87
  if (!key || !source || !reason) {
85
- ctx.print('Usage: /managed lock <key> <source> <reason...>');
88
+ ctx.print('Usage: /managed lock <key> <source> <reason...> --yes');
89
+ return;
90
+ }
91
+ if (!parsed.yes) {
92
+ requireYesFlag(ctx, `lock managed setting ${key}`, '/managed lock <key> <source> <reason...> --yes');
86
93
  return;
87
94
  }
88
95
  setManagedSettingLock(key, source, reason, controlPlaneConfigDir);
@@ -91,9 +98,13 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
91
98
  }
92
99
 
93
100
  if (sub === 'unlock') {
94
- const key = args[1] as ConfigKey | undefined;
101
+ const key = commandArgs[1] as ConfigKey | undefined;
95
102
  if (!key) {
96
- ctx.print('Usage: /managed unlock <key>');
103
+ ctx.print('Usage: /managed unlock <key> --yes');
104
+ return;
105
+ }
106
+ if (!parsed.yes) {
107
+ requireYesFlag(ctx, `unlock managed setting ${key}`, '/managed unlock <key> --yes');
97
108
  return;
98
109
  }
99
110
  ctx.print(clearManagedSettingLock(key, controlPlaneConfigDir) ? `Managed lock cleared for ${key}.` : `No managed lock found for ${key}.`);
@@ -101,10 +112,14 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
101
112
  }
102
113
 
103
114
  if (sub === 'export') {
104
- const profileName = args[1];
105
- const pathArg = args[2];
115
+ const profileName = commandArgs[1];
116
+ const pathArg = commandArgs[2];
106
117
  if (!profileName || !pathArg) {
107
- ctx.print('Usage: /managed export <profile> <path>');
118
+ ctx.print('Usage: /managed export <profile> <path> --yes');
119
+ return;
120
+ }
121
+ if (!parsed.yes) {
122
+ requireYesFlag(ctx, `export managed settings profile ${profileName} to ${pathArg}`, '/managed export <profile> <path> --yes');
108
123
  return;
109
124
  }
110
125
  const loaded = pm.load(profileName);
@@ -129,12 +144,16 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
129
144
  }
130
145
 
131
146
  if (sub === 'apply-staged') {
132
- const requestedKeys = args.slice(1).filter((value): value is ConfigKey => CONFIG_KEYS.has(value as ConfigKey));
133
- const invalidKeys = args.slice(1).filter((value) => !CONFIG_KEYS.has(value as ConfigKey));
147
+ const requestedKeys = commandArgs.slice(1).filter((value): value is ConfigKey => CONFIG_KEYS.has(value as ConfigKey));
148
+ const invalidKeys = commandArgs.slice(1).filter((value) => !CONFIG_KEYS.has(value as ConfigKey));
134
149
  if (invalidKeys.length > 0) {
135
150
  ctx.print(`Unknown config key(s): ${invalidKeys.join(', ')}`);
136
151
  return;
137
152
  }
153
+ if (!parsed.yes) {
154
+ requireYesFlag(ctx, 'apply staged managed settings', '/managed apply-staged [key ...] --yes');
155
+ return;
156
+ }
138
157
  try {
139
158
  const result = applyStagedManagedBundle(ctx.platform.configManager, requestedKeys);
140
159
  ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
@@ -149,9 +168,13 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
149
168
  }
150
169
 
151
170
  if (sub === 'rollback') {
152
- const token = args[1];
171
+ const token = commandArgs[1];
153
172
  if (!token) {
154
- ctx.print('Usage: /managed rollback <token>');
173
+ ctx.print('Usage: /managed rollback <token> --yes');
174
+ return;
175
+ }
176
+ if (!parsed.yes) {
177
+ requireYesFlag(ctx, `rollback managed settings token ${token}`, '/managed rollback <token> --yes');
155
178
  return;
156
179
  }
157
180
  try {
@@ -167,9 +190,9 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
167
190
  return;
168
191
  }
169
192
 
170
- const pathArg = args[1];
193
+ const pathArg = commandArgs[1];
171
194
  if (!pathArg) {
172
- ctx.print(`Usage: /managed ${sub} <path>`);
195
+ ctx.print(`Usage: /managed ${sub} <path>${sub === 'stage' || sub === 'apply' ? ' --yes' : ''}`);
173
196
  return;
174
197
  }
175
198
  const sourcePath = shellPaths.resolveWorkspacePath(pathArg);
@@ -181,18 +204,26 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
181
204
  }
182
205
 
183
206
  if (sub === 'stage') {
207
+ if (!parsed.yes) {
208
+ requireYesFlag(ctx, `stage managed settings bundle from ${pathArg}`, '/managed stage <path> --yes');
209
+ return;
210
+ }
184
211
  const stage = stageManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath);
185
212
  ctx.print(`Managed settings bundle staged from ${sourcePath} (${stage.changeCount} changes, risk=${stage.risk}).`);
186
213
  return;
187
214
  }
188
215
 
189
216
  if (sub === 'apply') {
190
- const requestedKeys = args.slice(2).filter((value): value is ConfigKey => CONFIG_KEYS.has(value as ConfigKey));
191
- const invalidKeys = args.slice(2).filter((value) => !CONFIG_KEYS.has(value as ConfigKey));
217
+ const requestedKeys = commandArgs.slice(2).filter((value): value is ConfigKey => CONFIG_KEYS.has(value as ConfigKey));
218
+ const invalidKeys = commandArgs.slice(2).filter((value) => !CONFIG_KEYS.has(value as ConfigKey));
192
219
  if (invalidKeys.length > 0) {
193
220
  ctx.print(`Unknown config key(s): ${invalidKeys.join(', ')}`);
194
221
  return;
195
222
  }
223
+ if (!parsed.yes) {
224
+ requireYesFlag(ctx, `apply managed settings bundle from ${pathArg}`, '/managed apply <path> [key ...] --yes');
225
+ return;
226
+ }
196
227
  stageManagedSettingsBundle(ctx.platform.configManager, bundle, sourcePath);
197
228
  const result = applyStagedManagedBundle(ctx.platform.configManager, requestedKeys);
198
229
  ctx.session.runtime.model = String(ctx.platform.configManager.get('provider.model'));
@@ -203,7 +234,7 @@ export function registerManagedRuntimeCommands(registry: CommandRegistry): void
203
234
  }
204
235
 
205
236
  recordSettingsSyncFailure('managed', `unsupported subcommand: ${sub}`, controlPlaneConfigDir);
206
- ctx.print('Usage: /managed [review|staged|rollback-history|export <profile> <path>|inspect <path>|stage <path>|apply <path> [key ...]|apply-staged [key ...]|rollback <token>|lock <key> <source> <reason...>|unlock <key>]');
237
+ ctx.print('Usage: /managed [review|staged|rollback-history|export <profile> <path> --yes|inspect <path>|stage <path> --yes|apply <path> [key ...] --yes|apply-staged [key ...] --yes|rollback <token> --yes|lock <key> <source> <reason...> --yes|unlock <key> --yes]');
207
238
  },
208
239
  });
209
240
  }
@@ -20,6 +20,7 @@ import {
20
20
  type EcosystemEntryKind,
21
21
  } from '@/runtime/index.ts';
22
22
  import { openCommandPanel, requireEcosystemCatalogPaths, requireReadModels, requireShellPaths } from './runtime-services.ts';
23
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
23
24
 
24
25
  function resolveMarketplaceEntry(
25
26
  kind: EcosystemEntryKind,
@@ -39,11 +40,13 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
39
40
  name: 'marketplace',
40
41
  aliases: ['catalog'],
41
42
  description: 'Browse the unified plugin and skill marketplace',
42
- usage: '[open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user]|update <plugin|skill|hook-pack|policy-pack> <id> [project|user]|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId]|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user]|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user]|bundle inspect <path>|bundle import <path> [project|user]|installed]',
43
+ usage: '[open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|update <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId] --yes|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user] --yes|bundle inspect <path>|bundle import <path> [project|user] --yes|installed]',
43
44
  handler(args, ctx) {
45
+ const parsed = stripYesFlag(args);
46
+ const commandArgs = [...parsed.rest];
44
47
  const shellPaths = requireShellPaths(ctx);
45
48
  const ecosystemPaths = requireEcosystemCatalogPaths(ctx);
46
- const sub = args[0] ?? 'open';
49
+ const sub = commandArgs[0] ?? 'open';
47
50
  if (sub === 'open' || sub === 'panel') {
48
51
  openCommandPanel(ctx, 'marketplace');
49
52
  return;
@@ -71,7 +74,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
71
74
  return;
72
75
  }
73
76
  if (sub === 'browse') {
74
- const query = args.slice(1).join(' ');
77
+ const query = commandArgs.slice(1).join(' ');
75
78
  const pluginEntries = query ? searchEcosystemCatalog('plugin', query, ecosystemPaths) : loadEcosystemCatalog('plugin', ecosystemPaths);
76
79
  const skillEntries = query ? searchEcosystemCatalog('skill', query, ecosystemPaths) : loadEcosystemCatalog('skill', ecosystemPaths);
77
80
  const hookPackEntries = query ? searchEcosystemCatalog('hook-pack', query, ecosystemPaths) : loadEcosystemCatalog('hook-pack', ecosystemPaths);
@@ -113,14 +116,18 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
113
116
  return;
114
117
  }
115
118
  if (sub === 'bundle') {
116
- const mode = args[1];
117
- const target = args[2];
118
- const scope = args[3] === 'user' ? 'user' : 'project';
119
+ const mode = commandArgs[1];
120
+ const target = commandArgs[2];
121
+ const scope = commandArgs[3] === 'user' ? 'user' : 'project';
119
122
  if ((mode === 'export' || mode === 'inspect' || mode === 'import') && !target) {
120
- ctx.print(`Usage: /marketplace bundle ${mode} <path>${mode === 'export' || mode === 'import' ? ' [project|user]' : ''}`);
123
+ ctx.print(`Usage: /marketplace bundle ${mode} <path>${mode === 'export' || mode === 'import' ? ' [project|user] --yes' : ''}`);
121
124
  return;
122
125
  }
123
126
  if (mode === 'export') {
127
+ if (!parsed.yes) {
128
+ requireYesFlag(ctx, `export marketplace bundle to ${target}`, '/marketplace bundle export <path> [project|user] --yes');
129
+ return;
130
+ }
124
131
  const bundle = exportEcosystemCatalogBundle(scope, ecosystemPaths);
125
132
  const targetPath = shellPaths.resolveWorkspacePath(target!);
126
133
  mkdirSync(dirname(targetPath), { recursive: true });
@@ -141,6 +148,10 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
141
148
  return;
142
149
  }
143
150
  if (mode === 'import') {
151
+ if (!parsed.yes) {
152
+ requireYesFlag(ctx, `import marketplace bundle from ${target}`, '/marketplace bundle import <path> [project|user] --yes');
153
+ return;
154
+ }
144
155
  const bundle = JSON.parse(readFileSync(shellPaths.resolveWorkspacePath(target!), 'utf-8')) as EcosystemCatalogBundle;
145
156
  const result = importEcosystemCatalogBundle(bundle, { ...ecosystemPaths, scope });
146
157
  ctx.print([
@@ -150,14 +161,14 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
150
161
  ].join('\n'));
151
162
  return;
152
163
  }
153
- ctx.print('Usage: /marketplace bundle <export|inspect|import> <path> [project|user]');
164
+ ctx.print('Usage: /marketplace bundle <export|inspect|import> <path> [project|user] --yes');
154
165
  return;
155
166
  }
156
167
 
157
- const kind = args[1] as EcosystemEntryKind | undefined;
158
- const entryId = args[2];
168
+ const kind = commandArgs[1] as EcosystemEntryKind | undefined;
169
+ const entryId = commandArgs[2];
159
170
  if ((sub === 'review' || sub === 'provenance' || sub === 'install-hint' || sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'history' || sub === 'uninstall' || sub === 'receipt') && (!kind || !entryId || !['plugin', 'skill', 'hook-pack', 'policy-pack'].includes(kind))) {
160
- ctx.print(`Usage: /marketplace ${sub} <plugin|skill|hook-pack|policy-pack> <id>${sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'history' || sub === 'uninstall' || sub === 'receipt' ? ' [project|user]' : ''}`);
171
+ ctx.print(`Usage: /marketplace ${sub} <plugin|skill|hook-pack|policy-pack> <id>${sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'history' || sub === 'uninstall' || sub === 'receipt' ? ' [project|user]' : ''}${sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'uninstall' ? ' --yes' : ''}`);
161
172
  return;
162
173
  }
163
174
  if (sub === 'review') {
@@ -216,7 +227,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
216
227
  return;
217
228
  }
218
229
  if (sub === 'receipt') {
219
- const scope = args[3] === 'user' ? 'user' : 'project';
230
+ const scope = commandArgs[3] === 'user' ? 'user' : 'project';
220
231
  const result = inspectInstalledEcosystemEntry(kind!, entryId!, { ...ecosystemPaths, scope });
221
232
  if (!result.ok) {
222
233
  ctx.print(`Error: ${result.error}`);
@@ -236,7 +247,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
236
247
  return;
237
248
  }
238
249
  if (sub === 'history') {
239
- const scope = args[3] === 'user' ? 'user' : 'project';
250
+ const scope = commandArgs[3] === 'user' ? 'user' : 'project';
240
251
  const backups = listEcosystemInstallBackups(kind!, entryId!, { ...ecosystemPaths, scope });
241
252
  ctx.print(backups.length > 0
242
253
  ? [
@@ -247,7 +258,11 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
247
258
  return;
248
259
  }
249
260
  if (sub === 'install' || sub === 'update' || sub === 'rollback' || sub === 'uninstall') {
250
- const scope = args[3] === 'user' ? 'user' : 'project';
261
+ const scope = commandArgs[3] === 'user' ? 'user' : 'project';
262
+ if (!parsed.yes) {
263
+ requireYesFlag(ctx, `${sub} curated ${kind} ${entryId}`, `/marketplace ${sub} <plugin|skill|hook-pack|policy-pack> <id> [project|user]${sub === 'rollback' ? ' [backupId]' : ''} --yes`);
264
+ return;
265
+ }
251
266
  if (sub === 'install') {
252
267
  const result = installEcosystemCatalogEntry(kind!, entryId!, { ...ecosystemPaths, scope });
253
268
  if (!result.ok) {
@@ -267,7 +282,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
267
282
  return;
268
283
  }
269
284
  if (sub === 'rollback') {
270
- const backupId = args[4];
285
+ const backupId = commandArgs[4];
271
286
  const result = rollbackInstalledEcosystemEntry(kind!, entryId!, { ...ecosystemPaths, scope, backupId });
272
287
  if (!result.ok) {
273
288
  ctx.print(`Error: ${result.error}`);
@@ -284,7 +299,7 @@ export function registerMarketplaceRuntimeCommands(registry: CommandRegistry): v
284
299
  ctx.print(`Uninstalled curated ${kind} ${entryId} from ${result.removedPath}`);
285
300
  return;
286
301
  }
287
- ctx.print('Usage: /marketplace [open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user]|update <plugin|skill|hook-pack|policy-pack> <id> [project|user]|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId]|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user]|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user]|bundle inspect <path>|bundle import <path> [project|user]|installed]');
302
+ ctx.print('Usage: /marketplace [open|overview|recommend|browse [query]|review <plugin|skill|hook-pack|policy-pack> <id>|provenance <plugin|skill|hook-pack|policy-pack> <id>|install-hint <plugin|skill|hook-pack|policy-pack> <id>|install <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|update <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|rollback <plugin|skill|hook-pack|policy-pack> <id> [project|user] [backupId] --yes|history <plugin|skill|hook-pack|policy-pack> <id> [project|user]|uninstall <plugin|skill|hook-pack|policy-pack> <id> [project|user] --yes|receipt <plugin|skill|hook-pack|policy-pack> <id> [project|user]|bundle export <path> [project|user] --yes|bundle inspect <path>|bundle import <path> [project|user] --yes|installed]');
288
303
  },
289
304
  });
290
305
  }