@pellux/goodvibes-agent 0.1.10 → 0.1.12

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 (69) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/package.json +1 -1
  3. package/src/cli/agent-knowledge-command.ts +30 -3
  4. package/src/cli/help.ts +2 -2
  5. package/src/input/commands/cloudflare-runtime.ts +20 -5
  6. package/src/input/commands/confirmation.ts +24 -0
  7. package/src/input/commands/discovery-runtime.ts +16 -7
  8. package/src/input/commands/eval.ts +27 -14
  9. package/src/input/commands/experience-runtime.ts +65 -26
  10. package/src/input/commands/health-runtime.ts +1 -1
  11. package/src/input/commands/hooks-runtime.ts +50 -19
  12. package/src/input/commands/incident-runtime.ts +17 -6
  13. package/src/input/commands/integration-runtime.ts +93 -50
  14. package/src/input/commands/knowledge.ts +38 -12
  15. package/src/input/commands/local-auth-runtime.ts +36 -13
  16. package/src/input/commands/local-provider-runtime.ts +22 -11
  17. package/src/input/commands/local-runtime.ts +21 -11
  18. package/src/input/commands/local-setup.ts +35 -16
  19. package/src/input/commands/managed-runtime.ts +51 -20
  20. package/src/input/commands/marketplace-runtime.ts +31 -16
  21. package/src/input/commands/mcp-runtime.ts +65 -34
  22. package/src/input/commands/memory-product-runtime.ts +72 -35
  23. package/src/input/commands/memory.ts +9 -9
  24. package/src/input/commands/notify-runtime.ts +27 -8
  25. package/src/input/commands/operator-runtime.ts +85 -17
  26. package/src/input/commands/planning-runtime.ts +14 -2
  27. package/src/input/commands/platform-access-runtime.ts +88 -45
  28. package/src/input/commands/platform-services-runtime.ts +51 -25
  29. package/src/input/commands/product-runtime.ts +54 -27
  30. package/src/input/commands/profile-sync-runtime.ts +17 -6
  31. package/src/input/commands/recall-bundle.ts +38 -17
  32. package/src/input/commands/recall-query.ts +15 -4
  33. package/src/input/commands/recall-review.ts +9 -3
  34. package/src/input/commands/remote-runtime-setup.ts +45 -18
  35. package/src/input/commands/remote-runtime.ts +25 -9
  36. package/src/input/commands/replay-runtime.ts +9 -2
  37. package/src/input/commands/services-runtime.ts +21 -10
  38. package/src/input/commands/session-content.ts +53 -51
  39. package/src/input/commands/session-workflow.ts +10 -4
  40. package/src/input/commands/session.ts +1 -1
  41. package/src/input/commands/settings-sync-runtime.ts +40 -17
  42. package/src/input/commands/share-runtime.ts +12 -4
  43. package/src/input/commands/shell-core.ts +3 -3
  44. package/src/input/commands/subscription-runtime.ts +35 -20
  45. package/src/input/commands/teleport-runtime.ts +16 -5
  46. package/src/input/commands/work-plan-runtime.ts +23 -12
  47. package/src/input/handler-content-actions.ts +11 -62
  48. package/src/input/handler-interactions.ts +1 -1
  49. package/src/input/handler-onboarding-cloudflare.ts +48 -117
  50. package/src/input/keybindings.ts +1 -1
  51. package/src/input/mcp-workspace.ts +25 -49
  52. package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +8 -8
  53. package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -6
  54. package/src/input/profile-picker-modal.ts +13 -31
  55. package/src/input/session-picker-modal.ts +4 -30
  56. package/src/input/settings-modal-subscriptions.ts +3 -3
  57. package/src/panels/incident-review-panel.ts +1 -1
  58. package/src/panels/local-auth-panel.ts +4 -4
  59. package/src/panels/provider-account-snapshot.ts +1 -1
  60. package/src/panels/provider-health-domains.ts +2 -2
  61. package/src/panels/settings-sync-panel.ts +2 -2
  62. package/src/panels/subscription-panel.ts +7 -7
  63. package/src/renderer/block-actions.ts +1 -1
  64. package/src/renderer/help-overlay.ts +2 -2
  65. package/src/renderer/mcp-workspace.ts +12 -12
  66. package/src/renderer/profile-picker-modal.ts +3 -11
  67. package/src/renderer/session-picker-modal.ts +2 -10
  68. package/src/verification/live-verifier.ts +100 -68
  69. package/src/version.ts +1 -1
@@ -1,111 +1,148 @@
1
1
  import type { CommandRegistry } from '../command-registry.ts';
2
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
2
3
 
3
4
  export function registerMemoryProductRuntimeCommands(registry: CommandRegistry): void {
4
5
  registry.register({
5
6
  name: 'memory-sync',
6
7
  aliases: ['memsync'],
7
8
  description: 'Dedicated front-door for durable memory export/import and bundle exchange',
8
- usage: '[export <path> [scope] | import <path>]',
9
+ usage: '[export <path> [scope] --yes | import <path> --yes]',
9
10
  async handler(args, ctx) {
10
- const sub = (args[0] ?? '').toLowerCase();
11
+ const parsed = stripYesFlag(args);
12
+ const commandArgs = [...parsed.rest];
13
+ const sub = (commandArgs[0] ?? '').toLowerCase();
11
14
  if (!ctx.executeCommand) {
12
15
  ctx.print('Memory sync controls are not available in this runtime.');
13
16
  return;
14
17
  }
15
- if (sub === 'export' && args[1]) {
16
- const scope = args[2];
17
- const recallArgs = ['export', args[1], ...(scope ? ['--scope', scope] : [])];
18
+ if (sub === 'export' && commandArgs[1]) {
19
+ if (!parsed.yes) {
20
+ requireYesFlag(ctx, `export durable memory bundle to ${commandArgs[1]}`, '/memory-sync export <path> [scope] --yes');
21
+ return;
22
+ }
23
+ const scope = commandArgs[2];
24
+ const recallArgs = ['export', commandArgs[1], ...(scope ? ['--scope', scope] : []), '--yes'];
18
25
  await ctx.executeCommand('recall', recallArgs);
19
26
  return;
20
27
  }
21
- if (sub === 'import' && args[1]) {
22
- await ctx.executeCommand('recall', ['import', args[1]]);
28
+ if (sub === 'import' && commandArgs[1]) {
29
+ if (!parsed.yes) {
30
+ requireYesFlag(ctx, `import durable memory bundle from ${commandArgs[1]}`, '/memory-sync import <path> --yes');
31
+ return;
32
+ }
33
+ await ctx.executeCommand('recall', ['import', commandArgs[1], '--yes']);
23
34
  return;
24
35
  }
25
- ctx.print('Usage: /memory-sync [export <path> [scope] | import <path>]');
36
+ ctx.print('Usage: /memory-sync [export <path> [scope] --yes | import <path> --yes]');
26
37
  },
27
38
  });
28
39
 
29
40
  registry.register({
30
41
  name: 'handoff',
31
42
  description: 'Dedicated front-door for reviewable memory handoff bundles',
32
- usage: '[export <path> [scope] | inspect <path> | import <path>]',
43
+ usage: '[export <path> [scope] --yes | inspect <path> | import <path> --yes]',
33
44
  async handler(args, ctx) {
34
- const sub = (args[0] ?? '').toLowerCase();
45
+ const parsed = stripYesFlag(args);
46
+ const commandArgs = [...parsed.rest];
47
+ const sub = (commandArgs[0] ?? '').toLowerCase();
35
48
  if (!ctx.executeCommand) {
36
49
  ctx.print('Handoff controls are not available in this runtime.');
37
50
  return;
38
51
  }
39
- if (sub === 'export' && args[1]) {
40
- const scope = args[2];
41
- await ctx.executeCommand('recall', ['handoff-export', args[1], ...(scope ? ['--scope', scope] : [])]);
52
+ if (sub === 'export' && commandArgs[1]) {
53
+ if (!parsed.yes) {
54
+ requireYesFlag(ctx, `export memory handoff bundle to ${commandArgs[1]}`, '/handoff export <path> [scope] --yes');
55
+ return;
56
+ }
57
+ const scope = commandArgs[2];
58
+ await ctx.executeCommand('recall', ['handoff-export', commandArgs[1], ...(scope ? ['--scope', scope] : []), '--yes']);
42
59
  return;
43
60
  }
44
- if (sub === 'inspect' && args[1]) {
45
- await ctx.executeCommand('recall', ['handoff-inspect', args[1]]);
61
+ if (sub === 'inspect' && commandArgs[1]) {
62
+ await ctx.executeCommand('recall', ['handoff-inspect', commandArgs[1]]);
46
63
  return;
47
64
  }
48
- if (sub === 'import' && args[1]) {
49
- await ctx.executeCommand('recall', ['handoff-import', args[1]]);
65
+ if (sub === 'import' && commandArgs[1]) {
66
+ if (!parsed.yes) {
67
+ requireYesFlag(ctx, `import memory handoff bundle from ${commandArgs[1]}`, '/handoff import <path> --yes');
68
+ return;
69
+ }
70
+ await ctx.executeCommand('recall', ['handoff-import', commandArgs[1], '--yes']);
50
71
  return;
51
72
  }
52
- ctx.print('Usage: /handoff [export <path> [scope] | inspect <path> | import <path>]');
73
+ ctx.print('Usage: /handoff [export <path> [scope] --yes | inspect <path> | import <path> --yes]');
53
74
  },
54
75
  });
55
76
 
56
77
  registry.register({
57
78
  name: 'session-memory',
58
79
  description: 'Dedicated front-door for session-scoped memory capture and review',
59
- usage: '[queue [limit] | export <path> | add <class> <summary...>]',
80
+ usage: '[queue [limit] | export <path> --yes | add <class> <summary...>]',
60
81
  async handler(args, ctx) {
61
- const sub = (args[0] ?? 'queue').toLowerCase();
82
+ const parsed = stripYesFlag(args);
83
+ const commandArgs = [...parsed.rest];
84
+ const sub = (commandArgs[0] ?? 'queue').toLowerCase();
62
85
  if (!ctx.executeCommand) {
63
86
  ctx.print('Session memory controls are not available in this runtime.');
64
87
  return;
65
88
  }
66
89
  if (sub === 'queue') {
67
- await ctx.executeCommand('recall', ['queue', ...(args[1] ? [args[1]] : [])]);
90
+ await ctx.executeCommand('recall', ['queue', ...(commandArgs[1] ? [commandArgs[1]] : [])]);
68
91
  return;
69
92
  }
70
- if (sub === 'export' && args[1]) {
71
- await ctx.executeCommand('recall', ['export', args[1], '--scope', 'session']);
93
+ if (sub === 'export' && commandArgs[1]) {
94
+ if (!parsed.yes) {
95
+ requireYesFlag(ctx, `export session memory bundle to ${commandArgs[1]}`, '/session-memory export <path> --yes');
96
+ return;
97
+ }
98
+ await ctx.executeCommand('recall', ['export', commandArgs[1], '--scope', 'session', '--yes']);
72
99
  return;
73
100
  }
74
- if (sub === 'add' && args.length >= 3) {
75
- await ctx.executeCommand('recall', ['add', args[1], ...args.slice(2), '--scope', 'session']);
101
+ if (sub === 'add' && commandArgs.length >= 3) {
102
+ await ctx.executeCommand('recall', ['add', commandArgs[1], ...commandArgs.slice(2), '--scope', 'session']);
76
103
  return;
77
104
  }
78
- ctx.print('Usage: /session-memory [queue [limit] | export <path> | add <class> <summary...>]');
105
+ ctx.print('Usage: /session-memory [queue [limit] | export <path> --yes | add <class> <summary...>]');
79
106
  },
80
107
  });
81
108
 
82
109
  registry.register({
83
110
  name: 'team-memory',
84
111
  description: 'Dedicated front-door for team/shared memory review and exchange',
85
- usage: '[queue [limit] | export <path> | import <path> | capture policy]',
112
+ usage: '[queue [limit] | export <path> --yes | import <path> --yes | capture policy]',
86
113
  async handler(args, ctx) {
87
- const sub = (args[0] ?? 'queue').toLowerCase();
114
+ const parsed = stripYesFlag(args);
115
+ const commandArgs = [...parsed.rest];
116
+ const sub = (commandArgs[0] ?? 'queue').toLowerCase();
88
117
  if (!ctx.executeCommand) {
89
118
  ctx.print('Team memory controls are not available in this runtime.');
90
119
  return;
91
120
  }
92
121
  if (sub === 'queue') {
93
- await ctx.executeCommand('recall', ['queue', ...(args[1] ? [args[1]] : [])]);
122
+ await ctx.executeCommand('recall', ['queue', ...(commandArgs[1] ? [commandArgs[1]] : [])]);
94
123
  return;
95
124
  }
96
- if (sub === 'export' && args[1]) {
97
- await ctx.executeCommand('recall', ['handoff-export', args[1], '--scope', 'team']);
125
+ if (sub === 'export' && commandArgs[1]) {
126
+ if (!parsed.yes) {
127
+ requireYesFlag(ctx, `export team memory handoff bundle to ${commandArgs[1]}`, '/team-memory export <path> --yes');
128
+ return;
129
+ }
130
+ await ctx.executeCommand('recall', ['handoff-export', commandArgs[1], '--scope', 'team', '--yes']);
98
131
  return;
99
132
  }
100
- if (sub === 'import' && args[1]) {
101
- await ctx.executeCommand('recall', ['handoff-import', args[1]]);
133
+ if (sub === 'import' && commandArgs[1]) {
134
+ if (!parsed.yes) {
135
+ requireYesFlag(ctx, `import team memory handoff bundle from ${commandArgs[1]}`, '/team-memory import <path> --yes');
136
+ return;
137
+ }
138
+ await ctx.executeCommand('recall', ['handoff-import', commandArgs[1], '--yes']);
102
139
  return;
103
140
  }
104
- if (sub === 'capture' && args[1]?.toLowerCase() === 'policy') {
141
+ if (sub === 'capture' && commandArgs[1]?.toLowerCase() === 'policy') {
105
142
  await ctx.executeCommand('recall', ['capture', 'policy']);
106
143
  return;
107
144
  }
108
- ctx.print('Usage: /team-memory [queue [limit] | export <path> | import <path> | capture policy]');
145
+ ctx.print('Usage: /team-memory [queue [limit] | export <path> --yes | import <path> --yes | capture policy]');
109
146
  },
110
147
  });
111
148
  }
@@ -7,10 +7,10 @@
7
7
  * /recall add <class> <summary> --detail <text> --tags <tag,tag>
8
8
  * /recall search [query] — Search memory records
9
9
  * /recall search --cls <class> — Filter by class
10
- * /recall link <fromId> <toId> <relation> — Link two records
10
+ * /recall link <fromId> <toId> <relation> --yes — Link two records
11
11
  * /recall get <id> — Show a single record with provenance
12
12
  * /recall list [class] — List all records (optionally by class)
13
- * /recall remove <id> — Delete a record
13
+ * /recall remove <id> --yes — Delete a record
14
14
  */
15
15
 
16
16
  import type { SlashCommand, CommandContext } from '../command-registry.ts';
@@ -128,20 +128,20 @@ export const recallCommand: SlashCommand = {
128
128
  ' search [query] [--semantic] [--cls <class>] [--scope <scope>] [--limit <n>] — Full-text or sqlite-vec semantic search',
129
129
  ' vector [status|doctor|rebuild] — Inspect or rebuild the sqlite-vec memory index',
130
130
  ' get <id> — Show record with provenance + links',
131
- ' link <fromId> <toId> <relation> — Create a directed relation between records',
131
+ ' link <fromId> <toId> <relation> --yes — Create a directed relation between records',
132
132
  ' queue [limit] — Show the operator review queue',
133
133
  ' review <id> <state> [--confidence <n>] [--by <name>] [--reason <text>]',
134
134
  ' stale <id> [reason...] — Mark a record stale with an operator reason',
135
135
  ' contradict <id> [reason...] — Mark a record contradicted with an operator reason',
136
136
  ' explain <task...> [--scope <path> ...] — Show the knowledge records that would be injected for a task',
137
- ' promote <id> <scope> — Promote a memory record into session|project|team scope',
138
- ' export <path> [--scope <scope>] [--cls <class>] — Export a durable knowledge bundle',
139
- ' import <path> — Import a durable knowledge bundle',
140
- ' handoff-export <path> [--scope <scope>] — Export a reviewable handoff bundle for team/shared use',
137
+ ' promote <id> <scope> --yes — Promote a memory record into session|project|team scope',
138
+ ' export <path> [--scope <scope>] [--cls <class>] --yes — Export a durable knowledge bundle',
139
+ ' import <path> --yes — Import a durable knowledge bundle',
140
+ ' handoff-export <path> [--scope <scope>] --yes — Export a reviewable handoff bundle for team/shared use',
141
141
  ' handoff-inspect <path> — Inspect a handoff bundle before import',
142
- ' handoff-import <path> — Import a handoff bundle into durable memory',
142
+ ' handoff-import <path> --yes — Import a handoff bundle into durable memory',
143
143
  ' list [class] [--scope <scope>] — List all records grouped by class',
144
- ' remove <id> — Delete a record',
144
+ ' remove <id> --yes — Delete a record',
145
145
  ].join('\n');
146
146
  context.print(usage);
147
147
  break;
@@ -1,18 +1,21 @@
1
1
  import type { CommandRegistry } from '../command-registry.ts';
2
2
  import { requireWebhookNotifier } from './runtime-services.ts';
3
+ import { requireYesFlag, stripYesFlag } from './confirmation.ts';
3
4
 
4
5
  export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
5
6
  registry.register({
6
7
  name: 'notify',
7
8
  aliases: [],
8
9
  description: 'Manage webhook notification URLs (ntfy.sh format)',
9
- usage: 'add <url> | remove <url> | list | clear | test',
10
- argsHint: 'add|remove|list|clear|test',
10
+ usage: 'add <url> --yes | remove <url> --yes | list | clear --yes | test --yes',
11
+ argsHint: 'list|add --yes|remove --yes|test --yes',
11
12
  async handler(args, ctx) {
13
+ const parsed = stripYesFlag(args);
14
+ const commandArgs = [...parsed.rest];
12
15
  const notifications = ctx.platform.configManager.getCategory('notifications');
13
16
  const urls: string[] = Array.isArray(notifications.webhookUrls) ? [...notifications.webhookUrls] : [];
14
17
  const notifier = requireWebhookNotifier(ctx);
15
- const sub = args[0];
18
+ const sub = commandArgs[0];
16
19
 
17
20
  if (!sub || sub === 'list') {
18
21
  if (urls.length === 0) ctx.print('No webhook URLs configured.\nUse: /notify add <url>');
@@ -21,9 +24,13 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
21
24
  }
22
25
 
23
26
  if (sub === 'add') {
24
- const url = args[1];
27
+ const url = commandArgs[1];
25
28
  if (!url) {
26
- ctx.print('Usage: /notify add <url>\nExample: /notify add https://ntfy.sh/my-topic');
29
+ ctx.print('Usage: /notify add <url> --yes\nExample: /notify add https://ntfy.sh/my-topic --yes');
30
+ return;
31
+ }
32
+ if (!parsed.yes) {
33
+ requireYesFlag(ctx, `add webhook notification URL ${url}`, '/notify add <url> --yes');
27
34
  return;
28
35
  }
29
36
  try { new URL(url); } catch {
@@ -42,9 +49,13 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
42
49
  }
43
50
 
44
51
  if (sub === 'remove') {
45
- const url = args[1];
52
+ const url = commandArgs[1];
46
53
  if (!url) {
47
- ctx.print('Usage: /notify remove <url>');
54
+ ctx.print('Usage: /notify remove <url> --yes');
55
+ return;
56
+ }
57
+ if (!parsed.yes) {
58
+ requireYesFlag(ctx, `remove webhook notification URL ${url}`, '/notify remove <url> --yes');
48
59
  return;
49
60
  }
50
61
  const next = urls.filter((u) => u !== url);
@@ -59,6 +70,10 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
59
70
  }
60
71
 
61
72
  if (sub === 'clear') {
73
+ if (!parsed.yes) {
74
+ requireYesFlag(ctx, 'clear all webhook notification URLs', '/notify clear --yes');
75
+ return;
76
+ }
62
77
  ctx.platform.configManager.mergeCategory('notifications', { webhookUrls: [] });
63
78
  notifier.setUrls([]);
64
79
  ctx.print('All webhook URLs cleared.');
@@ -66,6 +81,10 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
66
81
  }
67
82
 
68
83
  if (sub === 'test') {
84
+ if (!parsed.yes) {
85
+ requireYesFlag(ctx, 'send webhook notification test requests', '/notify test --yes');
86
+ return;
87
+ }
69
88
  if (urls.length === 0) {
70
89
  ctx.print('No webhook URLs configured. Use: /notify add <url>');
71
90
  return;
@@ -77,7 +96,7 @@ export function registerNotifyRuntimeCommands(registry: CommandRegistry): void {
77
96
  return;
78
97
  }
79
98
 
80
- ctx.print('Usage: /notify add <url> | remove <url> | list | clear | test');
99
+ ctx.print('Usage: /notify add <url> --yes | remove <url> --yes | list | clear --yes | test --yes');
81
100
  },
82
101
  });
83
102
  }
@@ -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;