@pellux/goodvibes-tui 0.19.53 → 0.19.55
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 +35 -0
- package/README.md +10 -13
- package/docs/foundation-artifacts/knowledge-store.sql +27 -0
- package/docs/foundation-artifacts/operator-contract.json +15736 -7265
- package/package.json +2 -2
- package/src/audio/spoken-turn-controller.ts +4 -1
- package/src/input/command-args-hint.ts +36 -0
- package/src/input/command-registry.ts +3 -1
- package/src/input/commands/config.ts +7 -521
- package/src/input/commands/knowledge.ts +111 -1
- package/src/input/commands/local-runtime.ts +0 -80
- package/src/input/commands/operator-runtime.ts +3 -3
- package/src/input/commands/planning-runtime.ts +83 -34
- package/src/input/commands/shell-core.ts +2 -34
- package/src/input/commands/tts-runtime.ts +1 -389
- package/src/input/commands.ts +0 -2
- package/src/input/handler-modal-routes.ts +61 -7
- package/src/input/handler-modal-token-routes.ts +1 -0
- package/src/input/handler-picker-routes.ts +50 -4
- package/src/input/model-picker-provider-filter.ts +28 -0
- package/src/input/model-picker-types.ts +12 -0
- package/src/input/model-picker.ts +65 -23
- package/src/input/selection-modal.ts +1 -1
- package/src/input/settings-modal-behavior.ts +2 -0
- package/src/input/settings-modal-subscriptions.ts +95 -0
- package/src/input/settings-modal-types.ts +50 -3
- package/src/input/settings-modal.ts +106 -134
- package/src/input/tts-settings-actions.ts +100 -0
- package/src/main.ts +50 -45
- package/src/panels/builtin/agent.ts +15 -0
- package/src/panels/builtin/shared.ts +17 -0
- package/src/panels/project-planning-panel.ts +370 -0
- package/src/planning/project-planning-coordinator.ts +249 -0
- package/src/renderer/compositor.ts +2 -1
- package/src/renderer/conversation-overlays.ts +4 -5
- package/src/renderer/model-workspace.ts +488 -0
- package/src/renderer/settings-modal-helpers.ts +16 -1
- package/src/renderer/settings-modal.ts +616 -716
- package/src/runtime/bootstrap-command-context.ts +6 -0
- package/src/runtime/bootstrap-command-parts.ts +5 -0
- package/src/runtime/bootstrap-shell.ts +2 -0
- package/src/runtime/services.ts +33 -2
- package/src/runtime/terminal-output-guard.ts +228 -0
- package/src/runtime/ui-services.ts +4 -0
- package/src/shell/ui-openers.ts +59 -3
- package/src/utils/clipboard.ts +2 -1
- package/src/version.ts +1 -1
- package/src/input/commands/permissions-runtime.ts +0 -104
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
import type { KnowledgeService } from '@pellux/goodvibes-sdk/platform/knowledge/index';
|
|
1
2
|
import type { CommandContext, SlashCommand } from '../command-registry.ts';
|
|
2
3
|
|
|
3
4
|
const KNOWLEDGE_REVIEW_ACTIONS = ['accept', 'reject', 'resolve', 'reopen', 'edit', 'forget'] as const;
|
|
4
5
|
|
|
5
6
|
type KnowledgeReviewAction = typeof KNOWLEDGE_REVIEW_ACTIONS[number];
|
|
7
|
+
type KnowledgeAskInput = Parameters<KnowledgeService['ask']>[0];
|
|
8
|
+
type KnowledgeAskResult = Awaited<ReturnType<KnowledgeService['ask']>>;
|
|
9
|
+
type KnowledgeAskMode = NonNullable<KnowledgeAskInput['mode']>;
|
|
6
10
|
|
|
7
11
|
function requireKnowledgeApi(context: CommandContext) {
|
|
8
12
|
const knowledgeApi = context.clients?.knowledgeApi;
|
|
@@ -24,6 +28,13 @@ function readStringListFlag(args: string[], name: string): string[] {
|
|
|
24
28
|
return value.split(',').map((entry) => entry.trim()).filter(Boolean);
|
|
25
29
|
}
|
|
26
30
|
|
|
31
|
+
function readPositiveIntFlag(args: string[], name: string, fallback: number): number {
|
|
32
|
+
const raw = readFlag(args, name);
|
|
33
|
+
if (!raw) return fallback;
|
|
34
|
+
const parsed = Number.parseInt(raw, 10);
|
|
35
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
36
|
+
}
|
|
37
|
+
|
|
27
38
|
function readJsonObjectFlag(args: string[], name: string): Record<string, unknown> | null | undefined {
|
|
28
39
|
const value = readFlag(args, name);
|
|
29
40
|
if (!value) return undefined;
|
|
@@ -46,12 +57,84 @@ function positionalArgs(args: string[], valuedFlags: readonly string[] = []): st
|
|
|
46
57
|
});
|
|
47
58
|
}
|
|
48
59
|
|
|
60
|
+
function requireKnowledgeAsk(context: CommandContext): ((input: KnowledgeAskInput) => Promise<KnowledgeAskResult>) | null {
|
|
61
|
+
const serviceAsk = context.extensions.knowledgeService?.ask?.bind(context.extensions.knowledgeService);
|
|
62
|
+
if (serviceAsk) return serviceAsk;
|
|
63
|
+
|
|
64
|
+
const clientAsk = (context.clients?.knowledgeApi as unknown as { ask?: (input: KnowledgeAskInput) => Promise<KnowledgeAskResult> } | undefined)?.ask;
|
|
65
|
+
if (clientAsk) return clientAsk;
|
|
66
|
+
|
|
67
|
+
context.print('[knowledge] Knowledge ask is not available in this runtime.');
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function cleanInline(value: unknown): string {
|
|
72
|
+
return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim() : '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function nodeLabel(node: { readonly kind?: string; readonly title?: string; readonly summary?: string; readonly confidence?: number }): string {
|
|
76
|
+
const kind = cleanInline(node.kind) || 'node';
|
|
77
|
+
const title = cleanInline(node.title) || 'untitled';
|
|
78
|
+
const summary = cleanInline(node.summary);
|
|
79
|
+
const confidence = typeof node.confidence === 'number' ? ` confidence=${node.confidence}` : '';
|
|
80
|
+
return summary ? `[${kind}] ${title}${confidence} - ${summary}` : `[${kind}] ${title}${confidence}`;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function sourceLabel(source: {
|
|
84
|
+
readonly id?: string;
|
|
85
|
+
readonly sourceType?: string;
|
|
86
|
+
readonly title?: string;
|
|
87
|
+
readonly canonicalUri?: string;
|
|
88
|
+
readonly sourceUri?: string;
|
|
89
|
+
readonly summary?: string;
|
|
90
|
+
readonly status?: string;
|
|
91
|
+
}): string {
|
|
92
|
+
const title = cleanInline(source.title) || cleanInline(source.canonicalUri) || cleanInline(source.sourceUri) || cleanInline(source.id) || 'untitled';
|
|
93
|
+
const type = cleanInline(source.sourceType) || 'source';
|
|
94
|
+
const status = cleanInline(source.status);
|
|
95
|
+
const summary = cleanInline(source.summary);
|
|
96
|
+
const suffix = status ? `/${status}` : '';
|
|
97
|
+
return summary ? `[${type}${suffix}] ${title} - ${summary}` : `[${type}${suffix}] ${title}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function renderKnowledgeAskResult(result: KnowledgeAskResult): string {
|
|
101
|
+
const answer = result.answer;
|
|
102
|
+
const lines = [
|
|
103
|
+
`[knowledge] ${result.query}`,
|
|
104
|
+
answer.text,
|
|
105
|
+
'',
|
|
106
|
+
`mode: ${answer.mode} confidence: ${answer.confidence} synthesized: ${answer.synthesized ? 'yes' : 'no'}`,
|
|
107
|
+
];
|
|
108
|
+
|
|
109
|
+
if (answer.sources.length > 0) {
|
|
110
|
+
lines.push('', 'Sources:');
|
|
111
|
+
for (const source of answer.sources) lines.push(` - ${sourceLabel(source)}`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (answer.facts.length > 0) {
|
|
115
|
+
lines.push('', 'Facts:');
|
|
116
|
+
for (const fact of answer.facts) lines.push(` - ${nodeLabel(fact)}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (answer.linkedObjects.length > 0) {
|
|
120
|
+
lines.push('', 'Linked objects:');
|
|
121
|
+
for (const object of answer.linkedObjects) lines.push(` - ${nodeLabel(object)}`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (answer.gaps.length > 0) {
|
|
125
|
+
lines.push('', 'Gaps:');
|
|
126
|
+
for (const gap of answer.gaps) lines.push(` - ${nodeLabel(gap)}`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return lines.join('\n');
|
|
130
|
+
}
|
|
131
|
+
|
|
49
132
|
export const knowledgeCommand: SlashCommand = {
|
|
50
133
|
name: 'knowledge',
|
|
51
134
|
aliases: ['know', 'kb'],
|
|
52
135
|
description: 'Structured knowledge graph: ingest URLs/bookmarks, inspect issues, and build compact prompt packets.',
|
|
53
136
|
usage: '<subcommand> [args]',
|
|
54
|
-
argsHint: 'status|ingest-url|import-bookmarks|import-urls|list|search|get|queue|review-issue|candidates|reports|schedules|lint|packet|explain|reindex|consolidate',
|
|
137
|
+
argsHint: 'status|ask|ingest-url|import-bookmarks|import-urls|list|search|get|queue|review-issue|candidates|reports|schedules|lint|packet|explain|reindex|consolidate',
|
|
55
138
|
handler: async (args: string[], context: CommandContext): Promise<void> => {
|
|
56
139
|
const knowledge = requireKnowledgeApi(context);
|
|
57
140
|
if (!knowledge) {
|
|
@@ -65,6 +148,32 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
65
148
|
const rest = args.slice(1);
|
|
66
149
|
|
|
67
150
|
switch (sub) {
|
|
151
|
+
case 'ask': {
|
|
152
|
+
const ask = requireKnowledgeAsk(context);
|
|
153
|
+
if (!ask) return;
|
|
154
|
+
const valuedFlags = ['--space', '--knowledge-space', '--limit', '--mode'];
|
|
155
|
+
const query = positionalArgs(rest, valuedFlags).join(' ').trim();
|
|
156
|
+
if (!query) {
|
|
157
|
+
context.print('[knowledge] Usage: /knowledge ask <query> [--space <knowledgeSpaceId>] [--limit <n>] [--mode <concise|standard|detailed>]');
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
const requestedMode = readFlag(rest, '--mode') as KnowledgeAskMode | undefined;
|
|
161
|
+
const mode: KnowledgeAskMode = requestedMode && ['concise', 'standard', 'detailed'].includes(requestedMode)
|
|
162
|
+
? requestedMode
|
|
163
|
+
: 'standard';
|
|
164
|
+
const result = await ask({
|
|
165
|
+
query,
|
|
166
|
+
knowledgeSpaceId: readFlag(rest, '--space') ?? readFlag(rest, '--knowledge-space'),
|
|
167
|
+
limit: readPositiveIntFlag(rest, '--limit', 10),
|
|
168
|
+
mode,
|
|
169
|
+
includeSources: true,
|
|
170
|
+
includeConfidence: true,
|
|
171
|
+
includeLinkedObjects: true,
|
|
172
|
+
});
|
|
173
|
+
context.print(renderKnowledgeAskResult(result));
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
|
|
68
177
|
case 'status': {
|
|
69
178
|
const status = await knowledge.status.get();
|
|
70
179
|
context.print([
|
|
@@ -399,6 +508,7 @@ export const knowledgeCommand: SlashCommand = {
|
|
|
399
508
|
context.print([
|
|
400
509
|
'Usage: /knowledge <subcommand>',
|
|
401
510
|
' status',
|
|
511
|
+
' ask <query> [--space <knowledgeSpaceId>] [--limit <n>] [--mode <concise|standard|detailed>]',
|
|
402
512
|
' ingest-url <url> [--title <title>] [--tags <a,b>] [--folder <path>]',
|
|
403
513
|
' import-bookmarks <path>',
|
|
404
514
|
' import-urls <path>',
|
|
@@ -4,8 +4,6 @@ import { readFile } from 'node:fs/promises';
|
|
|
4
4
|
import type { CommandRegistry, CommandContext } from '../command-registry.ts';
|
|
5
5
|
import type { SelectionItem } from '../selection-modal.ts';
|
|
6
6
|
import type { ContentPart } from '@pellux/goodvibes-sdk/platform/providers/interface';
|
|
7
|
-
import type { ConfigKey } from '../../config/index.ts';
|
|
8
|
-
import { CONFIG_SCHEMA } from '../../config/index.ts';
|
|
9
7
|
import { resolveAndValidatePath } from '@pellux/goodvibes-sdk/platform/utils/path-safety';
|
|
10
8
|
import { BUILTIN_SECRET_PROVIDER_SOURCES, describeSecretRef, isSecretRefInput, resolveSecretRef } from '@pellux/goodvibes-sdk/platform/config/secret-refs';
|
|
11
9
|
import { openCommandPanel, requireBookmarkManager, requireProviderApi, requireSecretsManager } from './runtime-services.ts';
|
|
@@ -246,84 +244,6 @@ export function registerLocalRuntimeCommands(registry: CommandRegistry): void {
|
|
|
246
244
|
},
|
|
247
245
|
});
|
|
248
246
|
|
|
249
|
-
registry.register({
|
|
250
|
-
name: 'danger',
|
|
251
|
-
argsHint: '[key] [value]',
|
|
252
|
-
description: '⚠ Danger zone settings (agent recursion, daemon, HTTP listener)',
|
|
253
|
-
usage: '[key] [value]',
|
|
254
|
-
handler(args, ctx) {
|
|
255
|
-
if (args.length === 0) {
|
|
256
|
-
if (ctx.openSelection) {
|
|
257
|
-
const cm = ctx.platform.configManager;
|
|
258
|
-
const dangerObj = cm.getAll().danger as Record<string, unknown>;
|
|
259
|
-
const items: SelectionItem[] = Object.entries(dangerObj).map(([field, val]) => {
|
|
260
|
-
const key = `danger.${field}`;
|
|
261
|
-
const schema = CONFIG_SCHEMA.find(s => s.key === key);
|
|
262
|
-
const toggleable = schema?.type === 'boolean';
|
|
263
|
-
return {
|
|
264
|
-
id: key,
|
|
265
|
-
label: key,
|
|
266
|
-
detail: String(val),
|
|
267
|
-
fg: '#ef4444',
|
|
268
|
-
adjustable: toggleable,
|
|
269
|
-
primaryAction: toggleable ? 'toggle' : 'select',
|
|
270
|
-
actions: schema ? `${toggleable ? '[Space/Enter] toggle [←/→] set' : '[Enter] inspect'} ${schema.description}` : undefined,
|
|
271
|
-
};
|
|
272
|
-
});
|
|
273
|
-
ctx.openSelection('⚠ Danger Zone', items, { allowSearch: false }, (result) => {
|
|
274
|
-
if (!result) return;
|
|
275
|
-
const key = result.item.id as ConfigKey;
|
|
276
|
-
const schema = CONFIG_SCHEMA.find(s => s.key === key);
|
|
277
|
-
if (result.action === 'toggle' && schema) {
|
|
278
|
-
const currentVal = cm.get(key);
|
|
279
|
-
let newVal: unknown = currentVal;
|
|
280
|
-
if (schema.type === 'boolean') {
|
|
281
|
-
newVal = !currentVal;
|
|
282
|
-
cm.setDynamic(key, newVal);
|
|
283
|
-
} else if (schema.type === 'number') {
|
|
284
|
-
ctx.print(`Current: ${key} = ${String(currentVal)}. Use /danger ${key.replace('danger.', '')} <value> to set.`);
|
|
285
|
-
return;
|
|
286
|
-
}
|
|
287
|
-
result.item.detail = String(newVal);
|
|
288
|
-
ctx.renderRequest();
|
|
289
|
-
} else if ((result.action === 'increment' || result.action === 'decrement') && schema?.type === 'boolean') {
|
|
290
|
-
const newVal = result.action === 'increment';
|
|
291
|
-
cm.setDynamic(key, newVal);
|
|
292
|
-
result.item.detail = String(newVal);
|
|
293
|
-
ctx.renderRequest();
|
|
294
|
-
}
|
|
295
|
-
});
|
|
296
|
-
} else {
|
|
297
|
-
const dangerObj = ctx.platform.configManager.getAll().danger as Record<string, unknown>;
|
|
298
|
-
ctx.print(['⚠ Danger Zone Settings:', '', ...Object.entries(dangerObj).map(([field, val]) => ` ${`danger.${field}`.padEnd(36)} ${String(val)}`)].join('\n'));
|
|
299
|
-
}
|
|
300
|
-
return;
|
|
301
|
-
}
|
|
302
|
-
const key = args[0].startsWith('danger.') ? args[0] : `danger.${args[0]}`;
|
|
303
|
-
if (args.length === 1) {
|
|
304
|
-
try {
|
|
305
|
-
ctx.print(`${key} = ${String(ctx.platform.configManager.get(key as Parameters<typeof ctx.platform.configManager.get>[0]))}`);
|
|
306
|
-
} catch (e) {
|
|
307
|
-
ctx.print(`Error: ${summarizeError(e)}`);
|
|
308
|
-
}
|
|
309
|
-
return;
|
|
310
|
-
}
|
|
311
|
-
try {
|
|
312
|
-
const schema = CONFIG_SCHEMA.find(s => s.key === key);
|
|
313
|
-
if (!schema) {
|
|
314
|
-
ctx.print(`Unknown danger key: ${key}`);
|
|
315
|
-
return;
|
|
316
|
-
}
|
|
317
|
-
const rawValue = args.slice(1).join(' ');
|
|
318
|
-
const coerced: unknown = schema.type === 'boolean' ? (rawValue === 'true' || rawValue === '1' || rawValue === 'yes') : schema.type === 'number' ? Number(rawValue) : rawValue;
|
|
319
|
-
ctx.platform.configManager.setDynamic(key as Parameters<typeof ctx.platform.configManager.get>[0], coerced);
|
|
320
|
-
ctx.print(`⚠ Set ${key} = ${String(coerced)}`);
|
|
321
|
-
} catch (e) {
|
|
322
|
-
ctx.print(`Error: ${summarizeError(e)}`);
|
|
323
|
-
}
|
|
324
|
-
},
|
|
325
|
-
});
|
|
326
|
-
|
|
327
247
|
registry.register({
|
|
328
248
|
name: 'image',
|
|
329
249
|
aliases: ['img'],
|
|
@@ -12,10 +12,10 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
|
|
|
12
12
|
registry.register({
|
|
13
13
|
name: 'settings',
|
|
14
14
|
aliases: ['cfg-ui'],
|
|
15
|
-
description: 'Open the
|
|
15
|
+
description: 'Open the fullscreen configuration workspace',
|
|
16
16
|
handler(_args, ctx) {
|
|
17
17
|
if (ctx.openSettingsModal) ctx.openSettingsModal();
|
|
18
|
-
else ctx.print('
|
|
18
|
+
else ctx.print('Configuration workspace is not available in this runtime.');
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
21
|
|
|
@@ -72,7 +72,7 @@ export function registerOperatorRuntimeCommands(registry: CommandRegistry): void
|
|
|
72
72
|
ctx.openProfilePicker();
|
|
73
73
|
} else {
|
|
74
74
|
const profiles = requireProfileManager(ctx).list();
|
|
75
|
-
if (profiles.length === 0) ctx.print('No profiles saved.
|
|
75
|
+
if (profiles.length === 0) ctx.print('No profiles saved. Open /profiles and press s to save the current settings as a profile.');
|
|
76
76
|
else ctx.print(['Saved profiles:', ...profiles.map(p => ` ${p.name}`)].join('\n'));
|
|
77
77
|
}
|
|
78
78
|
},
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
import type { CommandRegistry } from '../command-registry.ts';
|
|
2
|
-
import {
|
|
3
|
-
import { requireAdaptivePlanner, requirePlanManager, requireSessionLineageTracker } from './runtime-services.ts';
|
|
2
|
+
import { requirePlanManager, requireSessionLineageTracker } from './runtime-services.ts';
|
|
4
3
|
|
|
5
4
|
export function registerPlanningRuntimeCommands(registry: CommandRegistry): void {
|
|
6
5
|
registry.register({
|
|
7
6
|
name: 'plan',
|
|
8
|
-
description: '
|
|
9
|
-
usage: '[list | show <id> | mode | explain | override <strategy> | status | clear | <
|
|
10
|
-
argsHint: '[
|
|
11
|
-
handler(args, ctx) {
|
|
7
|
+
description: 'Inspect or seed TUI-owned project planning state',
|
|
8
|
+
usage: '[panel | approve | list | show <id> | mode | explain | override <strategy> | status | clear | <planning goal>]',
|
|
9
|
+
argsHint: '[panel|approve|status|<goal>]',
|
|
10
|
+
async handler(args, ctx) {
|
|
12
11
|
const planManager = requirePlanManager(ctx);
|
|
13
|
-
const adaptivePlanner = requireAdaptivePlanner(ctx);
|
|
14
12
|
const sessionLineageTracker = requireSessionLineageTracker(ctx);
|
|
15
13
|
const plannerSubs = ['mode', 'explain', 'override', 'status', 'clear'];
|
|
16
14
|
if (args.length > 0 && plannerSubs.includes(args[0].toLowerCase())) {
|
|
@@ -21,10 +19,29 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
21
19
|
return;
|
|
22
20
|
}
|
|
23
21
|
|
|
22
|
+
const projectPlanningService = ctx.workspace.projectPlanningService;
|
|
23
|
+
const projectId = ctx.workspace.projectPlanningProjectId;
|
|
24
|
+
const openProjectPlanningPanel = () => ctx.showPanel?.('project-planning');
|
|
25
|
+
|
|
24
26
|
if (args.length === 0) {
|
|
27
|
+
if (projectPlanningService && projectId) {
|
|
28
|
+
const [status, evaluation] = await Promise.all([
|
|
29
|
+
projectPlanningService.status({ projectId }),
|
|
30
|
+
projectPlanningService.evaluate({ projectId }),
|
|
31
|
+
]);
|
|
32
|
+
openProjectPlanningPanel();
|
|
33
|
+
ctx.print(
|
|
34
|
+
`Project planning: ${evaluation.readiness}\n` +
|
|
35
|
+
`Project: ${status.projectId}\n` +
|
|
36
|
+
`Knowledge space: ${status.knowledgeSpaceId}\n` +
|
|
37
|
+
`Artifacts: ${status.counts.states} state, ${status.counts.decisions} decisions, ${status.counts.languageArtifacts} language\n` +
|
|
38
|
+
(evaluation.nextQuestion ? `Next question: ${evaluation.nextQuestion.prompt}` : 'No next question recorded.'),
|
|
39
|
+
);
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
25
42
|
const active = planManager.getActive(ctx.session.runtime.sessionId);
|
|
26
43
|
if (!active) {
|
|
27
|
-
ctx.print('No active
|
|
44
|
+
ctx.print('No active execution plan.');
|
|
28
45
|
return;
|
|
29
46
|
}
|
|
30
47
|
const summary = planManager.getSummary(active);
|
|
@@ -32,6 +49,40 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
32
49
|
return;
|
|
33
50
|
}
|
|
34
51
|
|
|
52
|
+
if (args[0] === 'panel') {
|
|
53
|
+
openProjectPlanningPanel();
|
|
54
|
+
ctx.print('Opened project planning panel.');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (args[0] === 'approve') {
|
|
59
|
+
if (!projectPlanningService || !projectId) {
|
|
60
|
+
ctx.print('Project planning service is not available in this runtime.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const current = await projectPlanningService.getState({ projectId });
|
|
64
|
+
if (!current.state) {
|
|
65
|
+
ctx.print('No project planning state exists to approve.');
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const result = await projectPlanningService.upsertState({
|
|
69
|
+
projectId,
|
|
70
|
+
state: {
|
|
71
|
+
...current.state,
|
|
72
|
+
executionApproved: true,
|
|
73
|
+
metadata: {
|
|
74
|
+
...(current.state.metadata ?? {}),
|
|
75
|
+
approvedFrom: 'plan-command',
|
|
76
|
+
approvedAt: Date.now(),
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
const evaluation = await projectPlanningService.evaluate({ projectId });
|
|
81
|
+
openProjectPlanningPanel();
|
|
82
|
+
ctx.print(`Project planning approved. Readiness: ${evaluation.readiness}. State: ${result.state?.id ?? 'current'}.`);
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
35
86
|
if (args[0] === 'list') {
|
|
36
87
|
const plans = planManager.list();
|
|
37
88
|
if (plans.length === 0) {
|
|
@@ -62,36 +113,34 @@ export function registerPlanningRuntimeCommands(registry: CommandRegistry): void
|
|
|
62
113
|
}
|
|
63
114
|
|
|
64
115
|
const taskDescription = args.join(' ');
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
116
|
+
if (!projectPlanningService || !projectId) {
|
|
117
|
+
ctx.print('Project planning service is not available in this runtime.');
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
const result = await projectPlanningService.upsertState({
|
|
121
|
+
projectId,
|
|
122
|
+
state: {
|
|
123
|
+
goal: taskDescription,
|
|
124
|
+
knownContext: [
|
|
125
|
+
`Workspace planning was seeded from the TUI /plan command.`,
|
|
126
|
+
],
|
|
127
|
+
metadata: {
|
|
128
|
+
active: true,
|
|
129
|
+
owner: 'tui',
|
|
130
|
+
source: 'plan-command',
|
|
131
|
+
lastPromptAt: Date.now(),
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
const evaluation = await projectPlanningService.evaluate({ projectId });
|
|
69
136
|
sessionLineageTracker.setOriginalTask(taskDescription.slice(0, 200));
|
|
137
|
+
openProjectPlanningPanel();
|
|
70
138
|
|
|
71
139
|
ctx.print(
|
|
72
|
-
`
|
|
73
|
-
`
|
|
74
|
-
`
|
|
75
|
-
'The model will write the execution plan — agents will be spawned automatically.',
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
ctx.session.conversationManager.addSystemMessage(
|
|
79
|
-
`You are creating an execution plan for the following task: "${taskDescription}"\n\n` +
|
|
80
|
-
'Output the plan in EXACTLY this markdown format and nothing else:\n\n' +
|
|
81
|
-
'## Phase 1: [Phase Name] [PENDING]\n' +
|
|
82
|
-
'- [ ] [Task description] — PENDING\n' +
|
|
83
|
-
'- [ ] [Task description] — PENDING (depends: [other task description])\n\n' +
|
|
84
|
-
'## Phase 2: [Phase Name] [PENDING]\n' +
|
|
85
|
-
'- [ ] [Task description] — PENDING (depends: [Phase 1 task description])\n\n' +
|
|
86
|
-
'Rules:\n' +
|
|
87
|
-
'- Each item must be a concrete, independently executable task\n' +
|
|
88
|
-
'- Use (depends: ...) only where execution order truly matters\n' +
|
|
89
|
-
'- Items without dependencies in the same phase can run in parallel\n' +
|
|
90
|
-
'- Keep phases to 2-4 items each, aim for maximum parallelism\n' +
|
|
91
|
-
'- Output ONLY the plan markdown — the system will parse it and spawn agents automatically',
|
|
140
|
+
`Project planning seeded: "${result.state?.goal ?? taskDescription}"\n` +
|
|
141
|
+
`Readiness: ${evaluation.readiness}\n` +
|
|
142
|
+
(evaluation.nextQuestion ? `Next question: ${evaluation.nextQuestion.prompt}` : 'No next question recorded.'),
|
|
92
143
|
);
|
|
93
|
-
|
|
94
|
-
ctx.activatePlan?.(plan.id, taskDescription);
|
|
95
144
|
},
|
|
96
145
|
});
|
|
97
146
|
}
|
|
@@ -103,12 +103,8 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
103
103
|
{ id: '/model', label: '/model [id]', detail: 'Select LLM model', category: 'Model & Provider' },
|
|
104
104
|
{ id: '/provider', label: '/provider [name]', detail: 'Switch provider', category: 'Model & Provider' },
|
|
105
105
|
{ id: '/effort', label: '/effort [level]', detail: 'Reasoning effort (instant/low/medium/high)', category: 'Model & Provider' },
|
|
106
|
-
{ id: '/config', label: '/config [key]
|
|
107
|
-
{ id: '/config diff', label: '/config diff', detail: 'Show changed settings', category: 'Config & Display' },
|
|
108
|
-
{ id: '/config reset', label: '/config reset [key]', detail: 'Reset to defaults', category: 'Config & Display' },
|
|
109
|
-
{ id: '/config profile', label: '/config profile ...', detail: 'Save/load/list/delete profiles', category: 'Config & Display' },
|
|
106
|
+
{ id: '/config', label: '/config [category|key]', detail: 'Open fullscreen configuration workspace', category: 'Config & Display' },
|
|
110
107
|
{ id: '/debug', label: '/debug', detail: 'Toggle debug mode', category: 'Config & Display' },
|
|
111
|
-
{ id: '/lines', label: '/lines', detail: 'Set line numbers: all, code, or off', category: 'Config & Display' },
|
|
112
108
|
{ id: '/expand', label: '/expand [type]', detail: 'Expand blocks (all|thinking|tool|code)', category: 'Config & Display' },
|
|
113
109
|
{ id: '/collapse', label: '/collapse [type]', detail: 'Collapse blocks', category: 'Config & Display' },
|
|
114
110
|
{ id: '/bookmarks', label: '/bookmarks', detail: 'List bookmarked blocks', category: 'Config & Display' },
|
|
@@ -140,11 +136,9 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
140
136
|
{ id: '/template save', label: '/template save <name>', detail: 'Save prompt as template', category: 'Templates' },
|
|
141
137
|
{ id: '/template use', label: '/template use <name>', detail: 'Execute template', category: 'Templates' },
|
|
142
138
|
{ id: '/tools', label: '/tools', detail: 'List available tools', category: 'Tools & System' },
|
|
143
|
-
{ id: '/permissions', label: '/permissions', detail: 'Permission settings', category: 'Tools & System' },
|
|
144
139
|
{ id: '/shortcuts', label: '/shortcuts', detail: 'View keyboard shortcuts reference', category: 'Tools & System' },
|
|
145
140
|
{ id: '/commands', label: '/commands', detail: 'Browse all commands in a scrollable list', category: 'Tools & System' },
|
|
146
141
|
{ id: '/secrets', label: '/secrets set|link|get|test|list|delete', detail: 'Manage encrypted and provider-backed secrets', category: 'Tools & System' },
|
|
147
|
-
{ id: '/danger', label: '/danger [key] [value]', detail: 'DANGEROUS SETTINGS', category: 'Tools & System', fg: '#ef4444' },
|
|
148
142
|
{ id: '/help', label: '/help', detail: 'This help', category: 'Tools & System' },
|
|
149
143
|
{ id: '/quit', label: '/quit', detail: 'Exit', category: 'Tools & System' },
|
|
150
144
|
{ id: '/wq', label: '/wq', detail: 'Commit all git changes and then exit', category: 'Tools & System' },
|
|
@@ -161,7 +155,7 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
161
155
|
});
|
|
162
156
|
return;
|
|
163
157
|
}
|
|
164
|
-
ctx.print('Use /help to open the help modal. Commands: /model, /provider, /config, /template, /tools, /
|
|
158
|
+
ctx.print('Use /help to open the help modal. Commands: /model, /provider, /config, /template, /tools, /sessions, /bookmarks, /save, /load, /undo, /redo, /retry, /clear, /reset, /compact, /export, /title, /effort, /expand, /collapse, /debug, /quit, /wq');
|
|
165
159
|
},
|
|
166
160
|
});
|
|
167
161
|
|
|
@@ -292,30 +286,4 @@ export function registerShellCoreCommands(registry: CommandRegistry): void {
|
|
|
292
286
|
},
|
|
293
287
|
});
|
|
294
288
|
|
|
295
|
-
registry.register({
|
|
296
|
-
name: 'lines',
|
|
297
|
-
aliases: [],
|
|
298
|
-
description: 'Set line-number display: all, code, or off',
|
|
299
|
-
handler(args, ctx) {
|
|
300
|
-
const current = ctx.platform.configManager.get('display.lineNumbers');
|
|
301
|
-
const aliases: Record<string, 'all' | 'code' | 'off'> = {
|
|
302
|
-
on: 'all',
|
|
303
|
-
all: 'all',
|
|
304
|
-
code: 'code',
|
|
305
|
-
blocks: 'code',
|
|
306
|
-
off: 'off',
|
|
307
|
-
};
|
|
308
|
-
const cycle: Array<'all' | 'code' | 'off'> = ['all', 'code', 'off'];
|
|
309
|
-
const requested = args[0]?.toLowerCase();
|
|
310
|
-
const next = requested ? aliases[requested] : cycle[(cycle.indexOf(current) + 1) % cycle.length]!;
|
|
311
|
-
if (requested && !next) {
|
|
312
|
-
ctx.print('Usage: /lines [all|code|off]');
|
|
313
|
-
return;
|
|
314
|
-
}
|
|
315
|
-
ctx.platform.configManager.set('display.lineNumbers', next);
|
|
316
|
-
const label = next === 'all' ? 'ON (all lines)' : next === 'code' ? 'CODE BLOCKS ONLY' : 'OFF';
|
|
317
|
-
ctx.print(`Line numbers: ${label}`);
|
|
318
|
-
ctx.renderRequest();
|
|
319
|
-
},
|
|
320
|
-
});
|
|
321
289
|
}
|