@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.
- package/CHANGELOG.md +32 -0
- package/package.json +1 -1
- package/src/cli/agent-knowledge-command.ts +30 -3
- package/src/cli/help.ts +2 -2
- package/src/input/commands/cloudflare-runtime.ts +20 -5
- package/src/input/commands/confirmation.ts +24 -0
- package/src/input/commands/discovery-runtime.ts +16 -7
- package/src/input/commands/eval.ts +27 -14
- package/src/input/commands/experience-runtime.ts +65 -26
- package/src/input/commands/health-runtime.ts +1 -1
- package/src/input/commands/hooks-runtime.ts +50 -19
- package/src/input/commands/incident-runtime.ts +17 -6
- package/src/input/commands/integration-runtime.ts +93 -50
- package/src/input/commands/knowledge.ts +38 -12
- package/src/input/commands/local-auth-runtime.ts +36 -13
- package/src/input/commands/local-provider-runtime.ts +22 -11
- package/src/input/commands/local-runtime.ts +21 -11
- package/src/input/commands/local-setup.ts +35 -16
- package/src/input/commands/managed-runtime.ts +51 -20
- package/src/input/commands/marketplace-runtime.ts +31 -16
- package/src/input/commands/mcp-runtime.ts +65 -34
- package/src/input/commands/memory-product-runtime.ts +72 -35
- package/src/input/commands/memory.ts +9 -9
- package/src/input/commands/notify-runtime.ts +27 -8
- package/src/input/commands/operator-runtime.ts +85 -17
- package/src/input/commands/planning-runtime.ts +14 -2
- package/src/input/commands/platform-access-runtime.ts +88 -45
- package/src/input/commands/platform-services-runtime.ts +51 -25
- package/src/input/commands/product-runtime.ts +54 -27
- package/src/input/commands/profile-sync-runtime.ts +17 -6
- package/src/input/commands/recall-bundle.ts +38 -17
- package/src/input/commands/recall-query.ts +15 -4
- package/src/input/commands/recall-review.ts +9 -3
- package/src/input/commands/remote-runtime-setup.ts +45 -18
- package/src/input/commands/remote-runtime.ts +25 -9
- package/src/input/commands/replay-runtime.ts +9 -2
- package/src/input/commands/services-runtime.ts +21 -10
- package/src/input/commands/session-content.ts +53 -51
- package/src/input/commands/session-workflow.ts +10 -4
- package/src/input/commands/session.ts +1 -1
- package/src/input/commands/settings-sync-runtime.ts +40 -17
- package/src/input/commands/share-runtime.ts +12 -4
- package/src/input/commands/shell-core.ts +3 -3
- package/src/input/commands/subscription-runtime.ts +35 -20
- package/src/input/commands/teleport-runtime.ts +16 -5
- package/src/input/commands/work-plan-runtime.ts +23 -12
- package/src/input/handler-content-actions.ts +11 -62
- package/src/input/handler-interactions.ts +1 -1
- package/src/input/handler-onboarding-cloudflare.ts +48 -117
- package/src/input/keybindings.ts +1 -1
- package/src/input/mcp-workspace.ts +25 -49
- package/src/input/onboarding/onboarding-wizard-cloudflare-step.ts +8 -8
- package/src/input/onboarding/onboarding-wizard-cloudflare.ts +1 -6
- package/src/input/profile-picker-modal.ts +13 -31
- package/src/input/session-picker-modal.ts +4 -30
- package/src/input/settings-modal-subscriptions.ts +3 -3
- package/src/panels/incident-review-panel.ts +1 -1
- package/src/panels/local-auth-panel.ts +4 -4
- package/src/panels/provider-account-snapshot.ts +1 -1
- package/src/panels/provider-health-domains.ts +2 -2
- package/src/panels/settings-sync-panel.ts +2 -2
- package/src/panels/subscription-panel.ts +7 -7
- package/src/renderer/block-actions.ts +1 -1
- package/src/renderer/help-overlay.ts +2 -2
- package/src/renderer/mcp-workspace.ts +12 -12
- package/src/renderer/profile-picker-modal.ts +3 -11
- package/src/renderer/session-picker-modal.ts +2 -10
- package/src/verification/live-verifier.ts +100 -68
- package/src/version.ts +1 -1
|
@@ -4,6 +4,7 @@ import { AGENT_TEMPLATES } from '@pellux/goodvibes-sdk/platform/tools';
|
|
|
4
4
|
import { handleRemoteSetupCommand } from './remote-runtime-setup.ts';
|
|
5
5
|
import { handleRemotePoolCommand } from './remote-runtime-pool.ts';
|
|
6
6
|
import { requirePeerClient } from './runtime-services.ts';
|
|
7
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
7
8
|
|
|
8
9
|
type RemoteConnectionLike = { agentId: string };
|
|
9
10
|
type RemoteCancelContext = Pick<CommandContext, 'print'>;
|
|
@@ -45,7 +46,7 @@ export function registerRemoteRuntimeCommands(registry: CommandRegistry): void {
|
|
|
45
46
|
name: 'remote',
|
|
46
47
|
aliases: [],
|
|
47
48
|
description: 'Inspect, dispatch, and review self-hosted remote runners and artifacts',
|
|
48
|
-
usage: '[list | show [agentId] | supervisor [runnerId] | capabilities [runnerId] | recover [runnerId] | setup [export <path>] | env [export <path>] | tunnel [review|export <path>] | bootstrap [export <path
|
|
49
|
+
usage: '[list | show [agentId] | supervisor [runnerId] | capabilities [runnerId] | recover [runnerId] | setup [export <path> --yes] | env [export <path> --yes] | tunnel [review|export <path> --yes] | bootstrap [export <path> --yes|inspect <path>] | session <export|inspect|import> <path> [--yes] | pool <list|show|create|assign|unassign> ... | dispatch [template] <description> | dispatch-pool <pool> [template] <description> | contract [agentId] | cancel <agentId> | export <agentId> [path] --yes | artifact list | artifact show <id> | artifact export <id> [path] --yes | review <id> | rerun-local <id> | import <path> --yes]',
|
|
49
50
|
async handler(args, ctx) {
|
|
50
51
|
if (args.length === 0) {
|
|
51
52
|
if (ctx.openRemotePanel) {
|
|
@@ -310,9 +311,14 @@ export function registerRemoteRuntimeCommands(registry: CommandRegistry): void {
|
|
|
310
311
|
}
|
|
311
312
|
|
|
312
313
|
if (subcommand === 'export') {
|
|
313
|
-
const
|
|
314
|
+
const { rest, yes } = stripYesFlag(args);
|
|
315
|
+
const agentId = rest[1];
|
|
314
316
|
if (!agentId) {
|
|
315
|
-
ctx.print('Usage: /remote export <agentId> [path]');
|
|
317
|
+
ctx.print('Usage: /remote export <agentId> [path] --yes');
|
|
318
|
+
return;
|
|
319
|
+
}
|
|
320
|
+
if (!yes) {
|
|
321
|
+
requireYesFlag(ctx, `export remote review artifact for ${agentId}`, '/remote export <agentId> [path] --yes');
|
|
316
322
|
return;
|
|
317
323
|
}
|
|
318
324
|
const artifact = remoteRunners.captureArtifactForRunner(agentId);
|
|
@@ -320,7 +326,7 @@ export function registerRemoteRuntimeCommands(registry: CommandRegistry): void {
|
|
|
320
326
|
ctx.print(`Remote artifact export failed for ${agentId}.`);
|
|
321
327
|
return;
|
|
322
328
|
}
|
|
323
|
-
const exported = await remoteRunners.exportArtifact(artifact.id,
|
|
329
|
+
const exported = await remoteRunners.exportArtifact(artifact.id, rest[2]);
|
|
324
330
|
if (!exported) {
|
|
325
331
|
ctx.print(`Remote artifact export failed for ${agentId}.`);
|
|
326
332
|
return;
|
|
@@ -356,12 +362,17 @@ export function registerRemoteRuntimeCommands(registry: CommandRegistry): void {
|
|
|
356
362
|
return;
|
|
357
363
|
}
|
|
358
364
|
if (mode === 'export') {
|
|
359
|
-
const
|
|
365
|
+
const { rest, yes } = stripYesFlag(args);
|
|
366
|
+
const artifactId = rest[2];
|
|
360
367
|
if (!artifactId) {
|
|
361
|
-
ctx.print('Usage: /remote artifact export <artifactId> [path]');
|
|
368
|
+
ctx.print('Usage: /remote artifact export <artifactId> [path] --yes');
|
|
362
369
|
return;
|
|
363
370
|
}
|
|
364
|
-
|
|
371
|
+
if (!yes) {
|
|
372
|
+
requireYesFlag(ctx, `export remote review artifact ${artifactId}`, '/remote artifact export <artifactId> [path] --yes');
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const exported = await remoteRunners.exportArtifact(artifactId, rest[3]);
|
|
365
376
|
if (!exported) {
|
|
366
377
|
ctx.print(`Unknown remote artifact: ${artifactId}`);
|
|
367
378
|
return;
|
|
@@ -400,9 +411,14 @@ export function registerRemoteRuntimeCommands(registry: CommandRegistry): void {
|
|
|
400
411
|
}
|
|
401
412
|
|
|
402
413
|
if (subcommand === 'import') {
|
|
403
|
-
const
|
|
414
|
+
const { rest, yes } = stripYesFlag(args);
|
|
415
|
+
const path = rest[1];
|
|
404
416
|
if (!path) {
|
|
405
|
-
ctx.print('Usage: /remote import <path>');
|
|
417
|
+
ctx.print('Usage: /remote import <path> --yes');
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
if (!yes) {
|
|
421
|
+
requireYesFlag(ctx, `import remote review artifact from ${path}`, '/remote import <path> --yes');
|
|
406
422
|
return;
|
|
407
423
|
}
|
|
408
424
|
const artifact = await remoteRunners.importArtifact(path);
|
|
@@ -1,17 +1,24 @@
|
|
|
1
1
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
2
|
import { handleReplayCommand } from '@pellux/goodvibes-sdk/platform/core';
|
|
3
3
|
import { requireReplayEngine } from './runtime-services.ts';
|
|
4
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
4
5
|
|
|
5
6
|
export function registerReplayRuntimeCommands(registry: CommandRegistry): void {
|
|
6
7
|
registry.register({
|
|
7
8
|
name: 'replay',
|
|
8
9
|
aliases: ['rep'],
|
|
9
10
|
description: 'Deterministic replay: load, step, seek, diff, and export recorded runs',
|
|
10
|
-
usage: '[load [runId] | step [n] | seek <rev> | diff | export <path>]',
|
|
11
|
+
usage: '[load [runId] | step [n] | seek <rev> | diff | export <path> --yes]',
|
|
11
12
|
argsHint: '[load|step|seek|diff|export]',
|
|
12
13
|
handler(args, ctx) {
|
|
14
|
+
const command = args[0] ?? 'help';
|
|
15
|
+
const { rest, yes } = stripYesFlag(args);
|
|
16
|
+
if (command === 'export' && !yes) {
|
|
17
|
+
requireYesFlag(ctx, `export replay run to ${args[1] ?? '<path>'}`, '/replay export <path> --yes');
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
13
20
|
const replayEngine = requireReplayEngine(ctx);
|
|
14
|
-
const result = handleReplayCommand({ replayEngine },
|
|
21
|
+
const result = handleReplayCommand({ replayEngine }, command, rest.slice(1));
|
|
15
22
|
ctx.print(result.output);
|
|
16
23
|
},
|
|
17
24
|
});
|
|
@@ -5,15 +5,18 @@ import type { SelectionAction, SelectionItem } from '../selection-modal.ts';
|
|
|
5
5
|
import { openCommandPanel, requireServiceRegistry, requireShellPaths } from './runtime-services.ts';
|
|
6
6
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
7
7
|
import { GOODVIBES_AGENT_SURFACE_ROOT } from '../../config/surface.ts';
|
|
8
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
8
9
|
|
|
9
10
|
export function registerServicesRuntimeCommands(registry: CommandRegistry): void {
|
|
10
11
|
registry.register({
|
|
11
12
|
name: 'services',
|
|
12
13
|
aliases: ['svc'],
|
|
13
14
|
description: 'Manage API service configurations',
|
|
14
|
-
usage: '[open|list|inspect <name>|test <name>|resolve <name>|auth <name>|auth-review|doctor|export <path
|
|
15
|
+
usage: '[open|list|inspect <name>|test <name>|resolve <name>|auth <name>|auth-review|doctor|export <path> --yes|import <path> --yes]',
|
|
15
16
|
async handler(args, ctx) {
|
|
16
|
-
const
|
|
17
|
+
const parsed = stripYesFlag(args);
|
|
18
|
+
const commandArgs = [...parsed.rest];
|
|
19
|
+
const sub = commandArgs[0] ?? 'open';
|
|
17
20
|
const shellPaths = requireShellPaths(ctx);
|
|
18
21
|
if (sub === 'open' || sub === 'panel') {
|
|
19
22
|
openCommandPanel(ctx, 'services');
|
|
@@ -23,7 +26,7 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
|
|
|
23
26
|
const all = svcRegistry.getAll();
|
|
24
27
|
const keys = Object.keys(all);
|
|
25
28
|
if (sub === 'inspect') {
|
|
26
|
-
const name =
|
|
29
|
+
const name = commandArgs[1];
|
|
27
30
|
if (!name) {
|
|
28
31
|
ctx.print('Usage: /services inspect <name>');
|
|
29
32
|
return;
|
|
@@ -47,7 +50,7 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
|
|
|
47
50
|
return;
|
|
48
51
|
}
|
|
49
52
|
if (sub === 'test') {
|
|
50
|
-
const name =
|
|
53
|
+
const name = commandArgs[1];
|
|
51
54
|
if (!name) {
|
|
52
55
|
ctx.print('Usage: /services test <name>');
|
|
53
56
|
return;
|
|
@@ -63,7 +66,7 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
|
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
65
68
|
if (sub === 'resolve') {
|
|
66
|
-
const name =
|
|
69
|
+
const name = commandArgs[1];
|
|
67
70
|
if (!name) {
|
|
68
71
|
ctx.print('Usage: /services resolve <name>');
|
|
69
72
|
return;
|
|
@@ -80,7 +83,7 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
|
|
|
80
83
|
return;
|
|
81
84
|
}
|
|
82
85
|
if (sub === 'auth') {
|
|
83
|
-
const name =
|
|
86
|
+
const name = commandArgs[1];
|
|
84
87
|
if (!name) {
|
|
85
88
|
ctx.print('Usage: /services auth <name>');
|
|
86
89
|
return;
|
|
@@ -141,9 +144,13 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
|
|
|
141
144
|
return;
|
|
142
145
|
}
|
|
143
146
|
if (sub === 'export') {
|
|
144
|
-
const pathArg =
|
|
147
|
+
const pathArg = commandArgs[1];
|
|
145
148
|
if (!pathArg) {
|
|
146
|
-
ctx.print('Usage: /services export <path>');
|
|
149
|
+
ctx.print('Usage: /services export <path> --yes');
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
if (!parsed.yes) {
|
|
153
|
+
requireYesFlag(ctx, `export services config to ${pathArg}`, '/services export <path> --yes');
|
|
147
154
|
return;
|
|
148
155
|
}
|
|
149
156
|
const targetPath = shellPaths.resolveWorkspacePath(pathArg);
|
|
@@ -153,9 +160,13 @@ export function registerServicesRuntimeCommands(registry: CommandRegistry): void
|
|
|
153
160
|
return;
|
|
154
161
|
}
|
|
155
162
|
if (sub === 'import') {
|
|
156
|
-
const pathArg =
|
|
163
|
+
const pathArg = commandArgs[1];
|
|
157
164
|
if (!pathArg) {
|
|
158
|
-
ctx.print('Usage: /services import <path>');
|
|
165
|
+
ctx.print('Usage: /services import <path> --yes');
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (!parsed.yes) {
|
|
169
|
+
requireYesFlag(ctx, `import services config from ${pathArg}`, '/services import <path> --yes');
|
|
159
170
|
return;
|
|
160
171
|
}
|
|
161
172
|
const sourcePath = shellPaths.resolveWorkspacePath(pathArg);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join, resolve } from 'path';
|
|
2
|
-
import { existsSync, mkdirSync
|
|
2
|
+
import { existsSync, mkdirSync } from 'node:fs';
|
|
3
3
|
import { writeFile } from 'node:fs/promises';
|
|
4
4
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
5
5
|
import type { SelectionItem } from '../selection-modal.ts';
|
|
@@ -7,18 +7,20 @@ import { exportToMarkdown } from '@pellux/goodvibes-sdk/platform/export';
|
|
|
7
7
|
import { TemplateManager, parseTemplateArgs } from '@pellux/goodvibes-sdk/platform/templates';
|
|
8
8
|
import { requireSessionManager, requireSessionMemoryStore, requireShellPaths } from './runtime-services.ts';
|
|
9
9
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
10
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
10
11
|
|
|
11
12
|
export function registerSessionContentCommands(registry: CommandRegistry): void {
|
|
12
13
|
registry.register({
|
|
13
14
|
name: 'export',
|
|
14
15
|
description: 'Export conversation to a Markdown file',
|
|
15
|
-
usage: '[format] [path]',
|
|
16
|
+
usage: '[format] [path] --yes',
|
|
16
17
|
argsHint: '[markdown] [path]',
|
|
17
18
|
async handler(args, ctx) {
|
|
18
19
|
const shellPaths = requireShellPaths(ctx);
|
|
20
|
+
const { rest, yes } = stripYesFlag(args);
|
|
19
21
|
let format = 'markdown';
|
|
20
22
|
let outPath: string | undefined;
|
|
21
|
-
for (const arg of
|
|
23
|
+
for (const arg of rest) {
|
|
22
24
|
if (arg === 'markdown' || arg === 'md' || arg === 'text' || arg === 'txt') {
|
|
23
25
|
format = arg === 'md' ? 'markdown' : arg === 'txt' ? 'text' : arg;
|
|
24
26
|
} else {
|
|
@@ -34,6 +36,10 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
34
36
|
ctx.print('Error: Export path must be within the current directory.');
|
|
35
37
|
return;
|
|
36
38
|
}
|
|
39
|
+
if (!yes) {
|
|
40
|
+
requireYesFlag(ctx, `export conversation to ${resolvedPath}`, '/export [format] [path] --yes');
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
37
43
|
|
|
38
44
|
try {
|
|
39
45
|
const data = ctx.session.conversationManager.toJSON() as { messages: Array<Record<string, unknown>> };
|
|
@@ -229,34 +235,21 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
229
235
|
const sessionManager = requireSessionManager(ctx);
|
|
230
236
|
const sessions = sessionManager.list();
|
|
231
237
|
if (ctx.openSelection) {
|
|
232
|
-
const deleteAction = new Map([['d', 'delete' as const]]);
|
|
233
238
|
const items: SelectionItem[] = sessions.length === 0
|
|
234
239
|
? [{ id: '_empty', label: 'No saved sessions', detail: 'Use /save [name] to save' }]
|
|
235
|
-
: sessions.map(s => ({ id: s.name, label: s.name, detail: s.title || '(untitled)', actions: '
|
|
236
|
-
ctx.openSelection('Sessions', items, { allowSearch: true
|
|
240
|
+
: sessions.map(s => ({ id: s.name, label: s.name, detail: s.title || '(untitled)', actions: 'Enter to load' }));
|
|
241
|
+
ctx.openSelection('Sessions', items, { allowSearch: true }, (result) => {
|
|
237
242
|
if (!result) return;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
}
|
|
248
|
-
} else {
|
|
249
|
-
try {
|
|
250
|
-
const { meta, messages } = sessionManager.load(result.item.id);
|
|
251
|
-
ctx.session.conversationManager.resetAll();
|
|
252
|
-
ctx.session.conversationManager.fromJSON({ messages: messages as never[] });
|
|
253
|
-
if (meta.title) ctx.session.conversationManager.title = meta.title;
|
|
254
|
-
ctx.session.conversationManager.rebuildHistory();
|
|
255
|
-
ctx.renderRequest();
|
|
256
|
-
ctx.print(`Session loaded: ${result.item.id} (${messages.length} messages)`);
|
|
257
|
-
} catch (e) {
|
|
258
|
-
ctx.print(`Failed to load session: ${summarizeError(e)}`);
|
|
259
|
-
}
|
|
243
|
+
try {
|
|
244
|
+
const { meta, messages } = sessionManager.load(result.item.id);
|
|
245
|
+
ctx.session.conversationManager.resetAll();
|
|
246
|
+
ctx.session.conversationManager.fromJSON({ messages: messages as never[] });
|
|
247
|
+
if (meta.title) ctx.session.conversationManager.title = meta.title;
|
|
248
|
+
ctx.session.conversationManager.rebuildHistory();
|
|
249
|
+
ctx.renderRequest();
|
|
250
|
+
ctx.print(`Session loaded: ${result.item.id} (${messages.length} messages)`);
|
|
251
|
+
} catch (e) {
|
|
252
|
+
ctx.print(`Failed to load session: ${summarizeError(e)}`);
|
|
260
253
|
}
|
|
261
254
|
});
|
|
262
255
|
return;
|
|
@@ -271,7 +264,7 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
271
264
|
name: 'template',
|
|
272
265
|
aliases: ['tmpl'],
|
|
273
266
|
description: 'Manage and use prompt templates',
|
|
274
|
-
usage: 'save <name> | use <name> [args] | list | edit <name> | delete <name>',
|
|
267
|
+
usage: 'save <name> --yes | use <name> [args] | list | edit <name> | delete <name> --yes',
|
|
275
268
|
argsHint: '<save|use|list|edit|delete> [name]',
|
|
276
269
|
handler(args, ctx) {
|
|
277
270
|
const shellPaths = requireShellPaths(ctx);
|
|
@@ -279,28 +272,24 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
279
272
|
projectRoot: shellPaths.workingDirectory,
|
|
280
273
|
homeDirectory: shellPaths.homeDirectory,
|
|
281
274
|
});
|
|
282
|
-
const
|
|
283
|
-
const
|
|
275
|
+
const parsed = stripYesFlag(args);
|
|
276
|
+
const sub = parsed.rest[0];
|
|
277
|
+
const rest = parsed.rest.slice(1);
|
|
284
278
|
if (!sub || sub === 'list') {
|
|
285
279
|
const templates = templateManager.list();
|
|
286
280
|
if (ctx.openSelection) {
|
|
287
|
-
const actions = new Map([['
|
|
281
|
+
const actions = new Map([['e', 'edit' as const]]);
|
|
288
282
|
const items: SelectionItem[] = templates.length === 0
|
|
289
|
-
? [{ id: '_empty', label: 'No templates saved', detail: 'Use /template save <name>' }]
|
|
290
|
-
: templates.map(t => ({ id: t.name, label: t.name, detail: t.preview, category: t.scope === 'project' ? 'project' : 'global', actions: '[
|
|
283
|
+
? [{ id: '_empty', label: 'No templates saved', detail: 'Use /template save <name> --yes' }]
|
|
284
|
+
: templates.map(t => ({ id: t.name, label: t.name, detail: t.preview, category: t.scope === 'project' ? 'project' : 'global', actions: '[e] edit' }));
|
|
291
285
|
ctx.openSelection('Templates', items, { allowSearch: true, customActions: actions }, (result) => {
|
|
292
286
|
if (!result) return;
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
ctx.print(
|
|
287
|
+
const content = templateManager.load(result.item.id);
|
|
288
|
+
if (content !== null) {
|
|
289
|
+
if (result.action === 'edit') ctx.print(`Template: ${result.item.id}\n\n${content}`);
|
|
290
|
+
else ctx.submitInput?.(content);
|
|
296
291
|
} else {
|
|
297
|
-
|
|
298
|
-
if (content !== null) {
|
|
299
|
-
if (result.action === 'edit') ctx.print(`Template: ${result.item.id}\n\n${content}`);
|
|
300
|
-
else ctx.submitInput?.(content);
|
|
301
|
-
} else {
|
|
302
|
-
ctx.print(`Template not found: ${result.item.id}`);
|
|
303
|
-
}
|
|
292
|
+
ctx.print(`Template not found: ${result.item.id}`);
|
|
304
293
|
}
|
|
305
294
|
});
|
|
306
295
|
return;
|
|
@@ -311,7 +300,11 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
311
300
|
if (sub === 'save') {
|
|
312
301
|
const name = rest[0];
|
|
313
302
|
if (!name) {
|
|
314
|
-
ctx.print('Usage: /template save <name>');
|
|
303
|
+
ctx.print('Usage: /template save <name> --yes');
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (!parsed.yes) {
|
|
307
|
+
requireYesFlag(ctx, `save prompt template ${name}`, '/template save <name> --yes');
|
|
315
308
|
return;
|
|
316
309
|
}
|
|
317
310
|
try {
|
|
@@ -349,20 +342,24 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
349
342
|
if (sub === 'delete') {
|
|
350
343
|
const name = rest[0];
|
|
351
344
|
if (!name) {
|
|
352
|
-
ctx.print('Usage: /template delete <name>');
|
|
345
|
+
ctx.print('Usage: /template delete <name> --yes');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
if (!parsed.yes) {
|
|
349
|
+
requireYesFlag(ctx, `delete prompt template ${name}`, '/template delete <name> --yes');
|
|
353
350
|
return;
|
|
354
351
|
}
|
|
355
352
|
ctx.print(templateManager.delete(name) ? `Template deleted: ${name}` : `Template not found: ${name}`);
|
|
356
353
|
return;
|
|
357
354
|
}
|
|
358
|
-
ctx.print(`Unknown subcommand: ${sub}\nUsage: /template save|use|list|edit|delete`);
|
|
355
|
+
ctx.print(`Unknown subcommand: ${sub}\nUsage: /template save <name> --yes | use <name> | list | edit <name> | delete <name> --yes`);
|
|
359
356
|
},
|
|
360
357
|
});
|
|
361
358
|
|
|
362
359
|
registry.register({
|
|
363
360
|
name: 'memory',
|
|
364
361
|
description: 'Manage session memories (pinned across context compaction)',
|
|
365
|
-
usage: '[list|add <text>|remove <id>]',
|
|
362
|
+
usage: '[list|add <text>|remove <id> --yes]',
|
|
366
363
|
argsHint: '[list|add|remove]',
|
|
367
364
|
handler(args, ctx) {
|
|
368
365
|
const sub = args[0] ?? 'list';
|
|
@@ -380,15 +377,20 @@ export function registerSessionContentCommands(registry: CommandRegistry): void
|
|
|
380
377
|
const id = requireSessionMemoryStore(ctx).add(text);
|
|
381
378
|
ctx.print(`Memory added: [${id}] ${text}`);
|
|
382
379
|
} else if (sub === 'remove') {
|
|
383
|
-
const
|
|
380
|
+
const parsed = stripYesFlag(args);
|
|
381
|
+
const id = parsed.rest[1];
|
|
384
382
|
if (!id) {
|
|
385
|
-
ctx.print('Usage: /memory remove <id>');
|
|
383
|
+
ctx.print('Usage: /memory remove <id> --yes');
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
if (!parsed.yes) {
|
|
387
|
+
requireYesFlag(ctx, `remove session memory ${id}`, '/memory remove <id> --yes');
|
|
386
388
|
return;
|
|
387
389
|
}
|
|
388
390
|
const store = requireSessionMemoryStore(ctx);
|
|
389
391
|
ctx.print(store.remove(id) ? `Memory removed: [${id}]` : `Memory not found: ${id}`);
|
|
390
392
|
} else {
|
|
391
|
-
ctx.print('Usage: /memory [list|add <text>|remove <id>]\n /memory
|
|
393
|
+
ctx.print('Usage: /memory [list|add <text>|remove <id> --yes]\n /memory — list all session memories\n /memory list — list all session memories\n /memory add <text> — add a memory without sending a message\n /memory remove <id> --yes — remove a specific memory');
|
|
392
394
|
}
|
|
393
395
|
},
|
|
394
396
|
});
|
|
@@ -8,6 +8,7 @@ import type { SessionReturnContextSummary } from '@/runtime/index.ts';
|
|
|
8
8
|
import { formatReturnContextForDisplay, getReturnContextMode, maybeAssistReturnContextSummary } from '@/runtime/index.ts';
|
|
9
9
|
import { requirePanelManager, requireProviderApi, requireSessionManager } from './runtime-services.ts';
|
|
10
10
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
11
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
11
12
|
|
|
12
13
|
function parseTranscriptKind(raw: string | undefined): TranscriptEventKind | 'all' {
|
|
13
14
|
const normalized = (raw ?? 'all').toLowerCase().replace(/-/g, '_');
|
|
@@ -414,9 +415,14 @@ export async function handleSessionWorkflowCommand(args: string[], ctx: CommandC
|
|
|
414
415
|
}
|
|
415
416
|
|
|
416
417
|
if (sub === 'delete') {
|
|
417
|
-
const
|
|
418
|
+
const parsed = stripYesFlag(args);
|
|
419
|
+
const target = parsed.rest[1];
|
|
418
420
|
if (!target) {
|
|
419
|
-
ctx.print('Usage: /session delete <session-id>');
|
|
421
|
+
ctx.print('Usage: /session delete <session-id> --yes');
|
|
422
|
+
return true;
|
|
423
|
+
}
|
|
424
|
+
if (!parsed.yes) {
|
|
425
|
+
requireYesFlag(ctx, `delete saved session ${target}`, '/session delete <session-id> --yes');
|
|
420
426
|
return true;
|
|
421
427
|
}
|
|
422
428
|
const sessions = sm.list();
|
|
@@ -446,12 +452,12 @@ export function registerSessionWorkflowCommands(registry: CommandRegistry): void
|
|
|
446
452
|
name: 'session',
|
|
447
453
|
aliases: ['sess'],
|
|
448
454
|
description: 'Manage sessions, resume posture, and transcript structure',
|
|
449
|
-
usage: '[list | rename <name> | resume <id|name> | fork | save | info <id> | events [kind] | groups [kind] | hotspots | export <id> [format] | search <query> | delete <id>]',
|
|
455
|
+
usage: '[list | rename <name> | resume <id|name> | fork | save | info <id> | events [kind] | groups [kind] | hotspots | export <id> [format] | search <query> | delete <id> --yes]',
|
|
450
456
|
argsHint: '<list|rename|resume|fork|save|info|events|groups|hotspots|export|search|delete>',
|
|
451
457
|
async handler(args, ctx) {
|
|
452
458
|
const handled = await handleSessionWorkflowCommand(args, ctx);
|
|
453
459
|
if (!handled) {
|
|
454
|
-
ctx.print('Unknown subcommand: ' + (args[0] ?? '') + '\nUsage: /session [list | rename <name> | resume <id> | fork [name] | save [name] | info [id] | events [kind] | groups [kind] | hotspots | export <id> [format] | search <query> | delete <id>]');
|
|
460
|
+
ctx.print('Unknown subcommand: ' + (args[0] ?? '') + '\nUsage: /session [list | rename <name> | resume <id> | fork [name] | save [name] | info [id] | events [kind] | groups [kind] | hotspots | export <id> [format] | search <query> | delete <id> --yes]');
|
|
455
461
|
}
|
|
456
462
|
},
|
|
457
463
|
});
|
|
@@ -352,7 +352,7 @@ export const sessionCommand: SlashCommand = {
|
|
|
352
352
|
if (!handled) {
|
|
353
353
|
const usage = [
|
|
354
354
|
'Usage: /session <subcommand>',
|
|
355
|
-
' list | rename <name> | resume <id|name> | fork [name] | save [name] | info [id] | export <id> [format] | search <query> | delete <id>',
|
|
355
|
+
' list | rename <name> | resume <id|name> | fork [name] | save [name] | info [id] | export <id> [format] | search <query> | delete <id> --yes',
|
|
356
356
|
' — Session continuity, export, resume, and pruning',
|
|
357
357
|
' graph [--session <sid>] [--format text|json]',
|
|
358
358
|
' — Display the cross-session task dependency graph',
|
|
@@ -21,23 +21,26 @@ import { CONFIG_KEYS } from '@pellux/goodvibes-sdk/platform/config';
|
|
|
21
21
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
22
22
|
import { openCommandPanel, requireShellPaths } from './runtime-services.ts';
|
|
23
23
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
24
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
24
25
|
|
|
25
26
|
export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry): void {
|
|
26
27
|
registry.register({
|
|
27
28
|
name: 'settingssync',
|
|
28
29
|
aliases: ['settings-sync'],
|
|
29
30
|
description: 'Review sync posture, export/import settings-sync bundles, and open the settings sync workspace',
|
|
30
|
-
usage: '[review|panel|show <key>|staged|conflicts|resolve <key> <local|synced
|
|
31
|
+
usage: '[review|panel|show <key>|staged|conflicts|resolve <key> <local|synced> --yes|failures|rollback-history|export <path> --yes|inspect <path>|pull <path> --yes|push <path> --yes|lock <key> <source> <reason...> --yes|unlock <key> --yes]',
|
|
31
32
|
handler(args, ctx) {
|
|
33
|
+
const parsed = stripYesFlag(args);
|
|
34
|
+
const commandArgs = [...parsed.rest];
|
|
32
35
|
const shellPaths = requireShellPaths(ctx);
|
|
33
36
|
const controlPlaneConfigDir = ctx.platform.configManager.getControlPlaneConfigDir();
|
|
34
|
-
const sub = (
|
|
37
|
+
const sub = (commandArgs[0] ?? 'review').toLowerCase();
|
|
35
38
|
if (sub === 'panel' || sub === 'open') {
|
|
36
39
|
openCommandPanel(ctx, 'settings-sync');
|
|
37
40
|
return;
|
|
38
41
|
}
|
|
39
42
|
if (sub === 'show') {
|
|
40
|
-
const key =
|
|
43
|
+
const key = commandArgs[1] as ConfigKey | undefined;
|
|
41
44
|
if (!key || !CONFIG_KEYS.has(key)) {
|
|
42
45
|
ctx.print('Usage: /settingssync show <config-key>');
|
|
43
46
|
return;
|
|
@@ -60,10 +63,14 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
|
|
|
60
63
|
return;
|
|
61
64
|
}
|
|
62
65
|
if (sub === 'resolve') {
|
|
63
|
-
const key =
|
|
64
|
-
const resolution = (
|
|
66
|
+
const key = commandArgs[1] as ConfigKey | undefined;
|
|
67
|
+
const resolution = (commandArgs[2] ?? '').toLowerCase();
|
|
65
68
|
if (!key || !CONFIG_KEYS.has(key) || (resolution !== 'local' && resolution !== 'synced')) {
|
|
66
|
-
ctx.print('Usage: /settingssync resolve <config-key> <local|synced>');
|
|
69
|
+
ctx.print('Usage: /settingssync resolve <config-key> <local|synced> --yes');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
if (!parsed.yes) {
|
|
73
|
+
requireYesFlag(ctx, `resolve synced conflict for ${key}`, '/settingssync resolve <config-key> <local|synced> --yes');
|
|
67
74
|
return;
|
|
68
75
|
}
|
|
69
76
|
const changed = resolveSettingsSyncConflict(ctx.platform.configManager, key, resolution);
|
|
@@ -100,9 +107,13 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
|
|
|
100
107
|
return;
|
|
101
108
|
}
|
|
102
109
|
if (sub === 'export' || sub === 'push') {
|
|
103
|
-
const pathArg =
|
|
110
|
+
const pathArg = commandArgs[1];
|
|
104
111
|
if (!pathArg) {
|
|
105
|
-
ctx.print(`Usage: /settingssync ${sub} <path
|
|
112
|
+
ctx.print(`Usage: /settingssync ${sub} <path> --yes`);
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
if (!parsed.yes) {
|
|
116
|
+
requireYesFlag(ctx, `${sub === 'push' ? 'push' : 'export'} settings sync bundle to ${pathArg}`, `/settingssync ${sub} <path> --yes`);
|
|
106
117
|
return;
|
|
107
118
|
}
|
|
108
119
|
const targetPath = shellPaths.resolveWorkspacePath(pathArg);
|
|
@@ -120,7 +131,7 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
|
|
|
120
131
|
return;
|
|
121
132
|
}
|
|
122
133
|
if (sub === 'inspect') {
|
|
123
|
-
const pathArg =
|
|
134
|
+
const pathArg = commandArgs[1];
|
|
124
135
|
if (!pathArg) {
|
|
125
136
|
ctx.print('Usage: /settingssync inspect <path>');
|
|
126
137
|
return;
|
|
@@ -131,9 +142,13 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
|
|
|
131
142
|
return;
|
|
132
143
|
}
|
|
133
144
|
if (sub === 'pull') {
|
|
134
|
-
const pathArg =
|
|
145
|
+
const pathArg = commandArgs[1];
|
|
135
146
|
if (!pathArg) {
|
|
136
|
-
ctx.print('Usage: /settingssync pull <path>');
|
|
147
|
+
ctx.print('Usage: /settingssync pull <path> --yes');
|
|
148
|
+
return;
|
|
149
|
+
}
|
|
150
|
+
if (!parsed.yes) {
|
|
151
|
+
requireYesFlag(ctx, `pull settings sync bundle from ${pathArg}`, '/settingssync pull <path> --yes');
|
|
137
152
|
return;
|
|
138
153
|
}
|
|
139
154
|
const sourcePath = shellPaths.resolveWorkspacePath(pathArg);
|
|
@@ -148,11 +163,15 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
|
|
|
148
163
|
return;
|
|
149
164
|
}
|
|
150
165
|
if (sub === 'lock') {
|
|
151
|
-
const key =
|
|
152
|
-
const source =
|
|
153
|
-
const reason =
|
|
166
|
+
const key = commandArgs[1] as ConfigKey | undefined;
|
|
167
|
+
const source = commandArgs[2];
|
|
168
|
+
const reason = commandArgs.slice(3).join(' ').trim();
|
|
154
169
|
if (!key || !source || !reason || !CONFIG_KEYS.has(key)) {
|
|
155
|
-
ctx.print('Usage: /settingssync lock <config-key> <source> <reason...>');
|
|
170
|
+
ctx.print('Usage: /settingssync lock <config-key> <source> <reason...> --yes');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (!parsed.yes) {
|
|
174
|
+
requireYesFlag(ctx, `lock managed setting ${key}`, '/settingssync lock <config-key> <source> <reason...> --yes');
|
|
156
175
|
return;
|
|
157
176
|
}
|
|
158
177
|
setManagedSettingLock(key, source, reason, controlPlaneConfigDir);
|
|
@@ -160,9 +179,13 @@ export function registerSettingsSyncRuntimeCommands(registry: CommandRegistry):
|
|
|
160
179
|
return;
|
|
161
180
|
}
|
|
162
181
|
if (sub === 'unlock') {
|
|
163
|
-
const key =
|
|
182
|
+
const key = commandArgs[1] as ConfigKey | undefined;
|
|
164
183
|
if (!key || !CONFIG_KEYS.has(key)) {
|
|
165
|
-
ctx.print('Usage: /settingssync unlock <config-key>');
|
|
184
|
+
ctx.print('Usage: /settingssync unlock <config-key> --yes');
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
if (!parsed.yes) {
|
|
188
|
+
requireYesFlag(ctx, `unlock managed setting ${key}`, '/settingssync unlock <config-key> --yes');
|
|
166
189
|
return;
|
|
167
190
|
}
|
|
168
191
|
ctx.print(clearManagedSettingLock(key, controlPlaneConfigDir) ? `Managed lock cleared for ${key}.` : `No managed lock found for ${key}.`);
|
|
@@ -11,23 +11,26 @@ import {
|
|
|
11
11
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
12
12
|
import { requireShellPaths } from './runtime-services.ts';
|
|
13
13
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
14
|
+
import { requireYesFlag, stripYesFlag } from './confirmation.ts';
|
|
14
15
|
|
|
15
16
|
export function registerShareRuntimeCommands(registry: CommandRegistry): void {
|
|
16
17
|
registry.register({
|
|
17
18
|
name: 'share',
|
|
18
19
|
aliases: [],
|
|
19
20
|
description: 'Export the current session to a shareable format (html, json, md)',
|
|
20
|
-
usage: '<html|json|md> [path] [--redact]',
|
|
21
|
+
usage: '<html|json|md> [path] [--redact] --yes',
|
|
21
22
|
argsHint: '<html|json|md> [path]',
|
|
22
23
|
async handler(args, ctx) {
|
|
24
|
+
const parsed = stripYesFlag(args);
|
|
25
|
+
const commandArgs = [...parsed.rest];
|
|
23
26
|
const shellPaths = requireShellPaths(ctx);
|
|
24
27
|
const FORMATS = ['html', 'json', 'md'] as const;
|
|
25
28
|
type Format = typeof FORMATS[number];
|
|
26
29
|
|
|
27
|
-
const format =
|
|
30
|
+
const format = commandArgs[0]?.toLowerCase() as Format | undefined;
|
|
28
31
|
if (!format || !FORMATS.includes(format)) {
|
|
29
32
|
ctx.print(
|
|
30
|
-
'Usage: /share <html|json|md> [path] [--redact]\n'
|
|
33
|
+
'Usage: /share <html|json|md> [path] [--redact] --yes\n'
|
|
31
34
|
+ ' html — self-contained HTML with syntax highlighting\n'
|
|
32
35
|
+ ' json — structured JSON (machine-readable)\n'
|
|
33
36
|
+ ' md — Markdown\n\n'
|
|
@@ -38,13 +41,18 @@ export function registerShareRuntimeCommands(registry: CommandRegistry): void {
|
|
|
38
41
|
return;
|
|
39
42
|
}
|
|
40
43
|
|
|
41
|
-
const remainingArgs =
|
|
44
|
+
const remainingArgs = commandArgs.slice(1);
|
|
42
45
|
const redact = remainingArgs.includes('--redact');
|
|
43
46
|
const pathArgs = remainingArgs.filter(a => a !== '--redact');
|
|
44
47
|
const outputPath = pathArgs.length > 0
|
|
45
48
|
? shellPaths.resolveWorkspacePath(pathArgs[0])
|
|
46
49
|
: defaultExportPath(format, shellPaths.homeDirectory);
|
|
47
50
|
|
|
51
|
+
if (!parsed.yes) {
|
|
52
|
+
requireYesFlag(ctx, `export ${format.toUpperCase()} session to ${outputPath}`, '/share <html|json|md> [path] [--redact] --yes');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
48
56
|
const convData = ctx.session.conversationManager.toJSON() as {
|
|
49
57
|
messages: Array<{
|
|
50
58
|
role: string;
|