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