@pellux/goodvibes-agent 0.1.113 → 0.1.116
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 +19 -0
- package/README.md +4 -0
- package/dist/package/main.js +1659 -324
- package/docs/getting-started.md +4 -0
- package/package.json +1 -1
- package/src/agent/behavior-discovery-summary.ts +167 -0
- package/src/agent/persona-discovery.ts +117 -0
- package/src/agent/routine-discovery.ts +115 -0
- package/src/agent/runtime-profile-starters.ts +331 -0
- package/src/agent/runtime-profile.ts +150 -330
- package/src/cli/help.ts +11 -3
- package/src/cli/local-library-command.ts +81 -1
- package/src/cli/profiles-command.ts +128 -0
- package/src/cli/routines-command.ts +156 -1
- package/src/input/agent-workspace-basic-command-editor-submission.ts +534 -0
- package/src/input/agent-workspace-basic-command-editors.ts +77 -395
- package/src/input/agent-workspace-categories.ts +7 -0
- package/src/input/agent-workspace-command-editor.ts +4 -0
- package/src/input/agent-workspace-setup.ts +33 -13
- package/src/input/agent-workspace-snapshot.ts +6 -0
- package/src/input/agent-workspace-types.ts +6 -0
- package/src/input/commands/agent-runtime-profile-runtime.ts +73 -4
- package/src/input/commands/personas-runtime.ts +87 -2
- package/src/input/commands/routines-runtime.ts +87 -2
- package/src/input/onboarding/onboarding-wizard-operator-steps.ts +46 -9
- package/src/input/onboarding/onboarding-wizard-steps.ts +1 -1
- package/src/renderer/agent-workspace.ts +26 -0
- package/src/runtime/onboarding/derivation.ts +20 -1
- package/src/runtime/onboarding/snapshot.ts +2 -0
- package/src/runtime/onboarding/types.ts +2 -0
- package/src/version.ts +1 -1
|
@@ -1,36 +1,16 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
type AgentWorkspaceFieldReader = (fieldId: string) => string;
|
|
1
|
+
import type { AgentWorkspaceEditorKind, AgentWorkspaceLocalEditor } from './agent-workspace-types.ts';
|
|
2
|
+
export type { AgentWorkspaceBasicCommandEditorSubmission } from './agent-workspace-basic-command-editor-submission.ts';
|
|
3
|
+
export { buildAgentWorkspaceBasicCommandEditorSubmission } from './agent-workspace-basic-command-editor-submission.ts';
|
|
5
4
|
|
|
6
5
|
export type AgentWorkspaceBasicCommandEditorKind = Extract<
|
|
7
6
|
AgentWorkspaceEditorKind,
|
|
8
7
|
'knowledge-file' | 'knowledge-bookmarks' | 'knowledge-browser-history' | 'knowledge-connector-ingest' | 'tts-prompt' | 'image-input' | 'skill-bundle' | 'skill-discovery-import' | 'profile-template-export' | 'profile-template-import'
|
|
8
|
+
| 'profile-template-from-discovered' | 'profile-from-discovered'
|
|
9
|
+
| 'persona-discovery-import'
|
|
10
|
+
| 'routine-discovery-import'
|
|
9
11
|
| 'mcp-server' | 'notify-webhook' | 'notify-webhook-remove' | 'notify-webhook-test'
|
|
10
12
|
>;
|
|
11
13
|
|
|
12
|
-
export type AgentWorkspaceBasicCommandEditorSubmission =
|
|
13
|
-
| {
|
|
14
|
-
readonly kind: 'editor';
|
|
15
|
-
readonly editor: AgentWorkspaceLocalEditor;
|
|
16
|
-
readonly status: string;
|
|
17
|
-
readonly actionResult?: AgentWorkspaceActionResult;
|
|
18
|
-
}
|
|
19
|
-
| {
|
|
20
|
-
readonly kind: 'dispatch';
|
|
21
|
-
readonly command: string;
|
|
22
|
-
readonly status: string;
|
|
23
|
-
readonly actionResult: AgentWorkspaceActionResult;
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
function isAffirmative(value: string): boolean {
|
|
27
|
-
return /^(y|yes|true)$/i.test(value.trim());
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function splitCommaList(value: string): readonly string[] {
|
|
31
|
-
return value.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
14
|
export function isAgentWorkspaceBasicCommandEditorKind(kind: AgentWorkspaceEditorKind): kind is AgentWorkspaceBasicCommandEditorKind {
|
|
35
15
|
return kind === 'knowledge-bookmarks'
|
|
36
16
|
|| kind === 'knowledge-file'
|
|
@@ -39,9 +19,13 @@ export function isAgentWorkspaceBasicCommandEditorKind(kind: AgentWorkspaceEdito
|
|
|
39
19
|
|| kind === 'tts-prompt'
|
|
40
20
|
|| kind === 'image-input'
|
|
41
21
|
|| kind === 'skill-bundle'
|
|
22
|
+
|| kind === 'persona-discovery-import'
|
|
23
|
+
|| kind === 'routine-discovery-import'
|
|
42
24
|
|| kind === 'skill-discovery-import'
|
|
43
25
|
|| kind === 'profile-template-export'
|
|
44
26
|
|| kind === 'profile-template-import'
|
|
27
|
+
|| kind === 'profile-template-from-discovered'
|
|
28
|
+
|| kind === 'profile-from-discovered'
|
|
45
29
|
|| kind === 'mcp-server'
|
|
46
30
|
|| kind === 'notify-webhook'
|
|
47
31
|
|| kind === 'notify-webhook-remove'
|
|
@@ -222,6 +206,45 @@ export function createAgentWorkspaceBasicCommandEditor(kind: AgentWorkspaceBasic
|
|
|
222
206
|
],
|
|
223
207
|
};
|
|
224
208
|
}
|
|
209
|
+
if (kind === 'profile-template-from-discovered') {
|
|
210
|
+
return {
|
|
211
|
+
kind,
|
|
212
|
+
mode: 'create',
|
|
213
|
+
title: 'Create Starter from Discovered Behavior',
|
|
214
|
+
selectedFieldIndex: 0,
|
|
215
|
+
message: 'Create an Agent-local starter template from reviewed discovered persona, skill, and routine markdown. Type yes on the final field to confirm.',
|
|
216
|
+
fields: [
|
|
217
|
+
{ id: 'id', label: 'Starter id', value: '', required: true, multiline: false, hint: 'New local starter id, for example research-desk.' },
|
|
218
|
+
{ id: 'name', label: 'Starter name', value: '', required: false, multiline: false, hint: 'Optional display name. Defaults to the selected persona name.' },
|
|
219
|
+
{ id: 'description', label: 'Description', value: '', required: false, multiline: false, hint: 'Optional one-line summary.' },
|
|
220
|
+
{ id: 'persona', label: 'Persona', value: '', required: false, multiline: false, hint: 'Optional discovered persona name/path. Blank uses the first discovered persona.' },
|
|
221
|
+
{ id: 'skills', label: 'Skills', value: '', required: false, multiline: false, hint: 'all or comma-separated discovered skill names. Blank includes all.' },
|
|
222
|
+
{ id: 'routines', label: 'Routines', value: '', required: false, multiline: false, hint: 'all or comma-separated discovered routine names. Blank includes all.' },
|
|
223
|
+
{ id: 'replace', label: 'Replace existing', value: 'no', required: false, multiline: false, hint: 'yes/no. Existing starter ids are protected unless this is yes.' },
|
|
224
|
+
{ id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /agent-profile template from-discovered with --yes.' },
|
|
225
|
+
],
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
if (kind === 'profile-from-discovered') {
|
|
229
|
+
return {
|
|
230
|
+
kind,
|
|
231
|
+
mode: 'create',
|
|
232
|
+
title: 'Create Profile from Discovered Behavior',
|
|
233
|
+
selectedFieldIndex: 0,
|
|
234
|
+
message: 'Create a local starter template and isolated Agent profile from reviewed discovered behavior markdown. Type yes on the final field to confirm.',
|
|
235
|
+
fields: [
|
|
236
|
+
{ id: 'profile', label: 'Profile name', value: '', required: true, multiline: false, hint: 'New isolated Agent profile name, for example research-desk.' },
|
|
237
|
+
{ id: 'templateId', label: 'Starter id', value: '', required: false, multiline: false, hint: 'Optional local starter id. Blank uses the profile name.' },
|
|
238
|
+
{ id: 'name', label: 'Starter name', value: '', required: false, multiline: false, hint: 'Optional display name for the generated starter.' },
|
|
239
|
+
{ id: 'description', label: 'Description', value: '', required: false, multiline: false, hint: 'Optional one-line summary.' },
|
|
240
|
+
{ id: 'persona', label: 'Persona', value: '', required: false, multiline: false, hint: 'Optional discovered persona name/path. Blank uses the first discovered persona.' },
|
|
241
|
+
{ id: 'skills', label: 'Skills', value: '', required: false, multiline: false, hint: 'all or comma-separated discovered skill names. Blank includes all.' },
|
|
242
|
+
{ id: 'routines', label: 'Routines', value: '', required: false, multiline: false, hint: 'all or comma-separated discovered routine names. Blank includes all.' },
|
|
243
|
+
{ id: 'replace', label: 'Replace starter', value: 'no', required: false, multiline: false, hint: 'yes/no. Existing starter ids are protected unless this is yes.' },
|
|
244
|
+
{ id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /agent-profile create-from-discovered with --yes.' },
|
|
245
|
+
],
|
|
246
|
+
};
|
|
247
|
+
}
|
|
225
248
|
if (kind === 'skill-discovery-import') {
|
|
226
249
|
return {
|
|
227
250
|
kind,
|
|
@@ -236,6 +259,34 @@ export function createAgentWorkspaceBasicCommandEditor(kind: AgentWorkspaceBasic
|
|
|
236
259
|
],
|
|
237
260
|
};
|
|
238
261
|
}
|
|
262
|
+
if (kind === 'persona-discovery-import') {
|
|
263
|
+
return {
|
|
264
|
+
kind,
|
|
265
|
+
mode: 'create',
|
|
266
|
+
title: 'Import Discovered Persona',
|
|
267
|
+
selectedFieldIndex: 0,
|
|
268
|
+
message: 'Import one discovered persona markdown file into the Agent-local persona registry. Type yes on the final field to confirm.',
|
|
269
|
+
fields: [
|
|
270
|
+
{ id: 'name', label: 'Discovered persona', value: '', required: true, multiline: false, hint: 'Name shown by /personas discover.' },
|
|
271
|
+
{ id: 'use', label: 'Use now', value: 'yes', required: false, multiline: false, hint: 'yes/no.' },
|
|
272
|
+
{ id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /personas import-discovered with --yes.' },
|
|
273
|
+
],
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
if (kind === 'routine-discovery-import') {
|
|
277
|
+
return {
|
|
278
|
+
kind,
|
|
279
|
+
mode: 'create',
|
|
280
|
+
title: 'Import Discovered Routine',
|
|
281
|
+
selectedFieldIndex: 0,
|
|
282
|
+
message: 'Import one discovered routine markdown file into the Agent-local routine registry. Type yes on the final field to confirm.',
|
|
283
|
+
fields: [
|
|
284
|
+
{ id: 'name', label: 'Discovered routine', value: '', required: true, multiline: false, hint: 'Name shown by /routines discover.' },
|
|
285
|
+
{ id: 'enabled', label: 'Enable now', value: 'yes', required: false, multiline: false, hint: 'yes/no.' },
|
|
286
|
+
{ id: 'confirm', label: 'Confirm', value: '', required: true, multiline: false, hint: 'Type yes to run /routines import-discovered with --yes.' },
|
|
287
|
+
],
|
|
288
|
+
};
|
|
289
|
+
}
|
|
239
290
|
return {
|
|
240
291
|
kind,
|
|
241
292
|
mode: 'create',
|
|
@@ -250,372 +301,3 @@ export function createAgentWorkspaceBasicCommandEditor(kind: AgentWorkspaceBasic
|
|
|
250
301
|
],
|
|
251
302
|
};
|
|
252
303
|
}
|
|
253
|
-
|
|
254
|
-
export function buildAgentWorkspaceBasicCommandEditorSubmission(
|
|
255
|
-
editor: AgentWorkspaceLocalEditor,
|
|
256
|
-
readField: AgentWorkspaceFieldReader,
|
|
257
|
-
commandDispatchAvailable: boolean,
|
|
258
|
-
): AgentWorkspaceBasicCommandEditorSubmission {
|
|
259
|
-
if (!commandDispatchAvailable) {
|
|
260
|
-
return {
|
|
261
|
-
kind: 'editor',
|
|
262
|
-
editor: { ...editor, message: 'Command dispatch is unavailable; this action cannot run from this workspace.' },
|
|
263
|
-
status: 'Command dispatch unavailable.',
|
|
264
|
-
actionResult: {
|
|
265
|
-
kind: 'error',
|
|
266
|
-
title: 'Command dispatch unavailable',
|
|
267
|
-
detail: 'The Agent workspace cannot hand this action to the shell-owned command router.',
|
|
268
|
-
},
|
|
269
|
-
};
|
|
270
|
-
}
|
|
271
|
-
if (editor.kind === 'knowledge-bookmarks') {
|
|
272
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
273
|
-
return {
|
|
274
|
-
kind: 'editor',
|
|
275
|
-
editor: { ...editor, message: 'Bookmark import not confirmed. Type yes, then press Enter.' },
|
|
276
|
-
status: 'Agent Knowledge bookmark import not confirmed.',
|
|
277
|
-
};
|
|
278
|
-
}
|
|
279
|
-
const command = `/knowledge import-bookmarks ${quoteSlashCommandArg(readField('path'))} --yes`;
|
|
280
|
-
return {
|
|
281
|
-
kind: 'dispatch',
|
|
282
|
-
command,
|
|
283
|
-
status: 'Opening Agent Knowledge bookmark import.',
|
|
284
|
-
actionResult: {
|
|
285
|
-
kind: 'dispatched',
|
|
286
|
-
title: 'Opening Agent Knowledge bookmark import',
|
|
287
|
-
detail: 'The workspace handed a confirmed bookmark import command to the shell-owned command router.',
|
|
288
|
-
command,
|
|
289
|
-
safety: 'safe',
|
|
290
|
-
},
|
|
291
|
-
};
|
|
292
|
-
}
|
|
293
|
-
if (editor.kind === 'knowledge-file') {
|
|
294
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
295
|
-
return {
|
|
296
|
-
kind: 'editor',
|
|
297
|
-
editor: { ...editor, message: 'File ingest not confirmed. Type yes, then press Enter.' },
|
|
298
|
-
status: 'Agent Knowledge file ingest not confirmed.',
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
const parts = ['/knowledge', 'ingest-file', quoteSlashCommandArg(readField('path'))];
|
|
302
|
-
const title = readField('title');
|
|
303
|
-
const tags = readField('tags');
|
|
304
|
-
const folder = readField('folder');
|
|
305
|
-
if (title.length > 0) parts.push('--title', quoteSlashCommandArg(title));
|
|
306
|
-
if (tags.length > 0) parts.push('--tags', quoteSlashCommandArg(tags));
|
|
307
|
-
if (folder.length > 0) parts.push('--folder', quoteSlashCommandArg(folder));
|
|
308
|
-
parts.push('--yes');
|
|
309
|
-
const command = parts.join(' ');
|
|
310
|
-
return {
|
|
311
|
-
kind: 'dispatch',
|
|
312
|
-
command,
|
|
313
|
-
status: 'Opening Agent Knowledge file ingest.',
|
|
314
|
-
actionResult: {
|
|
315
|
-
kind: 'dispatched',
|
|
316
|
-
title: 'Opening Agent Knowledge file ingest',
|
|
317
|
-
detail: 'The workspace handed a confirmed file ingest command to the shell-owned command router.',
|
|
318
|
-
command,
|
|
319
|
-
safety: 'safe',
|
|
320
|
-
},
|
|
321
|
-
};
|
|
322
|
-
}
|
|
323
|
-
if (editor.kind === 'knowledge-browser-history') {
|
|
324
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
325
|
-
return {
|
|
326
|
-
kind: 'editor',
|
|
327
|
-
editor: { ...editor, message: 'Browser history import not confirmed. Type yes, then press Enter.' },
|
|
328
|
-
status: 'Agent Knowledge browser history import not confirmed.',
|
|
329
|
-
};
|
|
330
|
-
}
|
|
331
|
-
const parts = ['/knowledge', 'import-browser-history'];
|
|
332
|
-
const browsers = readField('browsers');
|
|
333
|
-
const sources = readField('sources');
|
|
334
|
-
const limit = readField('limit');
|
|
335
|
-
const sinceDays = readField('sinceDays');
|
|
336
|
-
if (browsers.length > 0) parts.push('--browsers', quoteSlashCommandArg(browsers));
|
|
337
|
-
if (sources.length > 0) parts.push('--sources', quoteSlashCommandArg(sources));
|
|
338
|
-
if (limit.length > 0) parts.push('--limit', quoteSlashCommandArg(limit));
|
|
339
|
-
if (sinceDays.length > 0) parts.push('--since-days', quoteSlashCommandArg(sinceDays));
|
|
340
|
-
parts.push('--yes');
|
|
341
|
-
const command = parts.join(' ');
|
|
342
|
-
return {
|
|
343
|
-
kind: 'dispatch',
|
|
344
|
-
command,
|
|
345
|
-
status: 'Opening Agent Knowledge browser history import.',
|
|
346
|
-
actionResult: {
|
|
347
|
-
kind: 'dispatched',
|
|
348
|
-
title: 'Opening Agent Knowledge browser history import',
|
|
349
|
-
detail: 'The workspace handed a confirmed browser-history import command to the shell-owned command router.',
|
|
350
|
-
command,
|
|
351
|
-
safety: 'safe',
|
|
352
|
-
},
|
|
353
|
-
};
|
|
354
|
-
}
|
|
355
|
-
if (editor.kind === 'knowledge-connector-ingest') {
|
|
356
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
357
|
-
return {
|
|
358
|
-
kind: 'editor',
|
|
359
|
-
editor: { ...editor, message: 'Connector ingest not confirmed. Type yes, then press Enter.' },
|
|
360
|
-
status: 'Agent Knowledge connector ingest not confirmed.',
|
|
361
|
-
};
|
|
362
|
-
}
|
|
363
|
-
const connectorId = readField('connectorId');
|
|
364
|
-
const input = readField('input');
|
|
365
|
-
const path = readField('path');
|
|
366
|
-
const content = readField('content');
|
|
367
|
-
const parts = ['/knowledge', 'ingest-connector', quoteSlashCommandArg(connectorId)];
|
|
368
|
-
if (input.length > 0) parts.push('--input', quoteSlashCommandArg(input));
|
|
369
|
-
if (path.length > 0) parts.push('--path', quoteSlashCommandArg(path));
|
|
370
|
-
if (content.length > 0) parts.push('--content', quoteSlashCommandArg(content));
|
|
371
|
-
if (isAffirmative(readField('allowPrivateHosts'))) parts.push('--allow-private-hosts');
|
|
372
|
-
parts.push('--yes');
|
|
373
|
-
const command = parts.join(' ');
|
|
374
|
-
return {
|
|
375
|
-
kind: 'dispatch',
|
|
376
|
-
command,
|
|
377
|
-
status: 'Opening Agent Knowledge connector ingest.',
|
|
378
|
-
actionResult: {
|
|
379
|
-
kind: 'dispatched',
|
|
380
|
-
title: 'Opening Agent Knowledge connector ingest',
|
|
381
|
-
detail: 'The workspace handed a confirmed connector ingest command to the shell-owned command router.',
|
|
382
|
-
command,
|
|
383
|
-
safety: 'safe',
|
|
384
|
-
},
|
|
385
|
-
};
|
|
386
|
-
}
|
|
387
|
-
if (editor.kind === 'mcp-server') {
|
|
388
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
389
|
-
return {
|
|
390
|
-
kind: 'editor',
|
|
391
|
-
editor: { ...editor, message: 'MCP server add/update not confirmed. Type yes, then press Enter.' },
|
|
392
|
-
status: 'MCP server add/update not confirmed.',
|
|
393
|
-
};
|
|
394
|
-
}
|
|
395
|
-
const parts = [
|
|
396
|
-
'/mcp',
|
|
397
|
-
'add',
|
|
398
|
-
quoteSlashCommandArg(readField('name')),
|
|
399
|
-
quoteSlashCommandArg(readField('command')),
|
|
400
|
-
...tokenizeSlashCommand(readField('args')).map(quoteSlashCommandArg),
|
|
401
|
-
];
|
|
402
|
-
const scope = readField('scope');
|
|
403
|
-
const role = readField('role');
|
|
404
|
-
const trust = readField('trust');
|
|
405
|
-
if (scope.length > 0) parts.push('--scope', quoteSlashCommandArg(scope));
|
|
406
|
-
if (role.length > 0) parts.push('--role', quoteSlashCommandArg(role));
|
|
407
|
-
if (trust.length > 0) parts.push('--trust', quoteSlashCommandArg(trust));
|
|
408
|
-
for (const env of splitCommaList(readField('env'))) parts.push('--env', quoteSlashCommandArg(env));
|
|
409
|
-
for (const path of splitCommaList(readField('paths'))) parts.push('--path', quoteSlashCommandArg(path));
|
|
410
|
-
for (const host of splitCommaList(readField('hosts'))) parts.push('--host', quoteSlashCommandArg(host));
|
|
411
|
-
parts.push('--yes');
|
|
412
|
-
const command = parts.join(' ');
|
|
413
|
-
return {
|
|
414
|
-
kind: 'dispatch',
|
|
415
|
-
command,
|
|
416
|
-
status: 'Opening MCP server add/update.',
|
|
417
|
-
actionResult: {
|
|
418
|
-
kind: 'dispatched',
|
|
419
|
-
title: 'Opening MCP server add/update',
|
|
420
|
-
detail: 'The workspace handed a confirmed MCP server add/update command to the shell-owned command router.',
|
|
421
|
-
command,
|
|
422
|
-
safety: 'safe',
|
|
423
|
-
},
|
|
424
|
-
};
|
|
425
|
-
}
|
|
426
|
-
if (editor.kind === 'notify-webhook') {
|
|
427
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
428
|
-
return {
|
|
429
|
-
kind: 'editor',
|
|
430
|
-
editor: { ...editor, message: 'Notification webhook add not confirmed. Type yes, then press Enter.' },
|
|
431
|
-
status: 'Notification webhook add not confirmed.',
|
|
432
|
-
};
|
|
433
|
-
}
|
|
434
|
-
const command = `/notify add ${quoteSlashCommandArg(readField('url'))} --yes`;
|
|
435
|
-
return {
|
|
436
|
-
kind: 'dispatch',
|
|
437
|
-
command,
|
|
438
|
-
status: 'Opening notification webhook add.',
|
|
439
|
-
actionResult: {
|
|
440
|
-
kind: 'dispatched',
|
|
441
|
-
title: 'Opening notification webhook add',
|
|
442
|
-
detail: 'The workspace handed a confirmed notification target command to the shell-owned command router.',
|
|
443
|
-
command,
|
|
444
|
-
safety: 'safe',
|
|
445
|
-
},
|
|
446
|
-
};
|
|
447
|
-
}
|
|
448
|
-
if (editor.kind === 'notify-webhook-remove') {
|
|
449
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
450
|
-
return {
|
|
451
|
-
kind: 'editor',
|
|
452
|
-
editor: { ...editor, message: 'Notification webhook remove not confirmed. Type yes, then press Enter.' },
|
|
453
|
-
status: 'Notification webhook remove not confirmed.',
|
|
454
|
-
};
|
|
455
|
-
}
|
|
456
|
-
const command = `/notify remove ${quoteSlashCommandArg(readField('url'))} --yes`;
|
|
457
|
-
return {
|
|
458
|
-
kind: 'dispatch',
|
|
459
|
-
command,
|
|
460
|
-
status: 'Opening notification webhook remove.',
|
|
461
|
-
actionResult: {
|
|
462
|
-
kind: 'dispatched',
|
|
463
|
-
title: 'Opening notification webhook remove',
|
|
464
|
-
detail: 'The workspace handed a confirmed notification target remove command to the shell-owned command router.',
|
|
465
|
-
command,
|
|
466
|
-
safety: 'safe',
|
|
467
|
-
},
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
if (editor.kind === 'notify-webhook-test') {
|
|
471
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
472
|
-
return {
|
|
473
|
-
kind: 'editor',
|
|
474
|
-
editor: { ...editor, message: 'Notification webhook test not confirmed. Type yes, then press Enter.' },
|
|
475
|
-
status: 'Notification webhook test not confirmed.',
|
|
476
|
-
};
|
|
477
|
-
}
|
|
478
|
-
const command = '/notify test --yes';
|
|
479
|
-
return {
|
|
480
|
-
kind: 'dispatch',
|
|
481
|
-
command,
|
|
482
|
-
status: 'Opening notification webhook test.',
|
|
483
|
-
actionResult: {
|
|
484
|
-
kind: 'dispatched',
|
|
485
|
-
title: 'Opening notification webhook test',
|
|
486
|
-
detail: 'The workspace handed a confirmed notification test command to the shell-owned command router.',
|
|
487
|
-
command,
|
|
488
|
-
safety: 'safe',
|
|
489
|
-
},
|
|
490
|
-
};
|
|
491
|
-
}
|
|
492
|
-
if (editor.kind === 'tts-prompt') {
|
|
493
|
-
const command = `/tts ${quoteSlashCommandArg(readField('prompt'))}`;
|
|
494
|
-
return {
|
|
495
|
-
kind: 'dispatch',
|
|
496
|
-
command,
|
|
497
|
-
status: 'Opening spoken assistant prompt.',
|
|
498
|
-
actionResult: {
|
|
499
|
-
kind: 'dispatched',
|
|
500
|
-
title: 'Opening spoken assistant prompt',
|
|
501
|
-
detail: 'The workspace handed a spoken prompt to the shell-owned command router.',
|
|
502
|
-
command,
|
|
503
|
-
safety: 'safe',
|
|
504
|
-
},
|
|
505
|
-
};
|
|
506
|
-
}
|
|
507
|
-
if (editor.kind === 'image-input') {
|
|
508
|
-
const prompt = readField('prompt');
|
|
509
|
-
const command = prompt.length > 0
|
|
510
|
-
? `/image ${quoteSlashCommandArg(readField('path'))} ${quoteSlashCommandArg(prompt)}`
|
|
511
|
-
: `/image ${quoteSlashCommandArg(readField('path'))}`;
|
|
512
|
-
return {
|
|
513
|
-
kind: 'dispatch',
|
|
514
|
-
command,
|
|
515
|
-
status: 'Opening image input.',
|
|
516
|
-
actionResult: {
|
|
517
|
-
kind: 'dispatched',
|
|
518
|
-
title: 'Opening image input',
|
|
519
|
-
detail: 'The workspace handed an image attachment command to the shell-owned command router.',
|
|
520
|
-
command,
|
|
521
|
-
safety: 'safe',
|
|
522
|
-
},
|
|
523
|
-
};
|
|
524
|
-
}
|
|
525
|
-
if (editor.kind === 'profile-template-export') {
|
|
526
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
527
|
-
return {
|
|
528
|
-
kind: 'editor',
|
|
529
|
-
editor: { ...editor, message: 'Starter template export not confirmed. Type yes, then press Enter.' },
|
|
530
|
-
status: 'Agent starter template export not confirmed.',
|
|
531
|
-
};
|
|
532
|
-
}
|
|
533
|
-
const command = `/agent-profile template export ${quoteSlashCommandArg(readField('templateId'))} ${quoteSlashCommandArg(readField('path'))} --yes`;
|
|
534
|
-
return {
|
|
535
|
-
kind: 'dispatch',
|
|
536
|
-
command,
|
|
537
|
-
status: 'Opening Agent starter template export.',
|
|
538
|
-
actionResult: {
|
|
539
|
-
kind: 'dispatched',
|
|
540
|
-
title: 'Opening Agent starter template export',
|
|
541
|
-
detail: 'The workspace handed a confirmed starter template export command to the shell-owned command router.',
|
|
542
|
-
command,
|
|
543
|
-
safety: 'safe',
|
|
544
|
-
},
|
|
545
|
-
};
|
|
546
|
-
}
|
|
547
|
-
if (editor.kind === 'profile-template-import') {
|
|
548
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
549
|
-
return {
|
|
550
|
-
kind: 'editor',
|
|
551
|
-
editor: { ...editor, message: 'Starter template import not confirmed. Type yes, then press Enter.' },
|
|
552
|
-
status: 'Agent starter template import not confirmed.',
|
|
553
|
-
};
|
|
554
|
-
}
|
|
555
|
-
const command = `/agent-profile template import ${quoteSlashCommandArg(readField('path'))} --yes`;
|
|
556
|
-
return {
|
|
557
|
-
kind: 'dispatch',
|
|
558
|
-
command,
|
|
559
|
-
status: 'Opening Agent starter template import.',
|
|
560
|
-
actionResult: {
|
|
561
|
-
kind: 'dispatched',
|
|
562
|
-
title: 'Opening Agent starter template import',
|
|
563
|
-
detail: 'The workspace handed a confirmed starter template import command to the shell-owned command router.',
|
|
564
|
-
command,
|
|
565
|
-
safety: 'safe',
|
|
566
|
-
},
|
|
567
|
-
};
|
|
568
|
-
}
|
|
569
|
-
if (editor.kind === 'skill-discovery-import') {
|
|
570
|
-
if (!isAffirmative(readField('confirm'))) {
|
|
571
|
-
return {
|
|
572
|
-
kind: 'editor',
|
|
573
|
-
editor: { ...editor, message: 'Discovered skill import not confirmed. Type yes, then press Enter.' },
|
|
574
|
-
status: 'Agent skill import not confirmed.',
|
|
575
|
-
};
|
|
576
|
-
}
|
|
577
|
-
const parts = [
|
|
578
|
-
'/agent-skills',
|
|
579
|
-
'import-discovered',
|
|
580
|
-
quoteSlashCommandArg(readField('name')),
|
|
581
|
-
];
|
|
582
|
-
if (isAffirmative(readField('enabled'))) parts.push('--enabled');
|
|
583
|
-
parts.push('--yes');
|
|
584
|
-
const command = parts.join(' ');
|
|
585
|
-
return {
|
|
586
|
-
kind: 'dispatch',
|
|
587
|
-
command,
|
|
588
|
-
status: 'Opening discovered skill import.',
|
|
589
|
-
actionResult: {
|
|
590
|
-
kind: 'dispatched',
|
|
591
|
-
title: 'Opening discovered skill import',
|
|
592
|
-
detail: 'The workspace handed a confirmed local skill import command to the shell-owned command router.',
|
|
593
|
-
command,
|
|
594
|
-
safety: 'safe',
|
|
595
|
-
},
|
|
596
|
-
};
|
|
597
|
-
}
|
|
598
|
-
const commandParts = [
|
|
599
|
-
'/agent-skills bundle create',
|
|
600
|
-
'--name',
|
|
601
|
-
quoteSlashCommandArg(readField('name')),
|
|
602
|
-
'--description',
|
|
603
|
-
quoteSlashCommandArg(readField('description')),
|
|
604
|
-
'--skills',
|
|
605
|
-
quoteSlashCommandArg(readField('skills')),
|
|
606
|
-
];
|
|
607
|
-
if (isAffirmative(readField('enabled'))) commandParts.push('--enabled');
|
|
608
|
-
const command = commandParts.join(' ');
|
|
609
|
-
return {
|
|
610
|
-
kind: 'dispatch',
|
|
611
|
-
command,
|
|
612
|
-
status: 'Opening skill bundle creation.',
|
|
613
|
-
actionResult: {
|
|
614
|
-
kind: 'dispatched',
|
|
615
|
-
title: 'Opening skill bundle creation',
|
|
616
|
-
detail: 'The workspace handed a concrete local skill bundle command to the shell-owned command router.',
|
|
617
|
-
command,
|
|
618
|
-
safety: 'safe',
|
|
619
|
-
},
|
|
620
|
-
};
|
|
621
|
-
}
|
|
@@ -33,6 +33,7 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
33
33
|
{ id: 'setup-provider-model', label: 'Provider and model', detail: 'Choose the provider/model route for normal assistant chat.', command: '/model', kind: 'command', safety: 'safe' },
|
|
34
34
|
{ id: 'setup-agent-knowledge', label: 'Agent Knowledge', detail: 'Inspect the isolated Agent Knowledge store before ingesting source-backed material.', command: '/knowledge status', kind: 'command', safety: 'read-only' },
|
|
35
35
|
{ id: 'setup-runtime-profiles', label: 'Agent profiles', detail: 'Browse starter templates for isolated Agent homes and operator identities.', command: '/agent-profile templates', kind: 'command', safety: 'read-only' },
|
|
36
|
+
{ id: 'setup-profile-from-discovered', label: 'Profile from discovered behavior', detail: 'Create an isolated Agent profile directly from reviewed local persona, skill, and routine files.', editorKind: 'profile-from-discovered', kind: 'editor', safety: 'safe' },
|
|
36
37
|
{ id: 'setup-personas', label: 'Personas', detail: 'Create or select the active local Agent persona.', targetCategoryId: 'personas', kind: 'workspace', safety: 'safe' },
|
|
37
38
|
{ id: 'setup-skills', label: 'Skills', detail: 'Create, review, and enable reusable local Agent skills.', targetCategoryId: 'skills', kind: 'workspace', safety: 'safe' },
|
|
38
39
|
{ id: 'setup-routines', label: 'Routines', detail: 'Create, review, and enable local Agent routines before any explicit schedule promotion.', targetCategoryId: 'routines', kind: 'workspace', safety: 'safe' },
|
|
@@ -124,6 +125,8 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
124
125
|
{ id: 'runtime-profile-list', label: 'List Agent profiles', detail: 'List isolated Agent profile homes under this Agent home.', command: '/agent-profile list', kind: 'command', safety: 'read-only' },
|
|
125
126
|
{ id: 'runtime-profile-template-export', label: 'Export starter template', detail: 'Open an in-workspace form that exports a starter template JSON file for review and customization.', editorKind: 'profile-template-export', kind: 'editor', safety: 'safe' },
|
|
126
127
|
{ id: 'runtime-profile-template-import', label: 'Import starter template', detail: 'Open an in-workspace form that imports a reviewed starter template JSON file into this Agent home.', editorKind: 'profile-template-import', kind: 'editor', safety: 'safe' },
|
|
128
|
+
{ id: 'runtime-profile-template-from-discovered', label: 'Starter from discovered behavior', detail: 'Open an in-workspace form that creates one local starter template from reviewed discovered persona, skill, and routine files.', editorKind: 'profile-template-from-discovered', kind: 'editor', safety: 'safe' },
|
|
129
|
+
{ id: 'runtime-profile-from-discovered', label: 'Profile from discovered behavior', detail: 'Open an in-workspace form that creates a local starter and isolated Agent profile from reviewed discovered behavior files.', editorKind: 'profile-from-discovered', kind: 'editor', safety: 'safe' },
|
|
127
130
|
{ id: 'runtime-profile-create', label: 'Create Agent profile', detail: 'Open an in-workspace form that creates an isolated Agent home from a built-in or local starter.', editorKind: 'profile', kind: 'editor', safety: 'safe' },
|
|
128
131
|
{ id: 'runtime-profile-template-edit', label: 'Customize starter', detail: 'Open the starter export form, edit the JSON file, then import it as a local starter.', editorKind: 'profile-template-export', kind: 'editor', safety: 'safe' },
|
|
129
132
|
{ id: 'runtime-profile-switch', label: 'Switch Agent profile', detail: 'Launch goodvibes-agent --agent-profile NAME to use that isolated Agent home. This workspace cannot switch the current process home after startup.', kind: 'guidance', safety: 'safe' },
|
|
@@ -158,6 +161,8 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
158
161
|
actions: [
|
|
159
162
|
{ id: 'personas-list', label: 'List personas', detail: 'Print the full local persona library.', command: '/personas list', kind: 'command', safety: 'read-only' },
|
|
160
163
|
{ id: 'personas-active', label: 'Show active persona', detail: 'Inspect the active local persona applied to new turns.', command: '/personas active', kind: 'command', safety: 'read-only' },
|
|
164
|
+
{ id: 'personas-discover', label: 'Discover persona files', detail: 'Scan project and global Agent persona folders for persona markdown without importing it.', command: '/personas discover', kind: 'command', safety: 'read-only' },
|
|
165
|
+
{ id: 'personas-import-discovered', label: 'Import discovered persona', detail: 'Open an in-workspace form that imports one reviewed persona markdown file into the Agent-local persona registry after typed confirmation.', editorKind: 'persona-discovery-import', kind: 'editor', safety: 'safe' },
|
|
161
166
|
{ id: 'personas-prev', label: 'Previous persona', detail: 'Move the local persona selection up without changing active state.', localKind: 'persona', selectionDelta: -1, kind: 'local-selection', safety: 'safe' },
|
|
162
167
|
{ id: 'personas-next', label: 'Next persona', detail: 'Move the local persona selection down without changing active state.', localKind: 'persona', selectionDelta: 1, kind: 'local-selection', safety: 'safe' },
|
|
163
168
|
{ id: 'personas-create', label: 'Create persona', detail: 'Open an in-workspace form for a local persona that writes Agent-local state only.', editorKind: 'persona', kind: 'editor', safety: 'safe' },
|
|
@@ -200,6 +205,8 @@ export const AGENT_WORKSPACE_CATEGORIES: readonly AgentWorkspaceCategory[] = [
|
|
|
200
205
|
actions: [
|
|
201
206
|
{ id: 'routines-list', label: 'List routines', detail: 'Print the full local Agent routine library.', command: '/routines list', kind: 'command', safety: 'read-only' },
|
|
202
207
|
{ id: 'routines-enabled', label: 'Enabled routines', detail: 'Show routines available for direct use.', command: '/routines enabled', kind: 'command', safety: 'read-only' },
|
|
208
|
+
{ id: 'routines-discover', label: 'Discover routine files', detail: 'Scan project and global Agent routine folders for routine markdown without importing it.', command: '/routines discover', kind: 'command', safety: 'read-only' },
|
|
209
|
+
{ id: 'routines-import-discovered', label: 'Import discovered routine', detail: 'Open an in-workspace form that imports one reviewed routine markdown file into the Agent-local routine registry after typed confirmation.', editorKind: 'routine-discovery-import', kind: 'editor', safety: 'safe' },
|
|
203
210
|
{ id: 'routines-prev', label: 'Previous routine', detail: 'Move the local routine selection up without changing enabled state.', localKind: 'routine', selectionDelta: -1, kind: 'local-selection', safety: 'safe' },
|
|
204
211
|
{ id: 'routines-next', label: 'Next routine', detail: 'Move the local routine selection down without changing enabled state.', localKind: 'routine', selectionDelta: 1, kind: 'local-selection', safety: 'safe' },
|
|
205
212
|
{ id: 'routines-create', label: 'Create routine', detail: 'Open an in-workspace form for a repeatable local workflow that writes Agent-local state only.', editorKind: 'routine', kind: 'editor', safety: 'safe' },
|
|
@@ -24,9 +24,13 @@ type AgentWorkspaceCommandEditorKind = Extract<
|
|
|
24
24
|
| 'tts-prompt'
|
|
25
25
|
| 'image-input'
|
|
26
26
|
| 'skill-bundle'
|
|
27
|
+
| 'persona-discovery-import'
|
|
28
|
+
| 'routine-discovery-import'
|
|
27
29
|
| 'skill-discovery-import'
|
|
28
30
|
| 'profile-template-export'
|
|
29
31
|
| 'profile-template-import'
|
|
32
|
+
| 'profile-template-from-discovered'
|
|
33
|
+
| 'profile-from-discovered'
|
|
30
34
|
>;
|
|
31
35
|
|
|
32
36
|
type AgentWorkspaceCommandEditorSubmission =
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { AgentBehaviorDiscoverySummary } from '../agent/behavior-discovery-summary.ts';
|
|
2
|
+
|
|
1
3
|
export type AgentWorkspaceSetupStatus = 'ready' | 'recommended' | 'optional' | 'blocked';
|
|
2
4
|
|
|
3
5
|
export interface AgentWorkspaceSetupChecklistItem {
|
|
@@ -22,6 +24,9 @@ export interface AgentWorkspaceSetupChecklistInput {
|
|
|
22
24
|
readonly skillBundleCount: number;
|
|
23
25
|
readonly enabledSkillBundleCount: number;
|
|
24
26
|
readonly activePersonaName: string;
|
|
27
|
+
readonly discoveredPersonas: AgentBehaviorDiscoverySummary;
|
|
28
|
+
readonly discoveredSkills: AgentBehaviorDiscoverySummary;
|
|
29
|
+
readonly discoveredRoutines: AgentBehaviorDiscoverySummary;
|
|
25
30
|
readonly readyChannelCount: number;
|
|
26
31
|
readonly voiceProviderCount: number;
|
|
27
32
|
readonly mediaProviderCount: number;
|
|
@@ -33,9 +38,16 @@ function setupStatusForCount(count: number, ready: AgentWorkspaceSetupStatus, em
|
|
|
33
38
|
return count > 0 ? ready : empty;
|
|
34
39
|
}
|
|
35
40
|
|
|
41
|
+
function sampleNames(summary: AgentBehaviorDiscoverySummary): string {
|
|
42
|
+
if (summary.names.length === 0) return '';
|
|
43
|
+
const suffix = summary.count > summary.names.length ? `, +${summary.count - summary.names.length} more` : '';
|
|
44
|
+
return ` Found: ${summary.names.join(', ')}${suffix}.`;
|
|
45
|
+
}
|
|
46
|
+
|
|
36
47
|
export function buildAgentWorkspaceSetupChecklist(input: AgentWorkspaceSetupChecklistInput): readonly AgentWorkspaceSetupChecklistItem[] {
|
|
37
48
|
const providerReady = input.provider !== 'unknown' && input.model !== 'unknown';
|
|
38
49
|
const hasActivePersona = input.activePersonaName !== '(none)' && input.activePersonaName !== '(unavailable)';
|
|
50
|
+
const discoveredBehaviorCount = input.discoveredPersonas.count + input.discoveredSkills.count + input.discoveredRoutines.count;
|
|
39
51
|
return [
|
|
40
52
|
{
|
|
41
53
|
id: 'runtime',
|
|
@@ -63,38 +75,46 @@ export function buildAgentWorkspaceSetupChecklist(input: AgentWorkspaceSetupChec
|
|
|
63
75
|
{
|
|
64
76
|
id: 'profile',
|
|
65
77
|
label: 'Agent profile',
|
|
66
|
-
status:
|
|
78
|
+
status: input.runtimeProfileCount > 0 ? 'ready' : discoveredBehaviorCount > 0 ? 'recommended' : 'optional',
|
|
67
79
|
detail: input.runtimeProfileCount > 0
|
|
68
80
|
? `${input.runtimeProfileCount} isolated Agent profile(s) are available.`
|
|
81
|
+
: discoveredBehaviorCount > 0
|
|
82
|
+
? `${discoveredBehaviorCount} discovered behavior file(s) can seed an isolated Agent profile from the Profiles workspace.`
|
|
69
83
|
: `${input.runtimeStarterTemplateCount} starter template(s) are available if this machine needs separate operator identities.`,
|
|
70
|
-
command: '/agent-profile templates',
|
|
84
|
+
command: discoveredBehaviorCount > 0 && input.runtimeProfileCount === 0 ? '/agent-profile guide' : '/agent-profile templates',
|
|
71
85
|
},
|
|
72
86
|
{
|
|
73
87
|
id: 'persona',
|
|
74
88
|
label: 'Persona',
|
|
75
89
|
status: hasActivePersona ? 'ready' : 'recommended',
|
|
76
90
|
detail: hasActivePersona
|
|
77
|
-
? `Active persona: ${input.activePersonaName}.`
|
|
78
|
-
:
|
|
79
|
-
|
|
91
|
+
? `Active persona: ${input.activePersonaName}.${input.discoveredPersonas.count > 0 ? ` ${input.discoveredPersonas.count} discovered persona file(s) are still available to import.` : ''}`
|
|
92
|
+
: input.discoveredPersonas.count > 0
|
|
93
|
+
? `${input.discoveredPersonas.count} discovered persona file(s) can be imported into the Agent-local registry.${sampleNames(input.discoveredPersonas)}`
|
|
94
|
+
: 'Create or choose a persona to make the assistant voice and policy explicit.',
|
|
95
|
+
command: input.discoveredPersonas.count > 0 ? '/personas discover' : '/personas',
|
|
80
96
|
},
|
|
81
97
|
{
|
|
82
98
|
id: 'skills',
|
|
83
99
|
label: 'Skills',
|
|
84
|
-
status: input.enabledSkillCount > 0 || input.enabledSkillBundleCount > 0 ? 'ready' : input.skillCount > 0 || input.skillBundleCount > 0 ? 'recommended' : 'optional',
|
|
100
|
+
status: input.enabledSkillCount > 0 || input.enabledSkillBundleCount > 0 ? 'ready' : input.skillCount > 0 || input.skillBundleCount > 0 || input.discoveredSkills.count > 0 ? 'recommended' : 'optional',
|
|
85
101
|
detail: input.skillCount > 0 || input.skillBundleCount > 0
|
|
86
|
-
? `${input.enabledSkillCount}/${input.skillCount} local skill(s) enabled; ${input.enabledSkillBundleCount}/${input.skillBundleCount} bundle(s) enabled.`
|
|
87
|
-
:
|
|
88
|
-
|
|
102
|
+
? `${input.enabledSkillCount}/${input.skillCount} local skill(s) enabled; ${input.enabledSkillBundleCount}/${input.skillBundleCount} bundle(s) enabled.${input.discoveredSkills.count > 0 ? ` ${input.discoveredSkills.count} discovered skill file(s) are still available to import.` : ''}`
|
|
103
|
+
: input.discoveredSkills.count > 0
|
|
104
|
+
? `${input.discoveredSkills.count} discovered skill file(s) can be imported as local reusable procedures.${sampleNames(input.discoveredSkills)}`
|
|
105
|
+
: 'Create reusable local skills and bundles for repeated workflows.',
|
|
106
|
+
command: input.discoveredSkills.count > 0 ? '/agent-skills discover' : '/agent-skills',
|
|
89
107
|
},
|
|
90
108
|
{
|
|
91
109
|
id: 'routines',
|
|
92
110
|
label: 'Routines',
|
|
93
|
-
status: setupStatusForCount(input.enabledRoutineCount, 'ready', input.routineCount > 0 ? 'recommended' : 'optional'),
|
|
111
|
+
status: setupStatusForCount(input.enabledRoutineCount, 'ready', input.routineCount > 0 || input.discoveredRoutines.count > 0 ? 'recommended' : 'optional'),
|
|
94
112
|
detail: input.routineCount > 0
|
|
95
|
-
? `${input.enabledRoutineCount}/${input.routineCount} local routine(s) enabled.`
|
|
96
|
-
:
|
|
97
|
-
|
|
113
|
+
? `${input.enabledRoutineCount}/${input.routineCount} local routine(s) enabled.${input.discoveredRoutines.count > 0 ? ` ${input.discoveredRoutines.count} discovered routine file(s) are still available to import.` : ''}`
|
|
114
|
+
: input.discoveredRoutines.count > 0
|
|
115
|
+
? `${input.discoveredRoutines.count} discovered routine file(s) can be imported as main-conversation workflows.${sampleNames(input.discoveredRoutines)}`
|
|
116
|
+
: 'Create local routines first; promote schedules only with explicit confirmation.',
|
|
117
|
+
command: input.discoveredRoutines.count > 0 ? '/routines discover' : '/routines',
|
|
98
118
|
},
|
|
99
119
|
{
|
|
100
120
|
id: 'memory',
|