@pellux/goodvibes-agent 0.1.1 → 0.1.3
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 +14 -0
- package/README.md +12 -1
- package/docs/README.md +2 -0
- package/docs/getting-started.md +19 -1
- package/docs/release-and-publishing.md +3 -1
- package/package.json +10 -1
- package/src/agent/persona-registry.ts +379 -0
- package/src/agent/skill-registry.ts +360 -0
- package/src/audio/spoken-turn-model-routing.ts +2 -1
- package/src/cli/agent-knowledge-command.ts +525 -0
- package/src/cli/help.ts +35 -0
- package/src/cli/management-commands.ts +3 -1
- package/src/cli/management.ts +33 -9
- package/src/cli/parser.ts +7 -0
- package/src/cli/types.ts +3 -0
- package/src/config/surface.ts +1 -0
- package/src/input/agent-workspace.ts +33 -3
- package/src/input/command-registry.ts +4 -1
- package/src/input/commands/agent-skills-runtime.ts +216 -0
- package/src/input/commands/delegation-runtime.ts +129 -0
- package/src/input/commands/knowledge.ts +18 -18
- package/src/input/commands/personas-runtime.ts +219 -0
- package/src/input/commands/shell-core.ts +9 -6
- package/src/input/commands/skills-runtime.ts +7 -2
- package/src/input/commands.ts +6 -0
- package/src/input/panel-integration-actions.ts +0 -52
- package/src/input/submission-router.ts +1 -1
- package/src/main.ts +2 -1
- package/src/panels/builtin/agent.ts +0 -14
- package/src/panels/builtin/session.ts +4 -3
- package/src/panels/index.ts +0 -5
- package/src/panels/orchestration-panel.ts +4 -5
- package/src/panels/qr-panel.ts +3 -2
- package/src/panels/tasks-panel.ts +4 -4
- package/src/renderer/agent-workspace.ts +2 -0
- package/src/runtime/bootstrap-command-context.ts +3 -0
- package/src/runtime/bootstrap-command-parts.ts +6 -2
- package/src/runtime/bootstrap-core.ts +8 -4
- package/src/runtime/bootstrap-shell.ts +5 -2
- package/src/runtime/bootstrap.ts +10 -2
- package/src/runtime/cloudflare-control-plane.ts +2 -1
- package/src/version.ts +1 -1
- package/src/daemon/cli.ts +0 -55
- package/src/daemon/safe-serve.ts +0 -61
- package/src/panels/diff-panel.ts +0 -520
- package/src/panels/file-explorer-panel.ts +0 -584
- package/src/panels/file-preview-panel.ts +0 -434
- package/src/panels/git-panel.ts +0 -638
- package/src/panels/sandbox-panel.ts +0 -283
- package/src/panels/symbol-outline-panel.ts +0 -486
- package/src/panels/worktree-panel.ts +0 -182
- package/src/panels/wrfc-panel.ts +0 -609
|
@@ -8,10 +8,10 @@ type KnowledgeAskInput = Parameters<KnowledgeService['ask']>[0];
|
|
|
8
8
|
type KnowledgeAskResult = Awaited<ReturnType<KnowledgeService['ask']>>;
|
|
9
9
|
type KnowledgeAskMode = NonNullable<KnowledgeAskInput['mode']>;
|
|
10
10
|
|
|
11
|
-
function
|
|
12
|
-
const knowledgeApi = context.clients?.
|
|
11
|
+
function requireAgentKnowledgeApi(context: CommandContext) {
|
|
12
|
+
const knowledgeApi = context.clients?.agentKnowledgeApi;
|
|
13
13
|
if (!knowledgeApi) {
|
|
14
|
-
context.print('[knowledge] Knowledge API is not available in this runtime.');
|
|
14
|
+
context.print('[knowledge] Agent Knowledge API is not available in this runtime. Refusing to use default Knowledge/Wiki or HomeGraph fallback.');
|
|
15
15
|
return null;
|
|
16
16
|
}
|
|
17
17
|
return knowledgeApi;
|
|
@@ -57,14 +57,11 @@ function positionalArgs(args: string[], valuedFlags: readonly string[] = []): st
|
|
|
57
57
|
});
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
function
|
|
61
|
-
const serviceAsk = context.extensions.
|
|
60
|
+
function requireAgentKnowledgeAsk(context: CommandContext): ((input: KnowledgeAskInput) => Promise<KnowledgeAskResult>) | null {
|
|
61
|
+
const serviceAsk = context.extensions.agentKnowledgeService?.ask?.bind(context.extensions.agentKnowledgeService);
|
|
62
62
|
if (serviceAsk) return serviceAsk;
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
if (clientAsk) return clientAsk;
|
|
66
|
-
|
|
67
|
-
context.print('[knowledge] Knowledge ask is not available in this runtime.');
|
|
64
|
+
context.print('[knowledge] Agent Knowledge ask is not available in this runtime. Refusing to use default Knowledge/Wiki or HomeGraph fallback.');
|
|
68
65
|
return null;
|
|
69
66
|
}
|
|
70
67
|
|
|
@@ -132,11 +129,11 @@ function renderKnowledgeAskResult(result: KnowledgeAskResult): string {
|
|
|
132
129
|
export const knowledgeCommand: SlashCommand = {
|
|
133
130
|
name: 'knowledge',
|
|
134
131
|
aliases: ['know', 'kb'],
|
|
135
|
-
description: '
|
|
132
|
+
description: 'Agent Knowledge/Wiki: isolated Agent-owned sources, graph, review queue, and compact prompt packets.',
|
|
136
133
|
usage: '<subcommand> [args]',
|
|
137
134
|
argsHint: 'status|ask|ingest-url|import-bookmarks|import-urls|list|search|get|queue|review-issue|candidates|reports|schedules|lint|packet|explain|reindex|consolidate',
|
|
138
135
|
handler: async (args: string[], context: CommandContext): Promise<void> => {
|
|
139
|
-
const knowledge =
|
|
136
|
+
const knowledge = requireAgentKnowledgeApi(context);
|
|
140
137
|
if (!knowledge) {
|
|
141
138
|
return;
|
|
142
139
|
}
|
|
@@ -149,12 +146,16 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
149
146
|
|
|
150
147
|
switch (sub) {
|
|
151
148
|
case 'ask': {
|
|
152
|
-
const ask =
|
|
149
|
+
const ask = requireAgentKnowledgeAsk(context);
|
|
153
150
|
if (!ask) return;
|
|
154
|
-
const valuedFlags = ['--
|
|
151
|
+
const valuedFlags = ['--limit', '--mode'];
|
|
155
152
|
const query = positionalArgs(rest, valuedFlags).join(' ').trim();
|
|
156
153
|
if (!query) {
|
|
157
|
-
context.print('[knowledge] Usage: /knowledge ask <query> [--
|
|
154
|
+
context.print('[knowledge] Usage: /knowledge ask <query> [--limit <n>] [--mode <concise|standard|detailed>]');
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
if (readFlag(rest, '--space') || readFlag(rest, '--knowledge-space')) {
|
|
158
|
+
context.print('[knowledge] Agent Knowledge is isolated. --space/--knowledge-space is not accepted because Agent must not fall back to default Knowledge/Wiki or HomeGraph.');
|
|
158
159
|
return;
|
|
159
160
|
}
|
|
160
161
|
const requestedMode = readFlag(rest, '--mode') as KnowledgeAskMode | undefined;
|
|
@@ -163,7 +164,6 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
163
164
|
: 'standard';
|
|
164
165
|
const result = await ask({
|
|
165
166
|
query,
|
|
166
|
-
knowledgeSpaceId: readFlag(rest, '--space') ?? readFlag(rest, '--knowledge-space'),
|
|
167
167
|
limit: readPositiveIntFlag(rest, '--limit', 10),
|
|
168
168
|
mode,
|
|
169
169
|
includeSources: true,
|
|
@@ -177,7 +177,7 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
177
177
|
case 'status': {
|
|
178
178
|
const status = await knowledge.status.get();
|
|
179
179
|
context.print([
|
|
180
|
-
'[knowledge]
|
|
180
|
+
'[knowledge] Agent Knowledge status',
|
|
181
181
|
` ready: ${status.ready ? 'yes' : 'no'}`,
|
|
182
182
|
` storage: ${status.storagePath}`,
|
|
183
183
|
` sources: ${status.sourceCount}`,
|
|
@@ -385,7 +385,7 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
385
385
|
const result = await knowledge.graph.issues.review({
|
|
386
386
|
issueId,
|
|
387
387
|
action: action as KnowledgeReviewAction,
|
|
388
|
-
reviewer: readFlag(rest, '--reviewer') ?? '
|
|
388
|
+
reviewer: readFlag(rest, '--reviewer') ?? 'agent',
|
|
389
389
|
...(value ? { value } : {}),
|
|
390
390
|
});
|
|
391
391
|
context.print([
|
|
@@ -508,7 +508,7 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
508
508
|
context.print([
|
|
509
509
|
'Usage: /knowledge <subcommand>',
|
|
510
510
|
' status',
|
|
511
|
-
' ask <query> [--
|
|
511
|
+
' ask <query> [--limit <n>] [--mode <concise|standard|detailed>]',
|
|
512
512
|
' ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>]',
|
|
513
513
|
' import-bookmarks <path>',
|
|
514
514
|
' import-urls <path>',
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import { AgentPersonaRegistry, type AgentPersonaRecord } from '../../agent/persona-registry.ts';
|
|
2
|
+
import type { CommandContext, CommandRegistry } from '../command-registry.ts';
|
|
3
|
+
import { requireShellPaths } from './runtime-services.ts';
|
|
4
|
+
|
|
5
|
+
interface ParsedPersonaArgs {
|
|
6
|
+
readonly rest: readonly string[];
|
|
7
|
+
readonly flags: ReadonlyMap<string, string>;
|
|
8
|
+
readonly yes: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function parsePersonaArgs(args: readonly string[]): ParsedPersonaArgs {
|
|
12
|
+
const flags = new Map<string, string>();
|
|
13
|
+
const rest: string[] = [];
|
|
14
|
+
let yes = false;
|
|
15
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
16
|
+
const token = args[index] ?? '';
|
|
17
|
+
if (token === '--yes') {
|
|
18
|
+
yes = true;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (token.startsWith('--')) {
|
|
22
|
+
const key = token.slice(2);
|
|
23
|
+
const next = args[index + 1];
|
|
24
|
+
if (next !== undefined && !next.startsWith('--')) {
|
|
25
|
+
flags.set(key, next);
|
|
26
|
+
index += 1;
|
|
27
|
+
} else {
|
|
28
|
+
flags.set(key, 'true');
|
|
29
|
+
}
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
rest.push(token);
|
|
33
|
+
}
|
|
34
|
+
return { rest, flags, yes };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function splitList(value: string | undefined): readonly string[] {
|
|
38
|
+
if (!value) return [];
|
|
39
|
+
return value.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function registryFromContext(ctx: CommandContext): AgentPersonaRegistry {
|
|
43
|
+
return AgentPersonaRegistry.fromShellPaths(requireShellPaths(ctx));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function summarizePersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
|
|
47
|
+
const active = persona.id === activePersonaId ? 'active' : 'inactive';
|
|
48
|
+
const tags = persona.tags.length > 0 ? ` tags=${persona.tags.join(',')}` : '';
|
|
49
|
+
return ` ${persona.id} ${active} ${persona.reviewState} ${persona.name} - ${persona.description}${tags}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function renderList(title: string, registry: AgentPersonaRegistry, personas: readonly AgentPersonaRecord[]): string {
|
|
53
|
+
const snapshot = registry.snapshot();
|
|
54
|
+
if (personas.length === 0) {
|
|
55
|
+
return `${title}\n No local Agent personas yet. Create one with /personas create --name <name> --description <summary> --body <instructions>.`;
|
|
56
|
+
}
|
|
57
|
+
return [
|
|
58
|
+
`${title} (${personas.length})`,
|
|
59
|
+
` store: ${snapshot.path}`,
|
|
60
|
+
` active: ${snapshot.activePersona?.name ?? '(none)'}`,
|
|
61
|
+
...personas.map((persona) => summarizePersona(persona, snapshot.activePersonaId)),
|
|
62
|
+
].join('\n');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function renderPersona(persona: AgentPersonaRecord, activePersonaId: string | null): string {
|
|
66
|
+
return [
|
|
67
|
+
`Persona ${persona.name}`,
|
|
68
|
+
` id: ${persona.id}`,
|
|
69
|
+
` active: ${persona.id === activePersonaId ? 'yes' : 'no'}`,
|
|
70
|
+
` review: ${persona.reviewState}`,
|
|
71
|
+
` source: ${persona.source}`,
|
|
72
|
+
` provenance: ${persona.provenance}`,
|
|
73
|
+
` tags: ${persona.tags.join(', ') || '(none)'}`,
|
|
74
|
+
` triggers: ${persona.triggers.join(', ') || '(none)'}`,
|
|
75
|
+
` created: ${persona.createdAt}`,
|
|
76
|
+
` updated: ${persona.updatedAt}`,
|
|
77
|
+
persona.staleReason ? ` stale reason: ${persona.staleReason}` : '',
|
|
78
|
+
'',
|
|
79
|
+
persona.description,
|
|
80
|
+
'',
|
|
81
|
+
persona.body,
|
|
82
|
+
].filter(Boolean).join('\n');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function requiredFlag(flags: ReadonlyMap<string, string>, key: string): string {
|
|
86
|
+
const value = flags.get(key)?.trim();
|
|
87
|
+
if (!value) throw new Error(`Missing --${key}.`);
|
|
88
|
+
return value;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function printError(ctx: CommandContext, error: unknown): void {
|
|
92
|
+
ctx.print(`Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function registerPersonasRuntimeCommands(registry: CommandRegistry): void {
|
|
96
|
+
registry.register({
|
|
97
|
+
name: 'personas',
|
|
98
|
+
aliases: ['persona'],
|
|
99
|
+
description: 'Manage local GoodVibes Agent personas',
|
|
100
|
+
usage: '[list|search <query>|show <id>|create --name <name> --description <summary> --body <instructions>|update <id> [--name ...] [--description ...] [--body ...]|use <id>|active|clear|review <id>|stale <id> <reason...>|delete <id> --yes]',
|
|
101
|
+
async handler(args, ctx) {
|
|
102
|
+
const sub = (args[0] ?? 'list').toLowerCase();
|
|
103
|
+
const registryStore = registryFromContext(ctx);
|
|
104
|
+
try {
|
|
105
|
+
if (sub === 'list' || sub === 'open') {
|
|
106
|
+
ctx.print(renderList('Agent Personas', registryStore, registryStore.list()));
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
if (sub === 'search') {
|
|
110
|
+
const query = args.slice(1).join(' ').trim();
|
|
111
|
+
ctx.print(renderList(query ? `Agent Personas matching "${query}"` : 'Agent Personas', registryStore, registryStore.search(query)));
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
if (sub === 'show') {
|
|
115
|
+
const id = args[1];
|
|
116
|
+
if (!id) {
|
|
117
|
+
ctx.print('Usage: /personas show <id>');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const snapshot = registryStore.snapshot();
|
|
121
|
+
const persona = registryStore.get(id);
|
|
122
|
+
ctx.print(persona ? renderPersona(persona, snapshot.activePersonaId) : `Unknown persona: ${id}`);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (sub === 'create') {
|
|
126
|
+
const parsed = parsePersonaArgs(args.slice(1));
|
|
127
|
+
const body = parsed.flags.get('body')?.trim() || parsed.rest.join(' ').trim();
|
|
128
|
+
const persona = registryStore.create({
|
|
129
|
+
name: requiredFlag(parsed.flags, 'name'),
|
|
130
|
+
description: requiredFlag(parsed.flags, 'description'),
|
|
131
|
+
body,
|
|
132
|
+
tags: splitList(parsed.flags.get('tags')),
|
|
133
|
+
triggers: splitList(parsed.flags.get('triggers')),
|
|
134
|
+
source: 'user',
|
|
135
|
+
provenance: 'slash-command',
|
|
136
|
+
});
|
|
137
|
+
ctx.print(`Created Agent persona ${persona.id}: ${persona.name}`);
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
if (sub === 'update') {
|
|
141
|
+
const id = args[1];
|
|
142
|
+
if (!id) {
|
|
143
|
+
ctx.print('Usage: /personas update <id> [--name ...] [--description ...] [--body ...]');
|
|
144
|
+
return;
|
|
145
|
+
}
|
|
146
|
+
const parsed = parsePersonaArgs(args.slice(2));
|
|
147
|
+
const updated = registryStore.update(id, {
|
|
148
|
+
name: parsed.flags.get('name'),
|
|
149
|
+
description: parsed.flags.get('description'),
|
|
150
|
+
body: parsed.flags.get('body'),
|
|
151
|
+
tags: parsed.flags.has('tags') ? splitList(parsed.flags.get('tags')) : undefined,
|
|
152
|
+
triggers: parsed.flags.has('triggers') ? splitList(parsed.flags.get('triggers')) : undefined,
|
|
153
|
+
provenance: 'slash-command',
|
|
154
|
+
});
|
|
155
|
+
ctx.print(`Updated Agent persona ${updated.id}: ${updated.name}`);
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
if (sub === 'use') {
|
|
159
|
+
const id = args[1];
|
|
160
|
+
if (!id) {
|
|
161
|
+
ctx.print('Usage: /personas use <id>');
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const persona = registryStore.setActive(id);
|
|
165
|
+
ctx.print(`Active Agent persona: ${persona.name} (${persona.id})`);
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
if (sub === 'active') {
|
|
169
|
+
const snapshot = registryStore.snapshot();
|
|
170
|
+
ctx.print(snapshot.activePersona ? renderPersona(snapshot.activePersona, snapshot.activePersonaId) : 'No active Agent persona.');
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (sub === 'clear') {
|
|
174
|
+
registryStore.clearActive();
|
|
175
|
+
ctx.print('Cleared active Agent persona.');
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (sub === 'review') {
|
|
179
|
+
const id = args[1];
|
|
180
|
+
if (!id) {
|
|
181
|
+
ctx.print('Usage: /personas review <id>');
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
const persona = registryStore.markReviewed(id);
|
|
185
|
+
ctx.print(`Reviewed Agent persona ${persona.id}.`);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (sub === 'stale') {
|
|
189
|
+
const id = args[1];
|
|
190
|
+
if (!id) {
|
|
191
|
+
ctx.print('Usage: /personas stale <id> <reason...>');
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
const persona = registryStore.markStale(id, args.slice(2).join(' '));
|
|
195
|
+
ctx.print(`Marked Agent persona ${persona.id} stale.`);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
if (sub === 'delete' || sub === 'remove') {
|
|
199
|
+
const parsed = parsePersonaArgs(args.slice(1));
|
|
200
|
+
const id = parsed.rest[0];
|
|
201
|
+
if (!id) {
|
|
202
|
+
ctx.print('Usage: /personas delete <id> --yes');
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!parsed.yes) {
|
|
206
|
+
ctx.print(`Refusing to delete Agent persona ${id} without --yes.`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const removed = registryStore.deletePersona(id);
|
|
210
|
+
ctx.print(`Deleted Agent persona ${removed.id}: ${removed.name}`);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
ctx.print('Usage: /personas [list|search <query>|show <id>|create|update|use|active|clear|review|stale|delete]');
|
|
214
|
+
} catch (error) {
|
|
215
|
+
printError(ctx, error);
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
}
|
|
@@ -2,7 +2,6 @@ import type { CommandRegistry } from '../command-registry.ts';
|
|
|
2
2
|
import type { SelectionItem } from '../selection-modal.ts';
|
|
3
3
|
import { EFFORT_DESCRIPTIONS } from '@pellux/goodvibes-sdk/platform/providers';
|
|
4
4
|
import { REASONING_BUDGET_MAP } from '@pellux/goodvibes-sdk/platform/providers';
|
|
5
|
-
import { executeWriteQuit } from './quit-shared.ts';
|
|
6
5
|
import { compactConversation, requireKeybindingsManager, requireProviderApi } from './runtime-services.ts';
|
|
7
6
|
import { summarizeError } from '@pellux/goodvibes-sdk/platform/utils';
|
|
8
7
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
@@ -159,7 +158,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
159
158
|
{ id: '/secrets', label: '/secrets set|link|get|test|list|delete', detail: 'Manage encrypted and provider-backed secrets', category: 'Tools & System' },
|
|
160
159
|
{ id: '/help', label: '/help', detail: 'This help', category: 'Tools & System' },
|
|
161
160
|
{ id: '/quit', label: '/quit', detail: 'Exit', category: 'Tools & System' },
|
|
162
|
-
{ id: '/wq', label: '/wq', detail: '
|
|
161
|
+
{ id: '/wq', label: '/wq', detail: 'Blocked in Agent; git commit/exit belongs to GoodVibes TUI', category: 'Tools & System' },
|
|
163
162
|
];
|
|
164
163
|
ctx.openSelection('Help — Commands', items, { allowSearch: true }, (result) => {
|
|
165
164
|
if (!result) return;
|
|
@@ -173,7 +172,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
173
172
|
});
|
|
174
173
|
return;
|
|
175
174
|
}
|
|
176
|
-
ctx.print('Use /help to open the help modal. Commands: /agent, /model, /provider, /config, /template, /tools, /paste, /sessions, /bookmarks, /save, /load, /undo, /redo, /retry, /clear, /reset, /compact, /export, /title, /effort, /expand, /collapse, /debug, /quit
|
|
175
|
+
ctx.print('Use /help to open the help modal. Commands: /agent, /model, /provider, /config, /template, /tools, /paste, /sessions, /bookmarks, /save, /load, /undo, /redo, /retry, /clear, /reset, /compact, /export, /title, /effort, /expand, /collapse, /debug, /quit');
|
|
177
176
|
},
|
|
178
177
|
});
|
|
179
178
|
|
|
@@ -225,9 +224,13 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
225
224
|
registry.register({
|
|
226
225
|
name: 'wq',
|
|
227
226
|
aliases: [':wq'],
|
|
228
|
-
description: '
|
|
229
|
-
|
|
230
|
-
|
|
227
|
+
description: 'Blocked in Agent; git commit/exit is owned by GoodVibes TUI',
|
|
228
|
+
handler(_args, ctx) {
|
|
229
|
+
ctx.print([
|
|
230
|
+
'Blocked: /wq is not available in GoodVibes Agent.',
|
|
231
|
+
'Git commit, worktree, and coding-session exit flows belong to GoodVibes TUI.',
|
|
232
|
+
'No files, commits, or repository state were changed.',
|
|
233
|
+
].join('\n'));
|
|
231
234
|
},
|
|
232
235
|
});
|
|
233
236
|
|
|
@@ -12,15 +12,20 @@ import {
|
|
|
12
12
|
upsertEcosystemCatalogEntry,
|
|
13
13
|
} from '@/runtime/index.ts';
|
|
14
14
|
import { requireEcosystemCatalogPaths, requirePanelManager, requireShellPaths } from './runtime-services.ts';
|
|
15
|
+
import { runAgentSkillsRuntimeCommand } from './agent-skills-runtime.ts';
|
|
15
16
|
|
|
16
17
|
export function registerSkillsRuntimeCommands(registry: CommandRegistry): void {
|
|
17
18
|
registry.register({
|
|
18
19
|
name: 'skills',
|
|
19
20
|
aliases: ['skill'],
|
|
20
21
|
description: 'Inspect installed skill packs',
|
|
21
|
-
usage: '[open|list|show <name>|origins|browse [query]|installed|catalog-review <id>|publish-local <id> <path> <summary...>|unpublish <id>|install-hint <catalog-id>|install <id> [project|user]|update <id> [project|user]|uninstall <id> [project|user]]',
|
|
22
|
+
usage: '[open|local ...|list|show <name>|origins|browse [query]|installed|catalog-review <id>|publish-local <id> <path> <summary...>|unpublish <id>|install-hint <catalog-id>|install <id> [project|user]|update <id> [project|user]|uninstall <id> [project|user]]',
|
|
22
23
|
async handler(args, ctx) {
|
|
23
24
|
const sub = args[0] ?? 'open';
|
|
25
|
+
if (sub === 'local' || sub === 'agent') {
|
|
26
|
+
await runAgentSkillsRuntimeCommand(args.slice(1), ctx);
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
24
29
|
if (sub === 'open' || sub === 'panel') {
|
|
25
30
|
if (ctx.showPanel) ctx.showPanel('skills');
|
|
26
31
|
else {
|
|
@@ -215,7 +220,7 @@ export function registerSkillsRuntimeCommands(registry: CommandRegistry): void {
|
|
|
215
220
|
ctx.print(result.ok ? `Uninstalled curated skill ${entryId} from ${result.removedPath}` : `Error: ${result.error}`);
|
|
216
221
|
return;
|
|
217
222
|
}
|
|
218
|
-
ctx.print('Usage: /skills [open|list|show <name>|origins|browse [query]|installed|catalog-review <id>|publish-local <id> <path> <summary...>|unpublish <id>|install-hint <catalog-id>|install <id> [project|user]|update <id> [project|user]|uninstall <id> [project|user]]');
|
|
223
|
+
ctx.print('Usage: /skills [open|local ...|list|show <name>|origins|browse [query]|installed|catalog-review <id>|publish-local <id> <path> <summary...>|unpublish <id>|install-hint <catalog-id>|install <id> [project|user]|update <id> [project|user]|uninstall <id> [project|user]]');
|
|
219
224
|
},
|
|
220
225
|
});
|
|
221
226
|
}
|
package/src/input/commands.ts
CHANGED
|
@@ -54,6 +54,9 @@ import { registerCloudflareRuntimeCommands } from './commands/cloudflare-runtime
|
|
|
54
54
|
import { registerWorkPlanRuntimeCommands } from './commands/work-plan-runtime.ts';
|
|
55
55
|
import { registerAgentWorkspaceRuntimeCommands } from './commands/agent-workspace-runtime.ts';
|
|
56
56
|
import { registerAgentExternalizedTuiCommands } from './commands/agent-externalized-tui.ts';
|
|
57
|
+
import { registerDelegationRuntimeCommands } from './commands/delegation-runtime.ts';
|
|
58
|
+
import { registerPersonasRuntimeCommands } from './commands/personas-runtime.ts';
|
|
59
|
+
import { registerAgentSkillsRuntimeCommands } from './commands/agent-skills-runtime.ts';
|
|
57
60
|
|
|
58
61
|
/**
|
|
59
62
|
* registerBuiltinCommands - Register all built-in slash commands into the registry.
|
|
@@ -62,6 +65,9 @@ import { registerAgentExternalizedTuiCommands } from './commands/agent-externali
|
|
|
62
65
|
export function registerBuiltinCommands(registry: CommandRegistry): void {
|
|
63
66
|
registerShellCoreCommands(registry);
|
|
64
67
|
registerAgentWorkspaceRuntimeCommands(registry);
|
|
68
|
+
registerPersonasRuntimeCommands(registry);
|
|
69
|
+
registerAgentSkillsRuntimeCommands(registry);
|
|
70
|
+
registerDelegationRuntimeCommands(registry);
|
|
65
71
|
registerConfigCommand(registry);
|
|
66
72
|
registerOperatorRuntimeCommands(registry);
|
|
67
73
|
registerIntegrationRuntimeCommands(registry);
|
|
@@ -2,37 +2,8 @@ import type { CommandContext } from './command-registry.ts';
|
|
|
2
2
|
import { logger } from '@pellux/goodvibes-sdk/platform/utils';
|
|
3
3
|
import type { Panel } from '../panels/types.ts';
|
|
4
4
|
import type { PanelManager } from '../panels/panel-manager.ts';
|
|
5
|
-
import { FileExplorerPanel } from '../panels/file-explorer-panel.ts';
|
|
6
|
-
import { FilePreviewPanel } from '../panels/file-preview-panel.ts';
|
|
7
|
-
import { SymbolOutlinePanel } from '../panels/symbol-outline-panel.ts';
|
|
8
5
|
import { ApprovalPanel } from '../panels/approval-panel.ts';
|
|
9
6
|
|
|
10
|
-
function ensurePreviewPanel(panelManager: PanelManager): FilePreviewPanel | null {
|
|
11
|
-
const existing = panelManager.getPanel('preview');
|
|
12
|
-
if (existing instanceof FilePreviewPanel) {
|
|
13
|
-
const pane = panelManager.getPaneOf('preview');
|
|
14
|
-
panelManager.activateById('preview');
|
|
15
|
-
if (pane) panelManager.focusPane(pane);
|
|
16
|
-
return existing;
|
|
17
|
-
}
|
|
18
|
-
const targetPane: 'top' | 'bottom' = panelManager.isBottomPaneVisible()
|
|
19
|
-
? (panelManager.getFocusedPane() === 'top' ? 'bottom' : 'top')
|
|
20
|
-
: 'bottom';
|
|
21
|
-
const opened = panelManager.open('preview', targetPane);
|
|
22
|
-
panelManager.show();
|
|
23
|
-
panelManager.focusPane(targetPane);
|
|
24
|
-
return opened instanceof FilePreviewPanel ? opened : null;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function syncSymbolOutlineFromPreview(panelManager: PanelManager, previewPanel: FilePreviewPanel): void {
|
|
28
|
-
const symbols = panelManager.getPanel('symbols');
|
|
29
|
-
const filePath = previewPanel.getCurrentFilePath();
|
|
30
|
-
const source = previewPanel.getSource();
|
|
31
|
-
if (symbols instanceof SymbolOutlinePanel && filePath && source !== null) {
|
|
32
|
-
symbols.loadFile(filePath, source);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
7
|
export function handlePanelIntegrationAction(
|
|
37
8
|
panelManager: PanelManager,
|
|
38
9
|
activePanel: Panel | null,
|
|
@@ -41,29 +12,6 @@ export function handlePanelIntegrationAction(
|
|
|
41
12
|
): boolean {
|
|
42
13
|
if (!activePanel) return false;
|
|
43
14
|
|
|
44
|
-
if ((key === 'enter' || key === 'return' || key === 'right') && activePanel instanceof FileExplorerPanel) {
|
|
45
|
-
const filePath = activePanel.getFocusedFilePath();
|
|
46
|
-
if (!filePath) return false;
|
|
47
|
-
const previewPanel = ensurePreviewPanel(panelManager);
|
|
48
|
-
if (!previewPanel) return false;
|
|
49
|
-
previewPanel.openFile(filePath);
|
|
50
|
-
syncSymbolOutlineFromPreview(panelManager, previewPanel);
|
|
51
|
-
return true;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if ((key === 'enter' || key === 'return') && activePanel instanceof SymbolOutlinePanel) {
|
|
55
|
-
const location = activePanel.getSelectedLocation();
|
|
56
|
-
if (!location) return false;
|
|
57
|
-
const previewPanel = ensurePreviewPanel(panelManager);
|
|
58
|
-
if (!previewPanel) return false;
|
|
59
|
-
if (previewPanel.getCurrentFilePath() !== location.path) {
|
|
60
|
-
previewPanel.openFile(location.path);
|
|
61
|
-
syncSymbolOutlineFromPreview(panelManager, previewPanel);
|
|
62
|
-
}
|
|
63
|
-
previewPanel.goToLine(location.line);
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
15
|
if ((key === 'enter' || key === 'return') && activePanel instanceof ApprovalPanel) {
|
|
68
16
|
const command = activePanel.getSelectedCommand();
|
|
69
17
|
if (!command || !commandContext?.executeCommand) return false;
|
|
@@ -8,7 +8,7 @@ export interface SubmissionRouterInput {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
const PLAN_COMMANDS = new Set(['plan']);
|
|
11
|
-
const DELEGATION_COMMANDS = new Set(['review', 'wrfc', 'teamwork', 'agents', 'remote']);
|
|
11
|
+
const DELEGATION_COMMANDS = new Set(['delegate', 'build', 'review', 'wrfc', 'teamwork', 'agents', 'remote']);
|
|
12
12
|
const PANEL_COMMANDS = new Set(['panel']);
|
|
13
13
|
const ORCHESTRATION_COMMANDS = new Set([
|
|
14
14
|
'orchestration',
|
package/src/main.ts
CHANGED
|
@@ -54,6 +54,7 @@ import { allowTerminalWrite, installTuiTerminalOutputGuard } from './runtime/ter
|
|
|
54
54
|
import { ProjectPlanningCoordinator } from './planning/project-planning-coordinator.ts';
|
|
55
55
|
import { buildCommandArgsHint } from './input/command-args-hint.ts';
|
|
56
56
|
import { summarizeRunningAgents } from './renderer/process-summary.ts';
|
|
57
|
+
import { GOODVIBES_AGENT_PAIRING_SURFACE } from './config/surface.ts';
|
|
57
58
|
|
|
58
59
|
const ALT_SCREEN_ENTER = '\x1b[?1049h';
|
|
59
60
|
const ALT_SCREEN_EXIT = '\x1b[?1049l';
|
|
@@ -320,7 +321,7 @@ async function main() {
|
|
|
320
321
|
inputOptions = {
|
|
321
322
|
origin: {
|
|
322
323
|
source: 'project-planning',
|
|
323
|
-
surface:
|
|
324
|
+
surface: GOODVIBES_AGENT_PAIRING_SURFACE,
|
|
324
325
|
metadata: {
|
|
325
326
|
projectId: ctx.services.projectPlanningProjectId,
|
|
326
327
|
knowledgeSpaceId: planning.state.knowledgeSpaceId,
|
|
@@ -3,7 +3,6 @@ import { AgentLogsPanel } from '../agent-logs-panel.ts';
|
|
|
3
3
|
import { ContextVisualizerPanel } from '../context-visualizer-panel.ts';
|
|
4
4
|
import { ThinkingPanel } from '../thinking-panel.ts';
|
|
5
5
|
import { ToolInspectorPanel } from '../tool-inspector-panel.ts';
|
|
6
|
-
import { WrfcPanel } from '../wrfc-panel.ts';
|
|
7
6
|
import { SchedulePanel } from '../schedule-panel.ts';
|
|
8
7
|
import { ProjectPlanningPanel } from '../project-planning-panel.ts';
|
|
9
8
|
import { WorkPlanPanel } from '../work-plan-panel.ts';
|
|
@@ -67,19 +66,6 @@ export function registerAgentPanels(manager: PanelManager, deps: ResolvedBuiltin
|
|
|
67
66
|
},
|
|
68
67
|
});
|
|
69
68
|
|
|
70
|
-
manager.registerType({
|
|
71
|
-
id: 'wrfc',
|
|
72
|
-
name: 'WRFC',
|
|
73
|
-
icon: 'W',
|
|
74
|
-
category: 'agent',
|
|
75
|
-
description: 'WRFC chain view: write, review, fix, and confirm cycle status',
|
|
76
|
-
preload: true,
|
|
77
|
-
factory: () => {
|
|
78
|
-
const ui = requireUiServices(deps);
|
|
79
|
-
return new WrfcPanel(ui.events.workflows, { controller: ui.agents.wrfcController });
|
|
80
|
-
},
|
|
81
|
-
});
|
|
82
|
-
|
|
83
69
|
manager.registerType({
|
|
84
70
|
id: 'work-plan',
|
|
85
71
|
name: 'Work Plan',
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
buildCompanionConnectionInfo,
|
|
15
15
|
} from '@pellux/goodvibes-sdk/platform/pairing';
|
|
16
16
|
import { copyToClipboard } from '../../utils/clipboard.ts';
|
|
17
|
+
import { GOODVIBES_AGENT_PAIRING_SURFACE } from '../../config/surface.ts';
|
|
17
18
|
|
|
18
19
|
function getLocalNetworkIp(): string {
|
|
19
20
|
const nets = networkInterfaces();
|
|
@@ -51,7 +52,7 @@ export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuilt
|
|
|
51
52
|
factory: () => {
|
|
52
53
|
if (!deps.daemonHomeDir) throw new Error('daemonHomeDir must be provided to the session panel factory via BuiltinPanelDeps');
|
|
53
54
|
const daemonHomeDir = deps.daemonHomeDir;
|
|
54
|
-
const tokenRecord = getOrCreateCompanionToken(
|
|
55
|
+
const tokenRecord = getOrCreateCompanionToken(GOODVIBES_AGENT_PAIRING_SURFACE, { daemonHomeDir });
|
|
55
56
|
const daemonPort = deps.configManager.get('controlPlane.port');
|
|
56
57
|
const daemonHost = String(process.env['GOODVIBES_DAEMON_HOST'] ?? getLocalNetworkIp());
|
|
57
58
|
const daemonUrl = `http://${daemonHost}:${daemonPort}`;
|
|
@@ -60,7 +61,7 @@ export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuilt
|
|
|
60
61
|
daemonUrl,
|
|
61
62
|
token: tokenRecord.token,
|
|
62
63
|
password: bootstrapPassword,
|
|
63
|
-
surface:
|
|
64
|
+
surface: GOODVIBES_AGENT_PAIRING_SURFACE,
|
|
64
65
|
});
|
|
65
66
|
const regenerate = (): typeof connectionInfo => {
|
|
66
67
|
const newRecord = regenerateCompanionToken({ daemonHomeDir });
|
|
@@ -68,7 +69,7 @@ export function registerSessionPanels(manager: PanelManager, deps: ResolvedBuilt
|
|
|
68
69
|
daemonUrl,
|
|
69
70
|
token: newRecord.token,
|
|
70
71
|
password: bootstrapPassword,
|
|
71
|
-
surface:
|
|
72
|
+
surface: GOODVIBES_AGENT_PAIRING_SURFACE,
|
|
72
73
|
});
|
|
73
74
|
};
|
|
74
75
|
return new QrPanel(connectionInfo, regenerate, copyToClipboard);
|
package/src/panels/index.ts
CHANGED
|
@@ -6,13 +6,9 @@ export { TokenBudgetPanel } from './token-budget-panel.ts';
|
|
|
6
6
|
export { CostTrackerPanel } from './cost-tracker-panel.ts';
|
|
7
7
|
export { AgentInspectorPanel } from './agent-inspector-panel.ts';
|
|
8
8
|
export { AgentLogsPanel } from './agent-logs-panel.ts';
|
|
9
|
-
export { WrfcPanel } from './wrfc-panel.ts';
|
|
10
9
|
export { ProviderHealthPanel } from './provider-health-panel.ts';
|
|
11
10
|
export { ProviderHealthTracker } from './provider-health-tracker.ts';
|
|
12
11
|
export type { ProviderHealth, ProviderStatus } from './provider-health-tracker.ts';
|
|
13
|
-
export { GitPanel } from './git-panel.ts';
|
|
14
|
-
export { SymbolOutlinePanel } from './symbol-outline-panel.ts';
|
|
15
|
-
export type { SymbolEntry, SymbolKind } from './symbol-outline-panel.ts';
|
|
16
12
|
export { ProviderStatsPanel } from './provider-stats-panel.ts';
|
|
17
13
|
export { SessionBrowserPanel } from './session-browser-panel.ts';
|
|
18
14
|
export { DocsPanel } from './docs-panel.ts';
|
|
@@ -38,7 +34,6 @@ export { SubscriptionPanel } from './subscription-panel.ts';
|
|
|
38
34
|
export { HooksPanel } from './hooks-panel.ts';
|
|
39
35
|
export { SecurityPanel } from './security-panel.ts';
|
|
40
36
|
export { MarketplacePanel } from './marketplace-panel.ts';
|
|
41
|
-
export { SandboxPanel } from './sandbox-panel.ts';
|
|
42
37
|
export { ApprovalPanel } from './approval-panel.ts';
|
|
43
38
|
export { KnowledgePanel } from './knowledge-panel.ts';
|
|
44
39
|
export { SystemMessagesPanel } from './system-messages-panel.ts';
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* OrchestrationPanel — displays task graphs, node contracts, recursion guards
|
|
3
|
-
* and WRFC-visible orchestration state.
|
|
2
|
+
* OrchestrationPanel — displays task graphs, node contracts, and recursion guards.
|
|
4
3
|
*
|
|
5
4
|
* Migrated (Wave B2): extends ScrollableListPanel<OrchestrationGraphRecord>.
|
|
6
5
|
* Navigation (up/down/j/k) is handled by the base class.
|
|
@@ -102,7 +101,7 @@ export class OrchestrationPanel extends ScrollableListPanel<OrchestrationGraphRe
|
|
|
102
101
|
// ---------------------------------------------------------------------------
|
|
103
102
|
|
|
104
103
|
public render(width: number, height: number): Line[] {
|
|
105
|
-
const intro = '
|
|
104
|
+
const intro = 'Read-only task graph posture, node contracts, and recursion guard state. Agent does not start local worker chains.';
|
|
106
105
|
|
|
107
106
|
if (!this.readModel) {
|
|
108
107
|
this.needsRender = false;
|
|
@@ -134,7 +133,7 @@ export class OrchestrationPanel extends ScrollableListPanel<OrchestrationGraphRe
|
|
|
134
133
|
{ label: 'failed', value: String(snapshot.totalFailedGraphs), valueColor: snapshot.totalFailedGraphs > 0 ? C.failed : C.dim },
|
|
135
134
|
{ label: 'guards', value: String(snapshot.recursionGuardTrips), valueColor: snapshot.recursionGuardTrips > 0 ? C.blocked : C.dim },
|
|
136
135
|
], C),
|
|
137
|
-
buildGuidanceLine(width, '/orchestration', 'inspect
|
|
136
|
+
buildGuidanceLine(width, '/orchestration', 'inspect graph health without spawning local workers', C),
|
|
138
137
|
];
|
|
139
138
|
if (graphs.length === 0) {
|
|
140
139
|
this.needsRender = false;
|
|
@@ -148,7 +147,7 @@ export class OrchestrationPanel extends ScrollableListPanel<OrchestrationGraphRe
|
|
|
148
147
|
...buildEmptyState(
|
|
149
148
|
width,
|
|
150
149
|
this.getEmptyStateMessage(),
|
|
151
|
-
'Graphs, nodes, child contracts, and recursion guard trips
|
|
150
|
+
'Graphs, nodes, child contracts, and recursion guard trips appear here only if a runtime record already exists.',
|
|
152
151
|
[
|
|
153
152
|
{ command: '/tasks', summary: 'create or inspect task flows that feed orchestration graphs' },
|
|
154
153
|
{ command: '/communication', summary: 'review structured agent communication alongside graph execution' },
|