@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
@@ -1,10 +1,12 @@
1
1
  import type { CommandRegistry } from '../command-registry.ts';
2
+ import type { ProfileData } from '@pellux/goodvibes-sdk/platform/profiles';
2
3
  import { ToolContractVerifier } from '@/runtime/index.ts';
3
4
  import type { ReplaySnapshotInput } from '@/runtime/index.ts';
4
5
  import { logger } from '@pellux/goodvibes-sdk/platform/utils';
5
6
  import { registerOperatorPanelCommand } from './operator-panel-runtime.ts';
6
7
  import { requireProfileManager, requireReplayEngine } from './runtime-services.ts';
7
8
  import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
9
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
8
10
 
9
11
  function printOpsMutationBlocked(print: (text: string) => void, target: string): void {
10
12
  print([
@@ -76,15 +78,71 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
76
78
  registry.register({
77
79
  name: 'profiles',
78
80
  aliases: ['profile'],
79
- description: 'Browse and load config profiles',
80
- handler(_args, ctx) {
81
- if (ctx.openProfilePicker) {
82
- ctx.openProfilePicker();
83
- } else {
84
- const profiles = requireProfileManager(ctx).list();
85
- if (profiles.length === 0) ctx.print('No profiles saved. Open /profiles and press s to save the current settings as a profile.');
81
+ description: 'Browse, save, and delete config profiles',
82
+ usage: '[list|open|save <name> --yes|delete <name> --yes]',
83
+ argsHint: '[list|open|save --yes|delete --yes]',
84
+ handler(args, ctx) {
85
+ const parsed = stripYesFlag(args);
86
+ const sub = parsed.rest[0] ?? 'open';
87
+ const profileManager = requireProfileManager(ctx);
88
+ if (sub === 'open') {
89
+ if (ctx.openProfilePicker) {
90
+ ctx.openProfilePicker();
91
+ } else {
92
+ const profiles = profileManager.list();
93
+ if (profiles.length === 0) ctx.print('No profiles saved. Use /profiles save <name> --yes to save the current settings as a profile.');
94
+ else ctx.print(['Saved profiles:', ...profiles.map(p => ` ${p.name}`)].join('\n'));
95
+ }
96
+ return;
97
+ }
98
+ if (sub === 'list') {
99
+ const profiles = profileManager.list();
100
+ if (profiles.length === 0) ctx.print('No profiles saved. Use /profiles save <name> --yes to save the current settings as a profile.');
86
101
  else ctx.print(['Saved profiles:', ...profiles.map(p => ` ${p.name}`)].join('\n'));
102
+ return;
103
+ }
104
+ if (sub === 'save') {
105
+ const name = parsed.rest[1];
106
+ if (!name) {
107
+ ctx.print('Usage: /profiles save <name> --yes');
108
+ return;
109
+ }
110
+ if (!parsed.yes) {
111
+ requireYesFlag(ctx, `save config profile ${name}`, '/profiles save <name> --yes');
112
+ return;
113
+ }
114
+ const all = ctx.platform.configManager.getAll();
115
+ const data: ProfileData = {
116
+ display: { ...all.display },
117
+ provider: {
118
+ model: all.provider.model,
119
+ reasoningEffort: all.provider.reasoningEffort,
120
+ },
121
+ behavior: { ...all.behavior },
122
+ };
123
+ profileManager.save(name, data);
124
+ ctx.print(`Profile saved: ${name}`);
125
+ return;
126
+ }
127
+ if (sub === 'delete' || sub === 'remove') {
128
+ const name = parsed.rest[1];
129
+ if (!name) {
130
+ ctx.print('Usage: /profiles delete <name> --yes');
131
+ return;
132
+ }
133
+ if (!parsed.yes) {
134
+ requireYesFlag(ctx, `delete config profile ${name}`, '/profiles delete <name> --yes');
135
+ return;
136
+ }
137
+ const deleted = profileManager.delete(name);
138
+ ctx.print(deleted ? `Profile deleted: ${name}` : `Profile not found: ${name}`);
139
+ return;
87
140
  }
141
+ if (args.length === 0 && ctx.openProfilePicker) {
142
+ ctx.openProfilePicker();
143
+ return;
144
+ }
145
+ ctx.print('Usage: /profiles [list|open|save <name> --yes|delete <name> --yes]');
88
146
  },
89
147
  });
90
148
 
@@ -103,7 +161,7 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
103
161
  name: 'mode',
104
162
  aliases: ['hitl'],
105
163
  description: 'Manage HITL UX notification mode (quiet/balanced/operator)',
106
- usage: '[quiet|balanced|operator|show|set-domain <domain> <verbosity>]',
164
+ usage: '[quiet|balanced|operator --yes|show|set-domain <domain> <verbosity> --yes]',
107
165
  argsHint: '[preset|show|set-domain]',
108
166
  handler(args, ctx) {
109
167
  const mgr = ctx.ops.modeManager;
@@ -111,9 +169,15 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
111
169
  ctx.print('Interaction mode manager is not available in this runtime.');
112
170
  return;
113
171
  }
114
- const sub = args[0] ?? 'show';
172
+ const parsed = stripYesFlag(args);
173
+ const commandArgs = [...parsed.rest];
174
+ const sub = commandArgs[0] ?? 'show';
115
175
 
116
176
  if (sub === 'quiet' || sub === 'balanced' || sub === 'operator') {
177
+ if (!parsed.yes) {
178
+ requireYesFlag(ctx, `set HITL mode to ${sub}`, '/mode <quiet|balanced|operator> --yes');
179
+ return;
180
+ }
117
181
  const newMode = sub as 'quiet' | 'balanced' | 'operator';
118
182
  mgr.setHITLMode(newMode);
119
183
  try {
@@ -156,29 +220,33 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
156
220
  }
157
221
 
158
222
  if (sub === 'set-domain') {
159
- const domain = args[1];
160
- const verbosity = args[2];
223
+ const domain = commandArgs[1];
224
+ const verbosity = commandArgs[2];
161
225
  if (!domain || !verbosity) {
162
- ctx.print('Usage: /mode set-domain <domain> <minimal|normal|verbose>');
226
+ ctx.print('Usage: /mode set-domain <domain> <minimal|normal|verbose> --yes');
163
227
  return;
164
228
  }
165
229
  if (verbosity !== 'minimal' && verbosity !== 'normal' && verbosity !== 'verbose') {
166
230
  ctx.print(`Invalid verbosity "${verbosity}". Valid values: minimal, normal, verbose`);
167
231
  return;
168
232
  }
233
+ if (!parsed.yes) {
234
+ requireYesFlag(ctx, `set HITL verbosity for ${domain}`, '/mode set-domain <domain> <minimal|normal|verbose> --yes');
235
+ return;
236
+ }
169
237
  mgr.setDomainVerbosity(domain, verbosity as 'minimal' | 'normal' | 'verbose');
170
238
  ctx.print(`Domain "${domain}" verbosity set to: ${verbosity}`);
171
239
  return;
172
240
  }
173
241
 
174
242
  ctx.print(
175
- 'Usage: /mode [quiet|balanced|operator|show|set-domain <domain> <verbosity>]\n'
243
+ 'Usage: /mode [quiet|balanced|operator --yes|show|set-domain <domain> <verbosity> --yes]\n'
176
244
  + ' /mode — show current mode and settings\n'
177
245
  + ' /mode show — show current mode and settings\n'
178
- + ' /mode quiet — suppress all non-critical notifications\n'
179
- + ' /mode balanced — surface warnings, batch info noise (default)\n'
180
- + ' /mode operator — full verbosity, no suppression\n'
181
- + ' /mode set-domain <d> <v> — per-domain verbosity override (minimal|normal|verbose)'
246
+ + ' /mode quiet --yes — suppress all non-critical notifications\n'
247
+ + ' /mode balanced --yes — surface warnings, batch info noise (default)\n'
248
+ + ' /mode operator --yes — full verbosity, no suppression\n'
249
+ + ' /mode set-domain <d> <v> --yes — per-domain verbosity override (minimal|normal|verbose)'
182
250
  );
183
251
  },
184
252
  });
@@ -6,6 +6,7 @@ import type {
6
6
  } from '@pellux/goodvibes-sdk/platform/knowledge';
7
7
  import type { CommandRegistry } from '../command-registry.ts';
8
8
  import { requirePlanManager, requireSessionLineageTracker } from './runtime-services.ts';
9
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
9
10
 
10
11
  function recordNextQuestion(
11
12
  state: Partial<ProjectPlanningState>,
@@ -51,15 +52,21 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
51
52
  registry.register({
52
53
  name: 'plan',
53
54
  description: 'Inspect or seed Agent workspace planning state',
54
- usage: '[panel | approve | list | show <id> | mode | explain | override <strategy> | status | clear | <planning goal>]',
55
+ usage: '[panel | approve --yes | list | show <id> | mode | explain | override <strategy> --yes | status | clear --yes | <planning goal>]',
55
56
  argsHint: '[panel|approve|status|<goal>]',
56
57
  async handler(args, ctx) {
57
58
  const planManager = requirePlanManager(ctx);
58
59
  const sessionLineageTracker = requireSessionLineageTracker(ctx);
59
60
  const plannerSubs = ['mode', 'explain', 'override', 'status', 'clear'];
60
61
  if (args.length > 0 && plannerSubs.includes(args[0].toLowerCase())) {
62
+ const parsed = stripYesFlag(args);
63
+ const subcommand = parsed.rest[0]?.toLowerCase() ?? '';
64
+ if ((subcommand === 'override' || subcommand === 'clear') && !parsed.yes) {
65
+ requireYesFlag(ctx, `${subcommand} planner runtime state`, `/plan ${subcommand}${subcommand === 'override' ? ' <strategy>' : ''} --yes`);
66
+ return;
67
+ }
61
68
  const result = ctx.ops.planRuntime
62
- ? ctx.ops.planRuntime(args[0], args.slice(1))
69
+ ? ctx.ops.planRuntime(parsed.rest[0] ?? args[0], parsed.rest.slice(1))
63
70
  : { ok: false, output: 'Plan runtime bridge is not available in this runtime.' };
64
71
  ctx.print(result.output);
65
72
  return;
@@ -109,6 +116,11 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
109
116
  }
110
117
 
111
118
  if (args[0] === 'approve') {
119
+ const parsed = stripYesFlag(args);
120
+ if (!parsed.yes) {
121
+ requireYesFlag(ctx, 'approve project planning state for execution', '/plan approve --yes');
122
+ return;
123
+ }
112
124
  if (!projectPlanningService || !projectId) {
113
125
  ctx.print('Project planning service is not available in this runtime.');
114
126
  return;
@@ -6,6 +6,7 @@ import { listBuiltinSubscriptionProviders } from '@pellux/goodvibes-sdk/platform
6
6
  import { handleLocalAuthCommand } from './local-auth-runtime.ts';
7
7
  import { buildAuthInspectionSnapshot, inspectProviderAuth } from '@/runtime/index.ts';
8
8
  import { requireProfileManager, requireSecretsManager, requireServiceRegistry, requireShellPaths, requireSubscriptionManager } from './runtime-services.ts';
9
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
9
10
 
10
11
  interface InstallBundle {
11
12
  readonly version: 1;
@@ -81,61 +82,79 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
81
82
  registry.register({
82
83
  name: 'login',
83
84
  description: 'Front-door login flow for provider subscriptions and local service sessions',
84
- usage: '[provider <name> start|finish <code>|service <daemon|listener> <baseUrl> <username> <password> [secretKey]]',
85
+ usage: '[provider <name> start|finish <code> --yes|service <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes]',
85
86
  async handler(args, ctx) {
87
+ const parsed = stripYesFlag(args);
88
+ const commandArgs = [...parsed.rest];
86
89
  const shellPaths = requireShellPaths(ctx);
87
- const target = (args[0] ?? '').toLowerCase();
90
+ const target = (commandArgs[0] ?? '').toLowerCase();
88
91
  if (target === 'provider') {
89
- const provider = args[1];
90
- const mode = args[2]?.toLowerCase();
92
+ const provider = commandArgs[1];
93
+ const mode = commandArgs[2]?.toLowerCase();
91
94
  if (!provider || !mode) {
92
- ctx.print('Usage: /login provider <name> start|finish <code>');
95
+ ctx.print('Usage: /login provider <name> start|finish <code> --yes');
96
+ return;
97
+ }
98
+ if (!parsed.yes) {
99
+ requireYesFlag(ctx, `${mode} provider subscription login for ${provider}`, '/login provider <name> start|finish <code> --yes');
93
100
  return;
94
101
  }
95
102
  if (ctx.executeCommand) {
96
- await ctx.executeCommand('subscription', ['login', provider, mode, ...args.slice(3)]);
103
+ await ctx.executeCommand('subscription', ['login', provider, mode, ...commandArgs.slice(3), '--yes']);
97
104
  return;
98
105
  }
99
- ctx.print(`Use /subscription login ${provider} ${mode}${args[3] ? ` ${args[3]}` : ''}`);
106
+ ctx.print(`Use /subscription login ${provider} ${mode}${commandArgs[3] ? ` ${commandArgs[3]}` : ''} --yes`);
100
107
  return;
101
108
  }
102
109
  if (target === 'service') {
110
+ if (!parsed.yes) {
111
+ requireYesFlag(ctx, 'store a local service session token', '/login service <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes');
112
+ return;
113
+ }
103
114
  if (ctx.executeCommand) {
104
- await ctx.executeCommand('auth', ['login', ...args.slice(1)]);
115
+ await ctx.executeCommand('auth', ['login', ...commandArgs.slice(1), '--yes']);
105
116
  return;
106
117
  }
107
- ctx.print('Use /auth login <daemon|listener> <baseUrl> <username> <password> [secretKey]');
118
+ ctx.print('Use /auth login <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes');
108
119
  return;
109
120
  }
110
- ctx.print('Usage: /login [provider <name> start|finish <code>|service <daemon|listener> <baseUrl> <username> <password> [secretKey]]');
121
+ ctx.print('Usage: /login [provider <name> start|finish <code> --yes|service <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes]');
111
122
  },
112
123
  });
113
124
 
114
125
  registry.register({
115
126
  name: 'logout',
116
127
  description: 'Front-door logout flow for provider subscription sessions and supported overrides',
117
- usage: 'provider <name>',
128
+ usage: 'provider <name> --yes',
118
129
  async handler(args, ctx) {
119
- const target = (args[0] ?? '').toLowerCase();
120
- if (target !== 'provider' || !args[1]) {
121
- ctx.print('Usage: /logout provider <name>');
130
+ const parsed = stripYesFlag(args);
131
+ const commandArgs = [...parsed.rest];
132
+ const target = (commandArgs[0] ?? '').toLowerCase();
133
+ if (target !== 'provider' || !commandArgs[1]) {
134
+ ctx.print('Usage: /logout provider <name> --yes');
135
+ return;
136
+ }
137
+ if (!parsed.yes) {
138
+ requireYesFlag(ctx, `log out provider subscription ${commandArgs[1]}`, '/logout provider <name> --yes');
122
139
  return;
123
140
  }
124
141
  if (ctx.executeCommand) {
125
- await ctx.executeCommand('subscription', ['logout', args[1]]);
142
+ await ctx.executeCommand('subscription', ['logout', commandArgs[1], '--yes']);
126
143
  return;
127
144
  }
128
- ctx.print(`Use /subscription logout ${args[1]}`);
145
+ ctx.print(`Use /subscription logout ${commandArgs[1]} --yes`);
129
146
  },
130
147
  });
131
148
 
132
149
  registry.register({
133
150
  name: 'install',
134
151
  description: 'Review install posture and export portable install bundles',
135
- usage: '[review|bundle export <path>|bundle inspect <path>]',
152
+ usage: '[review|bundle export <path> --yes|bundle inspect <path>]',
136
153
  async handler(args, ctx) {
154
+ const parsed = stripYesFlag(args);
155
+ const commandArgs = [...parsed.rest];
137
156
  const shellPaths = requireShellPaths(ctx);
138
- const sub = args[0] ?? 'review';
157
+ const sub = commandArgs[0] ?? 'review';
139
158
  if (sub === 'review') {
140
159
  const profiles = requireProfileManager(ctx).list();
141
160
  const secretKeys = await requireSecretsManager(ctx).list();
@@ -149,14 +168,18 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
149
168
  return;
150
169
  }
151
170
  if (sub === 'bundle') {
152
- const mode = args[1];
153
- const pathArg = args[2];
171
+ const mode = commandArgs[1];
172
+ const pathArg = commandArgs[2];
154
173
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
155
- ctx.print(`Usage: /install bundle ${mode} <path>`);
174
+ ctx.print(`Usage: /install bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
156
175
  return;
157
176
  }
158
177
  const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
159
178
  if (mode === 'export') {
179
+ if (!parsed.yes) {
180
+ requireYesFlag(ctx, `export install bundle to ${pathArg}`, '/install bundle export <path> --yes');
181
+ return;
182
+ }
160
183
  const profiles = requireProfileManager(ctx).list();
161
184
  const secretKeys = await requireSecretsManager(ctx).list();
162
185
  const bundle: InstallBundle = {
@@ -185,7 +208,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
185
208
  return;
186
209
  }
187
210
  }
188
- ctx.print('Usage: /install [review|bundle export <path>|bundle inspect <path>]');
211
+ ctx.print('Usage: /install [review|bundle export <path> --yes|bundle inspect <path>]');
189
212
  },
190
213
  });
191
214
 
@@ -193,10 +216,12 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
193
216
  name: 'update',
194
217
  aliases: ['upgrade'],
195
218
  description: 'Review update posture, choose release channel guidance, and package portable update bundles',
196
- usage: '[review|channel <stable|preview>|bundle export <path>|bundle inspect <path>]',
219
+ usage: '[review|channel <stable|preview> --yes|bundle export <path> --yes|bundle inspect <path>]',
197
220
  handler(args, ctx) {
221
+ const parsed = stripYesFlag(args);
222
+ const commandArgs = [...parsed.rest];
198
223
  const shellPaths = requireShellPaths(ctx);
199
- const sub = args[0] ?? 'review';
224
+ const sub = commandArgs[0] ?? 'review';
200
225
  const subscriptions = requireSubscriptionManager(ctx);
201
226
  const serviceRegistry = requireServiceRegistry(ctx);
202
227
  const secretsManager = requireSecretsManager(ctx);
@@ -216,14 +241,18 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
216
241
  ` built-in subscription providers: ${builtinProviders.length}${builtinProviders.length > 0 ? ` (${builtinProviders.join(', ')})` : ''}`,
217
242
  ` active subscriptions: ${activeSubscriptions.length}${activeSubscriptions.length > 0 ? ` (${activeSubscriptions.join(', ')})` : ''}`,
218
243
  ` sandbox profile: ${sandboxProfile}`,
219
- ' use /update channel <stable|preview> to change release posture',
244
+ ' use /update channel <stable|preview> --yes to change release posture',
220
245
  ].join('\n'));
221
246
  return;
222
247
  }
223
248
  if (sub === 'channel') {
224
- const channel = args[1];
249
+ const channel = commandArgs[1];
225
250
  if (channel !== 'stable' && channel !== 'preview') {
226
- ctx.print('Usage: /update channel <stable|preview>');
251
+ ctx.print('Usage: /update channel <stable|preview> --yes');
252
+ return;
253
+ }
254
+ if (!parsed.yes) {
255
+ requireYesFlag(ctx, `set update channel to ${channel}`, '/update channel <stable|preview> --yes');
227
256
  return;
228
257
  }
229
258
  ctx.platform.configManager.setDynamic('release.channel', channel);
@@ -231,14 +260,18 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
231
260
  return;
232
261
  }
233
262
  if (sub === 'bundle') {
234
- const mode = args[1];
235
- const pathArg = args[2];
263
+ const mode = commandArgs[1];
264
+ const pathArg = commandArgs[2];
236
265
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
237
- ctx.print(`Usage: /update bundle ${mode} <path>`);
266
+ ctx.print(`Usage: /update bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
238
267
  return;
239
268
  }
240
269
  const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
241
270
  if (mode === 'export') {
271
+ if (!parsed.yes) {
272
+ requireYesFlag(ctx, `export update bundle to ${pathArg}`, '/update bundle export <path> --yes');
273
+ return;
274
+ }
242
275
  const bundle: UpdateBundle = {
243
276
  version: 1,
244
277
  exportedAt: Date.now(),
@@ -262,17 +295,19 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
262
295
  return;
263
296
  }
264
297
  }
265
- ctx.print('Usage: /update [review|channel <stable|preview>|bundle export <path>|bundle inspect <path>]');
298
+ ctx.print('Usage: /update [review|channel <stable|preview> --yes|bundle export <path> --yes|bundle inspect <path>]');
266
299
  },
267
300
  });
268
301
 
269
302
  registry.register({
270
303
  name: 'auth',
271
304
  description: 'Review auth posture and exchange session login tokens with local services',
272
- usage: '[review|show <provider>|repair <provider>|bundle export <path>|bundle inspect <path>|login <daemon|listener> <baseUrl> <username> <password> [secretKey]|local <review|panel|add-user|delete-user|rotate-password|revoke-session|clear-bootstrap-file>]',
305
+ usage: '[review|show <provider>|repair <provider>|bundle export <path> --yes|bundle inspect <path>|login <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes|local <review|panel|add-user --yes|delete-user --yes|rotate-password --yes|revoke-session --yes|clear-bootstrap-file --yes>]',
273
306
  async handler(args, ctx) {
307
+ const parsed = stripYesFlag(args);
308
+ const commandArgs = [...parsed.rest];
274
309
  const shellPaths = requireShellPaths(ctx);
275
- const sub = args[0] ?? 'review';
310
+ const sub = commandArgs[0] ?? 'review';
276
311
  const subscriptions = requireSubscriptionManager(ctx);
277
312
  const serviceRegistry = requireServiceRegistry(ctx);
278
313
  const secretsManager = requireSecretsManager(ctx);
@@ -301,7 +336,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
301
336
  }
302
337
 
303
338
  if (sub === 'show') {
304
- const provider = args[1];
339
+ const provider = commandArgs[1];
305
340
  if (!provider) {
306
341
  ctx.print('Usage: /auth show <provider>');
307
342
  return;
@@ -331,7 +366,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
331
366
  }
332
367
 
333
368
  if (sub === 'repair') {
334
- const provider = args[1];
369
+ const provider = commandArgs[1];
335
370
  if (!provider) {
336
371
  ctx.print('Usage: /auth repair <provider>');
337
372
  return;
@@ -355,14 +390,18 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
355
390
  }
356
391
 
357
392
  if (sub === 'bundle') {
358
- const mode = args[1];
359
- const pathArg = args[2];
393
+ const mode = commandArgs[1];
394
+ const pathArg = commandArgs[2];
360
395
  if ((mode === 'export' || mode === 'inspect') && !pathArg) {
361
- ctx.print(`Usage: /auth bundle ${mode} <path>`);
396
+ ctx.print(`Usage: /auth bundle ${mode} <path>${mode === 'export' ? ' --yes' : ''}`);
362
397
  return;
363
398
  }
364
399
  const targetPath = shellPaths.resolveWorkspacePath(pathArg!);
365
400
  if (mode === 'export') {
401
+ if (!parsed.yes) {
402
+ requireYesFlag(ctx, `export auth review bundle to ${pathArg}`, '/auth bundle export <path> --yes');
403
+ return;
404
+ }
366
405
  const secretKeys = await secretsManager.list();
367
406
  const bundle: AuthReviewBundle = {
368
407
  version: 1,
@@ -386,13 +425,17 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
386
425
  }
387
426
 
388
427
  if (sub === 'login') {
389
- const target = args[1];
390
- const baseUrl = args[2];
391
- const username = args[3];
392
- const password = args[4];
393
- const secretKey = args[5] ?? `${target?.toUpperCase() ?? 'SERVICE'}_SESSION_TOKEN`;
428
+ const target = commandArgs[1];
429
+ const baseUrl = commandArgs[2];
430
+ const username = commandArgs[3];
431
+ const password = commandArgs[4];
432
+ const secretKey = commandArgs[5] ?? `${target?.toUpperCase() ?? 'SERVICE'}_SESSION_TOKEN`;
394
433
  if ((target !== 'daemon' && target !== 'listener') || !baseUrl || !username || !password) {
395
- ctx.print('Usage: /auth login <daemon|listener> <baseUrl> <username> <password> [secretKey]');
434
+ ctx.print('Usage: /auth login <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes');
435
+ return;
436
+ }
437
+ if (!parsed.yes) {
438
+ requireYesFlag(ctx, `store ${target} session token`, '/auth login <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes');
396
439
  return;
397
440
  }
398
441
  const url = new URL('/login', baseUrl).toString();
@@ -416,7 +459,7 @@ export function registerPlatformAccessRuntimeCommands(registry: CommandRegistry)
416
459
  return;
417
460
  }
418
461
 
419
- ctx.print('Usage: /auth [review|show <provider>|bundle export <path>|bundle inspect <path>|login <daemon|listener> <baseUrl> <username> <password> [secretKey]|local <review|panel|add-user|delete-user|rotate-password|revoke-session|clear-bootstrap-file>]');
462
+ ctx.print('Usage: /auth [review|show <provider>|bundle export <path> --yes|bundle inspect <path>|login <daemon|listener> <baseUrl> <username> <password> [secretKey] --yes|local <review|panel|add-user --yes|delete-user --yes|rotate-password --yes|revoke-session --yes|clear-bootstrap-file --yes>]');
420
463
  },
421
464
  });
422
465
  }