@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
@@ -2,6 +2,7 @@ import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, resolve } from 'node:path';
3
3
  import type { CommandRegistry } from '../command-registry.ts';
4
4
  import { requireSecretsManager, requireShellPaths } from './runtime-services.ts';
5
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
5
6
 
6
7
  interface SecureStorageBundle {
7
8
  readonly version: 1;
@@ -58,11 +59,13 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
58
59
  registry.register({
59
60
  name: 'storage',
60
61
  description: 'Review secure storage posture and export portable storage metadata bundles',
61
- usage: '[review|list|delete <key>|bundle export <path>|bundle inspect <path>]',
62
+ usage: '[review|list|delete <key> --yes|bundle export <path> --yes|bundle inspect <path>]',
62
63
  async handler(args, ctx) {
64
+ const parsed = stripYesFlag(args);
65
+ const commandArgs = [...parsed.rest];
63
66
  const shellPaths = requireShellPaths(ctx);
64
67
  const manager = requireSecretsManager(ctx);
65
- const sub = args[0] ?? 'review';
68
+ const sub = commandArgs[0] ?? 'review';
66
69
  const review = await manager.inspect();
67
70
  const storedKeys = await manager.list();
68
71
  const detailedKeys = await manager.listDetailed();
@@ -88,9 +91,13 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
88
91
  return;
89
92
  }
90
93
  if (sub === 'delete') {
91
- const key = args[1];
94
+ const key = commandArgs[1];
92
95
  if (!key) {
93
- ctx.print('Usage: /storage delete <key>');
96
+ ctx.print('Usage: /storage delete <key> --yes');
97
+ return;
98
+ }
99
+ if (!parsed.yes) {
100
+ requireYesFlag(ctx, `delete secure storage key ${key}`, '/storage delete <key> --yes');
94
101
  return;
95
102
  }
96
103
  await manager.delete(key);
@@ -98,14 +105,18 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
98
105
  return;
99
106
  }
100
107
  if (sub === 'bundle') {
101
- const mode = args[1];
102
- const pathArg = args[2];
108
+ const mode = commandArgs[1];
109
+ const pathArg = commandArgs[2];
103
110
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
104
- ctx.print(`Usage: /storage bundle ${mode} <path>`);
111
+ ctx.print(`Usage: /storage bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
105
112
  return;
106
113
  }
107
- const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
108
114
  if (mode === 'export') {
115
+ if (!parsed.yes) {
116
+ requireYesFlag(ctx, `export secure storage metadata bundle to ${pathArg}`, '/storage bundle export <path> --yes');
117
+ return;
118
+ }
119
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
109
120
  const bundle: SecureStorageBundle = {
110
121
  version: 1,
111
122
  exportedAt: Date.now(),
@@ -118,12 +129,13 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
118
129
  return;
119
130
  }
120
131
  if (mode === 'inspect') {
132
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
121
133
  const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as SecureStorageBundle;
122
134
  ctx.print(inspectStorageBundle(bundle));
123
135
  return;
124
136
  }
125
137
  }
126
- ctx.print('Usage: /storage [review|list|delete <key>|bundle export <path>|bundle inspect <path>]');
138
+ ctx.print('Usage: /storage [review|list|delete <key> --yes|bundle export <path> --yes|bundle inspect <path>]');
127
139
  },
128
140
  });
129
141
 
@@ -131,10 +143,12 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
131
143
  name: 'helpers',
132
144
  aliases: ['integration-api'],
133
145
  description: 'Review local integration helper API surfaces for remote clients and future web frontends',
134
- usage: '[review|bundle export <path>|bundle inspect <path>]',
146
+ usage: '[review|bundle export <path> --yes|bundle inspect <path>]',
135
147
  handler(args, ctx) {
148
+ const parsed = stripYesFlag(args);
149
+ const commandArgs = [...parsed.rest];
136
150
  const shellPaths = requireShellPaths(ctx);
137
- const sub = args[0] ?? 'review';
151
+ const sub = commandArgs[0] ?? 'review';
138
152
  const review = ctx.extensions.integrationHelpers?.buildReview();
139
153
  if (!review) {
140
154
  ctx.print('Integration helper service unavailable in this runtime.');
@@ -156,14 +170,18 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
156
170
  return;
157
171
  }
158
172
  if (sub === 'bundle') {
159
- const mode = args[1];
160
- const pathArg = args[2];
173
+ const mode = commandArgs[1];
174
+ const pathArg = commandArgs[2];
161
175
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
162
- ctx.print(`Usage: /helpers bundle ${mode} <path>`);
176
+ ctx.print(`Usage: /helpers bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
163
177
  return;
164
178
  }
165
- const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
166
179
  if (mode === 'export') {
180
+ if (!parsed.yes) {
181
+ requireYesFlag(ctx, `export integration helper bundle to ${pathArg}`, '/helpers bundle export <path> --yes');
182
+ return;
183
+ }
184
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
167
185
  const bundle: IntegrationHelperBundle = {
168
186
  version: 1,
169
187
  exportedAt: Date.now(),
@@ -176,12 +194,13 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
176
194
  return;
177
195
  }
178
196
  if (mode === 'inspect') {
197
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
179
198
  const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as IntegrationHelperBundle;
180
199
  ctx.print(inspectIntegrationHelperBundle(bundle));
181
200
  return;
182
201
  }
183
202
  }
184
- ctx.print('Usage: /helpers [review|bundle export <path>|bundle inspect <path>]');
203
+ ctx.print('Usage: /helpers [review|bundle export <path> --yes|bundle inspect <path>]');
185
204
  },
186
205
  });
187
206
 
@@ -189,10 +208,12 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
189
208
  name: 'deeplink',
190
209
  aliases: ['link'],
191
210
  description: 'Review and package deep-link entrypoints for setup and operator surfaces',
192
- usage: '[review|open <surface> [target]|bundle export <path>|bundle inspect <path>]',
211
+ usage: '[review|open <surface> [target]|bundle export <path> --yes|bundle inspect <path>]',
193
212
  handler(args, ctx) {
213
+ const parsed = stripYesFlag(args);
214
+ const commandArgs = [...parsed.rest];
194
215
  const shellPaths = requireShellPaths(ctx);
195
- const sub = args[0] ?? 'review';
216
+ const sub = commandArgs[0] ?? 'review';
196
217
  const links = [
197
218
  buildSetupLink('cockpit'),
198
219
  buildSetupLink('security'),
@@ -206,8 +227,8 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
206
227
  return;
207
228
  }
208
229
  if (sub === 'open') {
209
- const surface = args[1];
210
- const target = args[2];
230
+ const surface = commandArgs[1];
231
+ const target = commandArgs[2];
211
232
  if (!surface) {
212
233
  ctx.print('Usage: /deeplink open <surface> [target]');
213
234
  return;
@@ -216,14 +237,18 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
216
237
  return;
217
238
  }
218
239
  if (sub === 'bundle') {
219
- const mode = args[1];
220
- const pathArg = args[2];
240
+ const mode = commandArgs[1];
241
+ const pathArg = commandArgs[2];
221
242
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
222
- ctx.print(`Usage: /deeplink bundle ${mode} <path>`);
243
+ ctx.print(`Usage: /deeplink bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
223
244
  return;
224
245
  }
225
- const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
226
246
  if (mode === 'export') {
247
+ if (!parsed.yes) {
248
+ requireYesFlag(ctx, `export deep link bundle to ${pathArg}`, '/deeplink bundle export <path> --yes');
249
+ return;
250
+ }
251
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
227
252
  const bundle: DeepLinkBundle = {
228
253
  version: 1,
229
254
  exportedAt: Date.now(),
@@ -235,12 +260,13 @@ export function registerPlatformServicesRuntimeCommands(registry: CommandRegistr
235
260
  return;
236
261
  }
237
262
  if (mode === 'inspect') {
263
+ const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
238
264
  const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as DeepLinkBundle;
239
265
  ctx.print(inspectDeepLinkBundle(bundle));
240
266
  return;
241
267
  }
242
268
  }
243
- ctx.print('Usage: /deeplink [review|open <surface> [target]|bundle export <path>|bundle inspect <path>]');
269
+ ctx.print('Usage: /deeplink [review|open <surface> [target]|bundle export <path> --yes|bundle inspect <path>]');
244
270
  },
245
271
  });
246
272
  }
@@ -4,6 +4,7 @@ import type { CommandContext, CommandRegistry } from '../command-registry.ts';
4
4
  import { listInstalledEcosystemEntries, loadEcosystemCatalog } from '@/runtime/index.ts';
5
5
  import { BUILTIN_SUITES } from '@/runtime/index.ts';
6
6
  import { requireEcosystemCatalogPaths, requireReadModels, requireSecretsManager, requireServiceRegistry, requireShellPaths } from './runtime-services.ts';
7
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
7
8
 
8
9
  interface TrustReviewBundle {
9
10
  readonly version: 1;
@@ -155,23 +156,29 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
155
156
  registry.register({
156
157
  name: 'trust',
157
158
  description: 'Review trust posture and export portable trust bundles',
158
- usage: '[review|bundle export <path>|bundle inspect <path>]',
159
+ usage: '[review|bundle export <path> --yes|bundle inspect <path>]',
159
160
  async handler(args, ctx) {
161
+ const parsed = stripYesFlag(args);
162
+ const commandArgs = [...parsed.rest];
160
163
  const shellPaths = requireShellPaths(ctx);
161
- const sub = args[0] ?? 'review';
164
+ const sub = commandArgs[0] ?? 'review';
162
165
  if (sub === 'review') {
163
166
  const bundle = await buildTrustReviewBundle(ctx);
164
167
  ctx.print(formatTrustReview(bundle));
165
168
  return;
166
169
  }
167
170
  if (sub === 'bundle') {
168
- const mode = args[1];
169
- const pathArg = args[2];
171
+ const mode = commandArgs[1];
172
+ const pathArg = commandArgs[2];
170
173
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
171
- ctx.print(`Usage: /trust bundle ${mode} <path>`);
174
+ ctx.print(`Usage: /trust bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
172
175
  return;
173
176
  }
174
177
  if (mode === 'export') {
178
+ if (!parsed.yes) {
179
+ requireYesFlag(ctx, `export trust bundle to ${pathArg}`, '/trust bundle export <path> --yes');
180
+ return;
181
+ }
175
182
  const bundle = await buildTrustReviewBundle(ctx);
176
183
  const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
177
184
  mkdirSync(dirname(targetPath), { recursive: true });
@@ -184,21 +191,23 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
184
191
  return;
185
192
  }
186
193
  }
187
- ctx.print('Usage: /trust [review|bundle export <path>|bundle inspect <path>]');
194
+ ctx.print('Usage: /trust [review|bundle export <path> --yes|bundle inspect <path>]');
188
195
  },
189
196
  });
190
197
  registry.register({
191
198
  name: 'bridge',
192
199
  description: 'Review and operate self-hosted bridge and remote runner flows',
193
- usage: '[status|pools|assign <pool> <runner>|runner <id>|review <artifactId>|export <artifactId> [path]|import <path>]',
200
+ usage: '[status|pools|assign <pool> <runner> --yes|runner <id>|review <artifactId>|export <artifactId> [path] --yes|import <path> --yes]',
194
201
  async handler(args, ctx) {
202
+ const parsed = stripYesFlag(args);
203
+ const commandArgs = [...parsed.rest];
195
204
  const shellPaths = requireShellPaths(ctx);
196
205
  if (!ctx.ops.remoteRuntime) {
197
206
  ctx.print('Remote runner registry is not available in this runtime.');
198
207
  return;
199
208
  }
200
209
  const remoteRegistry = ctx.ops.remoteRuntime;
201
- const sub = args[0] ?? 'status';
210
+ const sub = commandArgs[0] ?? 'status';
202
211
  if (sub === 'status') {
203
212
  const remote = requireReadModels(ctx).remote.getSnapshot();
204
213
  ctx.print([
@@ -217,10 +226,14 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
217
226
  return;
218
227
  }
219
228
  if (sub === 'assign') {
220
- const poolId = args[1];
221
- const runnerId = args[2];
229
+ const poolId = commandArgs[1];
230
+ const runnerId = commandArgs[2];
222
231
  if (!poolId || !runnerId) {
223
- ctx.print('Usage: /bridge assign <pool> <runner>');
232
+ ctx.print('Usage: /bridge assign <pool> <runner> --yes');
233
+ return;
234
+ }
235
+ if (!parsed.yes) {
236
+ requireYesFlag(ctx, `assign bridge runner ${runnerId} to pool ${poolId}`, '/bridge assign <pool> <runner> --yes');
224
237
  return;
225
238
  }
226
239
  const pool = remoteRegistry.assignRunnerToPool(poolId, runnerId);
@@ -232,7 +245,7 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
232
245
  return;
233
246
  }
234
247
  if (sub === 'runner') {
235
- const runnerId = args[1];
248
+ const runnerId = commandArgs[1];
236
249
  if (!runnerId) {
237
250
  ctx.print('Usage: /bridge runner <id>');
238
251
  return;
@@ -253,7 +266,7 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
253
266
  return;
254
267
  }
255
268
  if (sub === 'review') {
256
- const artifactId = args[1];
269
+ const artifactId = commandArgs[1];
257
270
  if (!artifactId) {
258
271
  ctx.print('Usage: /bridge review <artifactId>');
259
272
  return;
@@ -263,14 +276,18 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
263
276
  return;
264
277
  }
265
278
  if (sub === 'export') {
266
- const artifactId = args[1];
279
+ const artifactId = commandArgs[1];
267
280
  if (!artifactId) {
268
- ctx.print('Usage: /bridge export <artifactId> [path]');
281
+ ctx.print('Usage: /bridge export <artifactId> [path] --yes');
282
+ return;
283
+ }
284
+ if (!parsed.yes) {
285
+ requireYesFlag(ctx, `export bridge artifact ${artifactId}`, '/bridge export <artifactId> [path] --yes');
269
286
  return;
270
287
  }
271
288
  const exported = await remoteRegistry.exportArtifact(
272
289
  artifactId,
273
- args[2] ? shellPaths.resolveWorkspacePath(args[2]) : undefined,
290
+ commandArgs[2] ? shellPaths.resolveWorkspacePath(commandArgs[2]) : undefined,
274
291
  );
275
292
  if (!exported) {
276
293
  ctx.print(`Unknown remote artifact: ${artifactId}`);
@@ -280,26 +297,32 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
280
297
  return;
281
298
  }
282
299
  if (sub === 'import') {
283
- const pathArg = args[1];
300
+ const pathArg = commandArgs[1];
284
301
  if (!pathArg) {
285
- ctx.print('Usage: /bridge import <path>');
302
+ ctx.print('Usage: /bridge import <path> --yes');
303
+ return;
304
+ }
305
+ if (!parsed.yes) {
306
+ requireYesFlag(ctx, `import bridge artifact from ${pathArg}`, '/bridge import <path> --yes');
286
307
  return;
287
308
  }
288
309
  const artifact = await remoteRegistry.importArtifact(shellPaths.resolveWorkspacePath(pathArg));
289
310
  ctx.print(`Imported remote bridge artifact ${artifact.id} for runner ${artifact.runnerId}.`);
290
311
  return;
291
312
  }
292
- ctx.print('Usage: /bridge [status|pools|assign <pool> <runner>|runner <id>|review <artifactId>|export <artifactId> [path]|import <path>]');
313
+ ctx.print('Usage: /bridge [status|pools|assign <pool> <runner> --yes|runner <id>|review <artifactId>|export <artifactId> [path] --yes|import <path> --yes]');
293
314
  },
294
315
  });
295
316
 
296
317
  registry.register({
297
318
  name: 'release',
298
319
  description: 'Package certification and release-readiness operations',
299
- usage: '[review|checklist|bundle export <path>|bundle inspect <path>]',
320
+ usage: '[review|checklist|bundle export <path> --yes|bundle inspect <path>]',
300
321
  handler(args, ctx) {
322
+ const parsed = stripYesFlag(args);
323
+ const commandArgs = [...parsed.rest];
301
324
  const shellPaths = requireShellPaths(ctx);
302
- const sub = args[0] ?? 'review';
325
+ const sub = commandArgs[0] ?? 'review';
303
326
  if (sub === 'review') {
304
327
  const bundle = buildReleaseBundle(ctx);
305
328
  ctx.print([
@@ -319,20 +342,24 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
319
342
  ' 1. Run /setup review and /setup doctor',
320
343
  ' 2. Run /security review and /trust review',
321
344
  ' 3. Run /policy preflight and /policy simulate',
322
- ' 4. Run /eval gate <suite> for required certification suites',
345
+ ' 4. Run /eval gate <suite> --yes for required certification suites',
323
346
  ' 5. Review /incident latest and /bridge status',
324
- ' 6. Export /release bundle export <path> for release evidence',
347
+ ' 6. Export /release bundle export <path> --yes for release evidence',
325
348
  ].join('\n'));
326
349
  return;
327
350
  }
328
351
  if (sub === 'bundle') {
329
- const mode = args[1];
330
- const pathArg = args[2];
352
+ const mode = commandArgs[1];
353
+ const pathArg = commandArgs[2];
331
354
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
332
- ctx.print(`Usage: /release bundle ${mode} <path>`);
355
+ ctx.print(`Usage: /release bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
333
356
  return;
334
357
  }
335
358
  if (mode === 'export') {
359
+ if (!parsed.yes) {
360
+ requireYesFlag(ctx, `export release bundle to ${pathArg}`, '/release bundle export <path> --yes');
361
+ return;
362
+ }
336
363
  const bundle = buildReleaseBundle(ctx);
337
364
  const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
338
365
  mkdirSync(dirname(targetPath), { recursive: true });
@@ -345,7 +372,7 @@ export function registerProductRuntimeCommands(registry: CommandRegistry): void
345
372
  return;
346
373
  }
347
374
  }
348
- ctx.print('Usage: /release [review|checklist|bundle export <path>|bundle inspect <path>]');
375
+ ctx.print('Usage: /release [review|checklist|bundle export <path> --yes|bundle inspect <path>]');
349
376
  },
350
377
  });
351
378
  }
@@ -4,6 +4,7 @@ import type { CommandRegistry } from '../command-registry.ts';
4
4
  import type { ProfileBundleEntry, ProfileSyncBundle } from '@/runtime/index.ts';
5
5
  import { recordSettingsSyncEvent, recordSettingsSyncFailure } from '@/runtime/index.ts';
6
6
  import { requireProfileManager, requireShellPaths } from './runtime-services.ts';
7
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
7
8
 
8
9
  function inspectProfileSyncBundle(bundle: ProfileSyncBundle): string {
9
10
  return [
@@ -18,11 +19,13 @@ export function registerProfileSyncRuntimeCommands(registry: CommandRegistry): v
18
19
  registry.register({
19
20
  name: 'profilesync',
20
21
  description: 'Export, import, and inspect profile sync bundles',
21
- usage: '[list|export <path>|inspect <path>|import <path> [prefix]]',
22
+ usage: '[list|export <path> --yes|inspect <path>|import <path> [prefix] --yes]',
22
23
  handler(args, ctx) {
24
+ const parsed = stripYesFlag(args);
25
+ const commandArgs = [...parsed.rest];
23
26
  const shellPaths = requireShellPaths(ctx);
24
27
  const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
25
- const sub = args[0] ?? 'list';
28
+ const sub = commandArgs[0] ?? 'list';
26
29
  const pm = requireProfileManager(ctx);
27
30
  if (sub === 'list') {
28
31
  const profiles = pm.list();
@@ -34,14 +37,18 @@ export function registerProfileSyncRuntimeCommands(registry: CommandRegistry): v
34
37
  return;
35
38
  }
36
39
 
37
- const pathArg = args[1];
40
+ const pathArg = commandArgs[1];
38
41
  if (!pathArg) {
39
- ctx.print(`Usage: /profilesync ${sub} <path>${sub === 'import' ? ' [prefix]' : ''}`);
42
+ ctx.print(`Usage: /profilesync ${sub} <path>${sub === 'import' ? ' [prefix]' : ''}${sub === 'export' || sub === 'import' ? ' --yes' : ''}`);
40
43
  return;
41
44
  }
42
45
  const targetPath = shellPaths.resolveWorkspacePath(pathArg);
43
46
 
44
47
  if (sub === 'export') {
48
+ if (!parsed.yes) {
49
+ requireYesFlag(ctx, `export profile sync bundle to ${pathArg}`, '/profilesync export <path> --yes');
50
+ return;
51
+ }
45
52
  const profiles = pm.list().map((profile) => {
46
53
  const loaded = pm.load(profile.name);
47
54
  return {
@@ -75,8 +82,12 @@ export function registerProfileSyncRuntimeCommands(registry: CommandRegistry): v
75
82
  }
76
83
 
77
84
  if (sub === 'import') {
85
+ if (!parsed.yes) {
86
+ requireYesFlag(ctx, `import profile sync bundle from ${pathArg}`, '/profilesync import <path> [prefix] --yes');
87
+ return;
88
+ }
78
89
  const bundle = JSON.parse(readFileSync(targetPath, 'utf-8')) as ProfileSyncBundle;
79
- const prefix = args[2]?.trim() ?? '';
90
+ const prefix = commandArgs[2]?.trim() ?? '';
80
91
  for (const entry of bundle.profiles) {
81
92
  const name = prefix ? `${prefix}-${entry.name}` : entry.name;
82
93
  pm.save(name, entry.data);
@@ -93,7 +104,7 @@ export function registerProfileSyncRuntimeCommands(registry: CommandRegistry): v
93
104
  }
94
105
 
95
106
  recordSettingsSyncFailure('profiles', `unsupported subcommand: ${sub}`, controlPlaneConfigDir);
96
- ctx.print('Usage: /profilesync [list|export <path>|inspect <path>|import <path> [prefix]]');
107
+ ctx.print('Usage: /profilesync [list|export <path> --yes|inspect <path>|import <path> [prefix] --yes]');
97
108
  },
98
109
  });
99
110
  }
@@ -6,23 +6,30 @@ import { VALID_CLASSES, VALID_SCOPES, isValidClass, isValidScope, resolveBundleP
6
6
  import { requireShellPaths } from './runtime-services.ts';
7
7
  import { getMemoryApi } from './recall-query.ts';
8
8
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
9
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
9
10
 
10
11
  export function handleRecallExport(args: string[], context: CommandContext): void {
12
+ const parsed = stripYesFlag(args);
13
+ const commandArgs = [...parsed.rest];
11
14
  const memory = getMemoryApi(context);
12
15
  if (!memory) {
13
16
  return;
14
17
  }
15
18
 
16
- const pathArg = args[0];
19
+ const pathArg = commandArgs[0];
17
20
  if (!pathArg) {
18
- context.print('[recall] Usage: /recall export <path> [--scope <scope>] [--cls <class>]');
21
+ context.print('[recall] Usage: /recall export <path> [--scope <scope>] [--cls <class>] --yes');
22
+ return;
23
+ }
24
+ if (!parsed.yes) {
25
+ requireYesFlag(context, `export durable memory bundle to ${pathArg}`, '/recall export <path> [--scope <scope>] [--cls <class>] --yes');
19
26
  return;
20
27
  }
21
28
 
22
29
  const filter: MemorySearchFilter = {};
23
- const scopeIdx = args.indexOf('--scope');
24
- if (scopeIdx !== -1 && args[scopeIdx + 1]) {
25
- const scope = args[scopeIdx + 1];
30
+ const scopeIdx = commandArgs.indexOf('--scope');
31
+ if (scopeIdx !== -1 && commandArgs[scopeIdx + 1]) {
32
+ const scope = commandArgs[scopeIdx + 1];
26
33
  if (!isValidScope(scope)) {
27
34
  context.print(`[recall] Unknown scope "${scope}". Valid: ${VALID_SCOPES.join(', ')}`);
28
35
  return;
@@ -30,9 +37,9 @@ export function handleRecallExport(args: string[], context: CommandContext): voi
30
37
  filter.scope = scope;
31
38
  }
32
39
 
33
- const clsIdx = args.indexOf('--cls');
34
- if (clsIdx !== -1 && args[clsIdx + 1]) {
35
- const cls = args[clsIdx + 1];
40
+ const clsIdx = commandArgs.indexOf('--cls');
41
+ if (clsIdx !== -1 && commandArgs[clsIdx + 1]) {
42
+ const cls = commandArgs[clsIdx + 1];
36
43
  if (!isValidClass(cls)) {
37
44
  context.print(`[recall] Unknown class "${cls}". Valid: ${VALID_CLASSES.join(', ')}`);
38
45
  return;
@@ -48,14 +55,20 @@ export function handleRecallExport(args: string[], context: CommandContext): voi
48
55
  }
49
56
 
50
57
  export async function handleRecallImport(args: string[], context: CommandContext): Promise<void> {
58
+ const parsed = stripYesFlag(args);
59
+ const commandArgs = [...parsed.rest];
51
60
  const memory = getMemoryApi(context);
52
61
  if (!memory) {
53
62
  return;
54
63
  }
55
64
 
56
- const pathArg = args[0];
65
+ const pathArg = commandArgs[0];
57
66
  if (!pathArg) {
58
- context.print('[recall] Usage: /recall import <path>');
67
+ context.print('[recall] Usage: /recall import <path> --yes');
68
+ return;
69
+ }
70
+ if (!parsed.yes) {
71
+ requireYesFlag(context, `import durable memory bundle from ${pathArg}`, '/recall import <path> --yes');
59
72
  return;
60
73
  }
61
74
 
@@ -85,17 +98,23 @@ function inspectBundle(bundle: MemoryBundle): string {
85
98
  }
86
99
 
87
100
  export function handleRecallHandoffExport(args: string[], context: CommandContext): void {
101
+ const parsed = stripYesFlag(args);
102
+ const commandArgs = [...parsed.rest];
88
103
  const memory = getMemoryApi(context);
89
104
  if (!memory) {
90
105
  return;
91
106
  }
92
- const pathArg = args[0];
107
+ const pathArg = commandArgs[0];
93
108
  if (!pathArg) {
94
- context.print('[recall] Usage: /recall handoff-export <path> [--scope <scope>]');
109
+ context.print('[recall] Usage: /recall handoff-export <path> [--scope <scope>] --yes');
95
110
  return;
96
111
  }
97
- const scopeIdx = args.indexOf('--scope');
98
- const scopeRaw = scopeIdx !== -1 ? args[scopeIdx + 1] : 'team';
112
+ if (!parsed.yes) {
113
+ requireYesFlag(context, `export memory handoff bundle to ${pathArg}`, '/recall handoff-export <path> [--scope <scope>] --yes');
114
+ return;
115
+ }
116
+ const scopeIdx = commandArgs.indexOf('--scope');
117
+ const scopeRaw = scopeIdx !== -1 ? commandArgs[scopeIdx + 1] : 'team';
99
118
  if (!scopeRaw || !isValidScope(scopeRaw)) {
100
119
  context.print(`[recall] Unknown scope "${scopeRaw ?? ''}". Valid: ${VALID_SCOPES.join(', ')}`);
101
120
  return;
@@ -123,10 +142,12 @@ export function handleRecallHandoffInspect(args: string[], context: CommandConte
123
142
  }
124
143
 
125
144
  export async function handleRecallHandoffImport(args: string[], context: CommandContext): Promise<void> {
126
- const pathArg = args[0];
145
+ const parsed = stripYesFlag(args);
146
+ const commandArgs = [...parsed.rest];
147
+ const pathArg = commandArgs[0];
127
148
  if (!pathArg) {
128
- context.print('[recall] Usage: /recall handoff-import <path>');
149
+ context.print('[recall] Usage: /recall handoff-import <path> --yes');
129
150
  return;
130
151
  }
131
- await handleRecallImport([pathArg], context);
152
+ await handleRecallImport([pathArg, ...(parsed.yes ? ['--yes'] : [])], context);
132
153
  }
@@ -2,6 +2,7 @@ import type { CommandContext } from '../command-registry.ts';
2
2
  import type { MemoryApi } from '@pellux/goodvibes-sdk/platform/knowledge';
3
3
  import type { MemorySearchFilter } from '@pellux/goodvibes-sdk/platform/state';
4
4
  import { VALID_CLASSES, VALID_SCOPES, isValidClass, isValidScope } from './recall-shared.ts';
5
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
5
6
 
6
7
  export function getMemoryApi(context: CommandContext): MemoryApi | null {
7
8
  const memoryApi = context.clients?.agentKnowledgeApi?.memory;
@@ -159,9 +160,14 @@ export async function handleRecallLink(args: string[], context: CommandContext):
159
160
  if (!memory) {
160
161
  return;
161
162
  }
162
- const [fromId, toId, relation] = args;
163
+ const parsed = stripYesFlag(args);
164
+ const [fromId, toId, relation] = parsed.rest;
163
165
  if (!fromId || !toId || !relation) {
164
- context.print('[recall] Usage: /recall link <fromId> <toId> <relation>');
166
+ context.print('[recall] Usage: /recall link <fromId> <toId> <relation> --yes');
167
+ return;
168
+ }
169
+ if (!parsed.yes) {
170
+ requireYesFlag(context, `link memory records ${fromId} and ${toId}`, '/recall link <fromId> <toId> <relation> --yes');
165
171
  return;
166
172
  }
167
173
  const link = await memory.link(fromId, toId, relation);
@@ -177,9 +183,14 @@ export function handleRecallRemove(args: string[], context: CommandContext): voi
177
183
  if (!memory) {
178
184
  return;
179
185
  }
180
- const id = args[0];
186
+ const parsed = stripYesFlag(args);
187
+ const id = parsed.rest[0];
181
188
  if (!id) {
182
- context.print('[recall] Usage: /recall remove <id>');
189
+ context.print('[recall] Usage: /recall remove <id> --yes');
190
+ return;
191
+ }
192
+ if (!parsed.yes) {
193
+ requireYesFlag(context, `delete durable memory record ${id}`, '/recall remove <id> --yes');
183
194
  return;
184
195
  }
185
196
  const removed = memory.delete(id);
@@ -1,6 +1,7 @@
1
1
  import type { CommandContext } from '../command-registry.ts';
2
2
  import { VALID_REVIEW_STATES, VALID_SCOPES, isValidReviewState, isValidScope } from './recall-shared.ts';
3
3
  import { getMemoryApi } from './recall-query.ts';
4
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
4
5
 
5
6
  export function handleRecallQueue(args: string[], context: CommandContext): void {
6
7
  const memory = getMemoryApi(context);
@@ -83,10 +84,15 @@ export function handleRecallPromote(args: string[], context: CommandContext): vo
83
84
  if (!memory) {
84
85
  return;
85
86
  }
86
- const id = args[0];
87
- const scope = args[1];
87
+ const parsed = stripYesFlag(args);
88
+ const id = parsed.rest[0];
89
+ const scope = parsed.rest[1];
88
90
  if (!id || !scope || !isValidScope(scope)) {
89
- context.print(`[recall] Usage: /recall promote <id> <${VALID_SCOPES.join('|')}>`);
91
+ context.print(`[recall] Usage: /recall promote <id> <${VALID_SCOPES.join('|')}> --yes`);
92
+ return;
93
+ }
94
+ if (!parsed.yes) {
95
+ requireYesFlag(context, `promote durable memory record ${id} to ${scope} scope`, '/recall promote <id> <scope> --yes');
90
96
  return;
91
97
  }
92
98
  const record = memory.update(id, { scope });