@oh-my-pi/pi-coding-agent 16.1.7 → 16.1.9
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 +71 -0
- package/dist/cli.js +4266 -3446
- package/dist/types/cli/flag-tables.d.ts +17 -0
- package/dist/types/cli/session-picker.d.ts +0 -1
- package/dist/types/cli/tiny-models-cli.d.ts +2 -0
- package/dist/types/cli-commands.d.ts +4 -2
- package/dist/types/collab/protocol.d.ts +20 -1
- package/dist/types/config/settings-schema.d.ts +57 -6
- package/dist/types/extensibility/legacy-pi-ai-shim.d.ts +5 -1
- package/dist/types/internal-urls/filesystem-resource.d.ts +9 -0
- package/dist/types/mcp/loader.d.ts +3 -2
- package/dist/types/mcp/manager.d.ts +4 -3
- package/dist/types/mcp/startup-events.d.ts +21 -4
- package/dist/types/mnemopi/config.d.ts +1 -0
- package/dist/types/modes/components/agent-hub.d.ts +1 -0
- package/dist/types/modes/components/agent-transcript-viewer.d.ts +1 -0
- package/dist/types/modes/components/assistant-message.d.ts +5 -1
- package/dist/types/modes/components/btw-panel.d.ts +2 -0
- package/dist/types/modes/components/chat-transcript-builder.d.ts +1 -0
- package/dist/types/modes/components/session-selector.d.ts +0 -2
- package/dist/types/modes/controllers/btw-controller.d.ts +2 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -1
- package/dist/types/modes/controllers/selector-controller.d.ts +1 -0
- package/dist/types/modes/controllers/streaming-reveal.d.ts +3 -2
- package/dist/types/modes/interactive-mode.d.ts +4 -1
- package/dist/types/modes/types.d.ts +4 -1
- package/dist/types/session/agent-session.d.ts +18 -3
- package/dist/types/session/session-history-format.d.ts +25 -0
- package/dist/types/session/snapcompact-inline.d.ts +1 -1
- package/dist/types/slash-commands/builtin-registry.d.ts +7 -4
- package/dist/types/slash-commands/types.d.ts +2 -0
- package/dist/types/system-prompt.d.ts +1 -0
- package/dist/types/tiny/models.d.ts +11 -7
- package/dist/types/tools/browser/launch.d.ts +5 -0
- package/dist/types/tools/todo.d.ts +1 -0
- package/dist/types/utils/thinking-display.d.ts +2 -0
- package/package.json +12 -12
- package/src/advisor/__tests__/advisor.test.ts +104 -0
- package/src/advisor/runtime.ts +38 -2
- package/src/cli/args.ts +0 -1
- package/src/cli/flag-tables.ts +42 -0
- package/src/cli/profile-bootstrap.ts +1 -11
- package/src/cli/session-picker.ts +1 -2
- package/src/cli/tiny-models-cli.ts +7 -2
- package/src/cli-commands.ts +48 -3
- package/src/collab/guest.ts +172 -20
- package/src/collab/host.ts +47 -5
- package/src/collab/protocol.ts +16 -1
- package/src/config/model-registry.ts +6 -7
- package/src/config/settings-schema.ts +59 -5
- package/src/edit/renderer.ts +8 -12
- package/src/extensibility/legacy-pi-ai-shim.ts +16 -4
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/internal-urls/filesystem-resource.ts +34 -0
- package/src/internal-urls/local-protocol.ts +7 -1
- package/src/internal-urls/memory-protocol.ts +5 -1
- package/src/internal-urls/skill-protocol.ts +20 -4
- package/src/internal-urls/vault-protocol.ts +5 -2
- package/src/lsp/client.ts +39 -25
- package/src/main.ts +8 -8
- package/src/mcp/loader.ts +4 -3
- package/src/mcp/manager.ts +35 -15
- package/src/mcp/startup-events.ts +106 -11
- package/src/mnemopi/config.ts +2 -0
- package/src/mnemopi/state.ts +3 -1
- package/src/modes/components/agent-hub.ts +4 -0
- package/src/modes/components/agent-transcript-viewer.ts +2 -0
- package/src/modes/components/assistant-message.ts +217 -18
- package/src/modes/components/btw-panel.ts +15 -3
- package/src/modes/components/chat-transcript-builder.ts +8 -2
- package/src/modes/components/model-selector.ts +72 -9
- package/src/modes/components/omfg-panel.ts +1 -1
- package/src/modes/components/session-selector.ts +4 -9
- package/src/modes/components/snapcompact-shape-preview.ts +1 -1
- package/src/modes/components/tool-execution.ts +10 -2
- package/src/modes/controllers/btw-controller.ts +32 -7
- package/src/modes/controllers/command-controller.ts +18 -3
- package/src/modes/controllers/event-controller.ts +24 -4
- package/src/modes/controllers/input-controller.ts +43 -21
- package/src/modes/controllers/mcp-command-controller.ts +40 -12
- package/src/modes/controllers/selector-controller.ts +23 -13
- package/src/modes/controllers/streaming-reveal.ts +78 -20
- package/src/modes/controllers/todo-command-controller.ts +9 -10
- package/src/modes/interactive-mode.ts +84 -9
- package/src/modes/types.ts +4 -1
- package/src/modes/utils/ui-helpers.ts +1 -0
- package/src/prompts/advisor/system.md +1 -1
- package/src/prompts/system/side-channel-no-tools.md +3 -0
- package/src/sdk.ts +12 -8
- package/src/session/agent-session.ts +385 -85
- package/src/session/history-storage.ts +15 -16
- package/src/session/session-history-format.ts +41 -1
- package/src/session/snapcompact-inline.ts +9 -6
- package/src/slash-commands/builtin-registry.ts +171 -87
- package/src/slash-commands/helpers/todo.ts +12 -6
- package/src/slash-commands/types.ts +2 -0
- package/src/system-prompt.ts +6 -11
- package/src/tiny/models.ts +7 -3
- package/src/tiny/title-client.ts +8 -2
- package/src/tiny/worker.ts +1 -0
- package/src/tools/browser/launch.ts +107 -31
- package/src/tools/puppeteer/00_stealth_tampering.txt +16 -35
- package/src/tools/puppeteer/01_stealth_activity.txt +79 -19
- package/src/tools/puppeteer/02_stealth_hairline.txt +57 -11
- package/src/tools/puppeteer/03_stealth_botd.txt +10 -14
- package/src/tools/puppeteer/04_stealth_iframe.txt +156 -63
- package/src/tools/puppeteer/05_stealth_webgl.txt +222 -64
- package/src/tools/puppeteer/06_stealth_screen.txt +255 -67
- package/src/tools/puppeteer/07_stealth_fonts.txt +17 -15
- package/src/tools/puppeteer/08_stealth_audio.txt +32 -20
- package/src/tools/puppeteer/09_stealth_locale.txt +45 -40
- package/src/tools/puppeteer/10_stealth_plugins.txt +12 -8
- package/src/tools/puppeteer/11_stealth_hardware.txt +57 -6
- package/src/tools/puppeteer/12_stealth_codecs.txt +20 -18
- package/src/tools/puppeteer/13_stealth_worker.txt +206 -45
- package/src/tools/search.ts +10 -1
- package/src/tools/sqlite-reader.ts +59 -3
- package/src/tools/todo.ts +6 -0
- package/src/tools/write.ts +4 -6
- package/src/utils/thinking-display.ts +78 -0
|
@@ -38,24 +38,23 @@ class AsyncDrain<T> {
|
|
|
38
38
|
let queue = this.#queue;
|
|
39
39
|
if (!queue) {
|
|
40
40
|
this.#queue = queue = [];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
}
|
|
47
|
-
resolve(hnd(queue!));
|
|
48
|
-
} catch (error) {
|
|
49
|
-
reject(error);
|
|
41
|
+
const { promise, resolve, reject } = Promise.withResolvers<void>();
|
|
42
|
+
const exec = (): void => {
|
|
43
|
+
try {
|
|
44
|
+
if (this.#queue === queue) {
|
|
45
|
+
this.#queue = undefined;
|
|
50
46
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
setTimeout(exec, this.delayMs);
|
|
55
|
-
} else {
|
|
56
|
-
queueMicrotask(exec);
|
|
47
|
+
resolve(hnd(queue!));
|
|
48
|
+
} catch (error) {
|
|
49
|
+
reject(error);
|
|
57
50
|
}
|
|
58
|
-
}
|
|
51
|
+
};
|
|
52
|
+
if (this.delayMs > 0) {
|
|
53
|
+
setTimeout(exec, this.delayMs);
|
|
54
|
+
} else {
|
|
55
|
+
queueMicrotask(exec);
|
|
56
|
+
}
|
|
57
|
+
this.#promise = promise;
|
|
59
58
|
}
|
|
60
59
|
queue.push(value);
|
|
61
60
|
return this.#promise;
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
10
10
|
import type { AssistantMessage, ImageContent, TextContent, ToolResultMessage } from "@oh-my-pi/pi-ai";
|
|
11
|
+
import { escapeXmlText } from "@oh-my-pi/pi-utils";
|
|
11
12
|
import { INTENT_FIELD } from "@oh-my-pi/pi-wire";
|
|
12
13
|
import type {
|
|
13
14
|
BashExecutionMessage,
|
|
@@ -28,6 +29,17 @@ export interface HistoryFormatOptions {
|
|
|
28
29
|
includeToolIntent?: boolean;
|
|
29
30
|
/** Render watched-session roles as inline `**agent**:` / `**user**:` labels (collapsing consecutive same-role messages) instead of `## ` headings, so a primary transcript embedded inside an advisor turn stays visually distinct. */
|
|
30
31
|
watchedRoles?: boolean;
|
|
32
|
+
/**
|
|
33
|
+
* Expand the primary agent's injected constraint context — plan mode's rules
|
|
34
|
+
* (`plan-mode-context`) and the approved plan it implements
|
|
35
|
+
* (`plan-mode-reference`) — verbatim instead of as a truncated one-liner,
|
|
36
|
+
* wrapped in a `<primary-context>` tag so a reviewer reads it as the primary's
|
|
37
|
+
* instructions, not its own. The advisor sets this: a truncated rule (plan
|
|
38
|
+
* mode's "NEVER create files … except the plan file") makes it raise false
|
|
39
|
+
* blockers. See {@link PRIMARY_CONTEXT_CUSTOM_TYPES}. Other custom messages
|
|
40
|
+
* still collapse to a one-liner.
|
|
41
|
+
*/
|
|
42
|
+
expandPrimaryContext?: boolean;
|
|
31
43
|
}
|
|
32
44
|
|
|
33
45
|
/** Max length of the primary-arg summary inside `→ tool(...)` lines. */
|
|
@@ -156,6 +168,21 @@ function executionLine(
|
|
|
156
168
|
return `→ ${kind}! ${oneLine(source)} ⇒ ${status} · ${lines} ${lines === 1 ? "line" : "lines"}`;
|
|
157
169
|
}
|
|
158
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Hidden custom messages that inject the primary agent's operative *constraints*
|
|
173
|
+
* — plan mode's rules and the approved plan it implements. A reviewer (the
|
|
174
|
+
* advisor) must read these verbatim; truncating them hides load-bearing
|
|
175
|
+
* exceptions (e.g. plan mode permits exactly one plan file). Every other custom
|
|
176
|
+
* type stays a one-liner.
|
|
177
|
+
*
|
|
178
|
+
* Deliberately excludes `goal-mode-context`: its body carries live budget
|
|
179
|
+
* counters (tokens/seconds used) that change every turn, so it can neither be
|
|
180
|
+
* deduped against a prior copy nor expanded each turn without flooding the
|
|
181
|
+
* reviewer — and its constraints don't drive the file-write misreads this
|
|
182
|
+
* targets.
|
|
183
|
+
*/
|
|
184
|
+
export const PRIMARY_CONTEXT_CUSTOM_TYPES: ReadonlySet<string> = new Set(["plan-mode-context", "plan-mode-reference"]);
|
|
185
|
+
|
|
159
186
|
/** One-liner for custom/hook messages: `[irc] A → B: body…`. */
|
|
160
187
|
function customOneLiner(msg: CustomMessage | HookMessage): string {
|
|
161
188
|
const details = (msg.details ?? {}) as Record<string, unknown>;
|
|
@@ -279,7 +306,20 @@ export function formatSessionHistoryMarkdown(messages: unknown[], opts?: History
|
|
|
279
306
|
}
|
|
280
307
|
case "custom":
|
|
281
308
|
case "hookMessage": {
|
|
282
|
-
|
|
309
|
+
const custom = msg as CustomMessage | HookMessage;
|
|
310
|
+
if (opts?.expandPrimaryContext && PRIMARY_CONTEXT_CUSTOM_TYPES.has(custom.customType)) {
|
|
311
|
+
const text = contentToText(custom.content).trim();
|
|
312
|
+
if (text) {
|
|
313
|
+
lines.push(
|
|
314
|
+
`<primary-context kind="${custom.customType}">`,
|
|
315
|
+
escapeXmlText(text),
|
|
316
|
+
"</primary-context>",
|
|
317
|
+
"",
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
} else {
|
|
321
|
+
lines.push(customOneLiner(custom), "");
|
|
322
|
+
}
|
|
283
323
|
lastWatchedLabel = undefined;
|
|
284
324
|
break;
|
|
285
325
|
}
|
|
@@ -414,7 +414,7 @@ export class SnapcompactInlineTransformer {
|
|
|
414
414
|
private readonly onToolResultSavings?: SnapcompactSavingsSink,
|
|
415
415
|
) {}
|
|
416
416
|
|
|
417
|
-
transform(context: Context, model: Model): Context {
|
|
417
|
+
async transform(context: Context, model: Model): Promise<Context> {
|
|
418
418
|
// Vision gate: providers silently DROP images on text-only models —
|
|
419
419
|
// rendering would lose the content entirely.
|
|
420
420
|
if (!model.input.includes("image")) return context;
|
|
@@ -482,7 +482,7 @@ export class SnapcompactInlineTransformer {
|
|
|
482
482
|
for (const swap of plan.toolResults) {
|
|
483
483
|
const target = targets.get(swap.id);
|
|
484
484
|
if (!target) continue;
|
|
485
|
-
const frames = this.#framesFor(this.#toolCache, swap.id, target.text, shape);
|
|
485
|
+
const frames = await this.#framesFor(this.#toolCache, swap.id, target.text, shape);
|
|
486
486
|
messages[target.index] = { ...target.message, content: [{ type: "text", text: toolResultNote }, ...frames] };
|
|
487
487
|
changed = true;
|
|
488
488
|
savings.push({
|
|
@@ -506,7 +506,10 @@ export class SnapcompactInlineTransformer {
|
|
|
506
506
|
if (!cached || cached.hash !== hash) {
|
|
507
507
|
cached = {
|
|
508
508
|
hash,
|
|
509
|
-
frames: snapcompact.renderMany(systemPromptTarget.text, {
|
|
509
|
+
frames: await snapcompact.renderMany(systemPromptTarget.text, {
|
|
510
|
+
shape,
|
|
511
|
+
maxFrames: MAX_SYSTEM_PROMPT_FRAMES,
|
|
512
|
+
}),
|
|
510
513
|
};
|
|
511
514
|
this.#systemCache = cached;
|
|
512
515
|
}
|
|
@@ -526,16 +529,16 @@ export class SnapcompactInlineTransformer {
|
|
|
526
529
|
return { ...context, systemPrompt, messages };
|
|
527
530
|
}
|
|
528
531
|
|
|
529
|
-
#framesFor(
|
|
532
|
+
async #framesFor(
|
|
530
533
|
cache: Map<string, FrameCacheEntry>,
|
|
531
534
|
key: string,
|
|
532
535
|
text: string,
|
|
533
536
|
shape: snapcompact.Shape,
|
|
534
|
-
): ImageContent[] {
|
|
537
|
+
): Promise<ImageContent[]> {
|
|
535
538
|
const hash = Bun.hash(text);
|
|
536
539
|
const cached = cache.get(key);
|
|
537
540
|
if (cached && cached.hash === hash) return cached.frames;
|
|
538
|
-
const frames = snapcompact.renderMany(text, { shape });
|
|
541
|
+
const frames = await snapcompact.renderMany(text, { shape });
|
|
539
542
|
cache.set(key, { hash, frames });
|
|
540
543
|
return frames;
|
|
541
544
|
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import * as fs from "node:fs/promises";
|
|
2
|
-
import * as os from "node:os";
|
|
3
2
|
import * as path from "node:path";
|
|
4
3
|
import { getOAuthProviders } from "@oh-my-pi/pi-ai/oauth";
|
|
5
|
-
import { setNextRequestDebugPath } from "@oh-my-pi/pi-ai/utils/request-debug";
|
|
6
4
|
import { type AutocompleteItem, Spacer } from "@oh-my-pi/pi-tui";
|
|
7
5
|
import { APP_NAME, setProjectDir } from "@oh-my-pi/pi-utils";
|
|
8
6
|
import { COLLAB_GUEST_ALLOWED_COMMANDS, CollabGuestLink } from "../collab/guest";
|
|
@@ -24,6 +22,7 @@ import {
|
|
|
24
22
|
MarketplaceManager,
|
|
25
23
|
} from "../extensibility/plugins/marketplace";
|
|
26
24
|
import { resolveMemoryBackend } from "../memory-backend";
|
|
25
|
+
import { describeLoopLimitRuntime } from "../modes/loop-limit";
|
|
27
26
|
import { theme } from "../modes/theme/theme";
|
|
28
27
|
import type { InteractiveModeContext } from "../modes/types";
|
|
29
28
|
import type { AgentSession, FreshSessionResult } from "../session/agent-session";
|
|
@@ -58,6 +57,12 @@ export type { BuiltinSlashCommand, SubcommandDef } from "./types";
|
|
|
58
57
|
/** TUI-specific runtime accepted by `executeBuiltinSlashCommand`. */
|
|
59
58
|
export type BuiltinSlashCommandRuntime = TuiSlashCommandRuntime;
|
|
60
59
|
|
|
60
|
+
export interface TuiBuiltinSlashCommand extends BuiltinSlashCommand {
|
|
61
|
+
getArgumentCompletions?: (prefix: string) => AutocompleteItem[] | null;
|
|
62
|
+
getInlineHint?: (argumentText: string) => string | null;
|
|
63
|
+
getAutocompleteDescription?: () => string | undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
61
66
|
function refreshStatusLine(ctx: InteractiveModeContext): void {
|
|
62
67
|
ctx.statusLine.invalidate();
|
|
63
68
|
ctx.updateEditorTopBorder();
|
|
@@ -77,6 +82,17 @@ function formatFastModeStatus(session: AgentSession): string {
|
|
|
77
82
|
}
|
|
78
83
|
}
|
|
79
84
|
|
|
85
|
+
const AUTOCOMPLETE_DETAIL_LIMIT = 48;
|
|
86
|
+
|
|
87
|
+
function shortDetail(value: string, limit = AUTOCOMPLETE_DETAIL_LIMIT): string {
|
|
88
|
+
const singleLine = value.replace(/\s+/g, " ").trim();
|
|
89
|
+
return singleLine.length <= limit ? singleLine : `${singleLine.slice(0, limit - 1)}…`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatTokenCount(value: number): string {
|
|
93
|
+
return value.toLocaleString();
|
|
94
|
+
}
|
|
95
|
+
|
|
80
96
|
/** Scheme-less display form of a browser deep link: accent + underline, OSC-8 linked to the full URL. */
|
|
81
97
|
function collabWebLinkClickable(webLink: string): string {
|
|
82
98
|
const display = theme.fg("accent", `\x1b[4m${webLink.replace(/^https?:\/\//, "")}\x1b[24m`);
|
|
@@ -174,46 +190,6 @@ async function handleUsageResetCommand(
|
|
|
174
190
|
await output(describeRedeemOutcome(outcome, target.label));
|
|
175
191
|
}
|
|
176
192
|
|
|
177
|
-
const DEBUG_DUMP_NEXT_REQUEST_USAGE = "Usage: /debug dump-next-request <path>";
|
|
178
|
-
|
|
179
|
-
function resolveDebugRequestDumpPath(target: string, cwd: string): string {
|
|
180
|
-
const expanded =
|
|
181
|
-
target === "~"
|
|
182
|
-
? os.homedir()
|
|
183
|
-
: target.startsWith("~/") || target.startsWith("~\\")
|
|
184
|
-
? path.join(os.homedir(), target.slice(2))
|
|
185
|
-
: target;
|
|
186
|
-
return path.resolve(cwd, expanded);
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
async function handleDebugSubcommand(
|
|
190
|
-
args: string,
|
|
191
|
-
cwd: string,
|
|
192
|
-
output: (text: string) => Promise<void> | void,
|
|
193
|
-
): Promise<SlashCommandResult> {
|
|
194
|
-
const { verb, rest } = parseSubcommand(args);
|
|
195
|
-
switch (verb) {
|
|
196
|
-
case "":
|
|
197
|
-
await output(DEBUG_DUMP_NEXT_REQUEST_USAGE);
|
|
198
|
-
return commandConsumed();
|
|
199
|
-
case "dump-next-request":
|
|
200
|
-
case "dump-request":
|
|
201
|
-
case "next-request": {
|
|
202
|
-
if (!rest) {
|
|
203
|
-
await output(DEBUG_DUMP_NEXT_REQUEST_USAGE);
|
|
204
|
-
return commandConsumed();
|
|
205
|
-
}
|
|
206
|
-
const requestPath = resolveDebugRequestDumpPath(rest, cwd);
|
|
207
|
-
setNextRequestDebugPath(requestPath);
|
|
208
|
-
await output(`Next AI provider request will be dumped to ${requestPath}`);
|
|
209
|
-
return commandConsumed();
|
|
210
|
-
}
|
|
211
|
-
default:
|
|
212
|
-
await output(`Unknown /debug subcommand "${verb}". ${DEBUG_DUMP_NEXT_REQUEST_USAGE}`);
|
|
213
|
-
return commandConsumed();
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
|
|
217
193
|
/** Parse the `/shake` subcommand into a {@link ShakeMode}; empty defaults to elide. */
|
|
218
194
|
function parseShakeMode(args: string): ShakeMode | { error: string } {
|
|
219
195
|
const verb = args.trim().toLowerCase();
|
|
@@ -253,6 +229,15 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
253
229
|
description: "Toggle plan mode (agent plans before executing)",
|
|
254
230
|
inlineHint: "[prompt]",
|
|
255
231
|
allowArgs: true,
|
|
232
|
+
getTuiAutocompleteDescription: runtime => {
|
|
233
|
+
if (!runtime.ctx.settings.get("plan.enabled" as SettingPath)) return "Plan: disabled in settings";
|
|
234
|
+
if (runtime.ctx.planModeEnabled) {
|
|
235
|
+
const planFile = runtime.ctx.planModePlanFilePath;
|
|
236
|
+
return `Plan: on${planFile ? ` (${path.basename(planFile)})` : ""}`;
|
|
237
|
+
}
|
|
238
|
+
if (runtime.ctx.goalModeEnabled) return "Plan: blocked by goal mode";
|
|
239
|
+
return "Plan: off";
|
|
240
|
+
},
|
|
256
241
|
handleTui: async (command, runtime) => {
|
|
257
242
|
const hadArgs = !!command.args;
|
|
258
243
|
await runtime.ctx.handlePlanModeCommand(command.args || undefined);
|
|
@@ -265,6 +250,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
265
250
|
{
|
|
266
251
|
name: "plan-review",
|
|
267
252
|
description: "Re-open the plan review for the latest plan (plan mode only)",
|
|
253
|
+
getTuiAutocompleteDescription: runtime =>
|
|
254
|
+
runtime.ctx.planModeEnabled ? "Plan review: available" : "Plan review: plan mode inactive",
|
|
268
255
|
handleTui: async (_command, runtime) => {
|
|
269
256
|
await runtime.ctx.openPlanReview();
|
|
270
257
|
runtime.ctx.editor.setText("");
|
|
@@ -283,6 +270,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
283
270
|
],
|
|
284
271
|
inlineHint: "[objective]",
|
|
285
272
|
allowArgs: true,
|
|
273
|
+
getTuiAutocompleteDescription: runtime => {
|
|
274
|
+
if (!runtime.ctx.settings.get("goal.enabled" as SettingPath)) return "Goal: disabled in settings";
|
|
275
|
+
if (runtime.ctx.planModeEnabled) return "Goal: blocked by plan mode";
|
|
276
|
+
const state = runtime.ctx.session.getGoalModeState();
|
|
277
|
+
return state ? `Goal: ${state.goal.status} (${shortDetail(state.goal.objective)})` : "Goal: off";
|
|
278
|
+
},
|
|
286
279
|
handleTui: async (command, runtime) => {
|
|
287
280
|
const hadArgs = !!command.args;
|
|
288
281
|
await runtime.ctx.handleGoalModeCommand(command.args || undefined);
|
|
@@ -308,6 +301,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
308
301
|
"Toggle loop mode. While enabled, the next prompt you send re-submits after every yield. Esc cancels the current iteration; /loop again to disable.",
|
|
309
302
|
inlineHint: "[count|duration] [prompt]",
|
|
310
303
|
allowArgs: true,
|
|
304
|
+
getTuiAutocompleteDescription: runtime => {
|
|
305
|
+
if (!runtime.ctx.loopModeEnabled) return "Loop: off";
|
|
306
|
+
if (runtime.ctx.loopLimit) return `Loop: on (${describeLoopLimitRuntime(runtime.ctx.loopLimit)})`;
|
|
307
|
+
if (runtime.ctx.loopPrompt) return "Loop: on (repeating prompt)";
|
|
308
|
+
return "Loop: on (waiting for next prompt)";
|
|
309
|
+
},
|
|
311
310
|
handleTui: async (command, runtime) => {
|
|
312
311
|
const prompt = await runtime.ctx.handleLoopCommand(command.args);
|
|
313
312
|
runtime.ctx.editor.setText("");
|
|
@@ -321,6 +320,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
321
320
|
aliases: ["models"],
|
|
322
321
|
description: "Switch model for this session",
|
|
323
322
|
acpDescription: "Show current model selection",
|
|
323
|
+
getTuiAutocompleteDescription: runtime => {
|
|
324
|
+
const model = runtime.ctx.session.model;
|
|
325
|
+
return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
|
|
326
|
+
},
|
|
324
327
|
handle: async (command, runtime) => {
|
|
325
328
|
if (command.args) {
|
|
326
329
|
const modelId = command.args.trim();
|
|
@@ -359,6 +362,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
359
362
|
{
|
|
360
363
|
name: "switch",
|
|
361
364
|
description: "Switch model for this session (same as alt+p)",
|
|
365
|
+
getTuiAutocompleteDescription: runtime => {
|
|
366
|
+
const model = runtime.ctx.session.model;
|
|
367
|
+
return model ? `Model: ${model.provider}/${model.id}` : "Model: none selected";
|
|
368
|
+
},
|
|
362
369
|
handleTui: (_command, runtime) => {
|
|
363
370
|
runtime.ctx.showModelSelector({ temporaryOnly: true });
|
|
364
371
|
runtime.ctx.editor.setText("");
|
|
@@ -375,6 +382,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
375
382
|
{ name: "status", description: "Show fast mode status" },
|
|
376
383
|
],
|
|
377
384
|
allowArgs: true,
|
|
385
|
+
getTuiAutocompleteDescription: runtime => `Fast: ${formatFastModeStatus(runtime.ctx.session)}`,
|
|
378
386
|
handle: async (command, runtime) => {
|
|
379
387
|
const arg = command.args.toLowerCase();
|
|
380
388
|
if (!arg || arg === "toggle") {
|
|
@@ -442,6 +450,12 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
442
450
|
{ name: "dump", description: "Copy the advisor's transcript to clipboard", usage: "[raw]" },
|
|
443
451
|
],
|
|
444
452
|
allowArgs: true,
|
|
453
|
+
getTuiAutocompleteDescription: runtime => {
|
|
454
|
+
const stats = runtime.ctx.session.getAdvisorStats();
|
|
455
|
+
if (stats.active && stats.model) return `Advisor: on (${stats.model.provider}/${stats.model.id})`;
|
|
456
|
+
if (stats.configured) return "Advisor: configured, no model";
|
|
457
|
+
return "Advisor: off";
|
|
458
|
+
},
|
|
445
459
|
handle: async (command, runtime) => {
|
|
446
460
|
const { verb, rest } = parseSubcommand(command.args);
|
|
447
461
|
if (!verb || verb === "toggle") {
|
|
@@ -556,16 +570,33 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
556
570
|
},
|
|
557
571
|
{
|
|
558
572
|
name: "dump",
|
|
559
|
-
description: "Copy session transcript to clipboard",
|
|
560
|
-
acpDescription: "Return full transcript as plain text",
|
|
573
|
+
description: "Copy session transcript to clipboard (and write LLM request JSON to tmp)",
|
|
574
|
+
acpDescription: "Return full transcript as plain text, with LLM request JSON path",
|
|
561
575
|
allowArgs: true,
|
|
562
576
|
handle: async (_command, runtime) => {
|
|
563
577
|
const text = runtime.session.formatSessionAsText();
|
|
564
|
-
|
|
578
|
+
if (!text) {
|
|
579
|
+
await runtime.output("No messages to dump yet.");
|
|
580
|
+
return commandConsumed();
|
|
581
|
+
}
|
|
582
|
+
let sidecarPath: string | undefined;
|
|
583
|
+
try {
|
|
584
|
+
sidecarPath = await runtime.session.dumpLlmRequestToTmpDir();
|
|
585
|
+
} catch {
|
|
586
|
+
// Sidecar is best-effort; the transcript is still output below.
|
|
587
|
+
}
|
|
588
|
+
const lines = [text];
|
|
589
|
+
if (sidecarPath)
|
|
590
|
+
lines.push(
|
|
591
|
+
"",
|
|
592
|
+
`LLM request JSON: ${sidecarPath}`,
|
|
593
|
+
"This file persists on disk and may contain raw context/secrets — treat accordingly.",
|
|
594
|
+
);
|
|
595
|
+
await runtime.output(lines.join("\n"));
|
|
565
596
|
return commandConsumed();
|
|
566
597
|
},
|
|
567
|
-
handleTui: (_command, runtime) => {
|
|
568
|
-
runtime.ctx.handleDumpCommand();
|
|
598
|
+
handleTui: async (_command, runtime) => {
|
|
599
|
+
await runtime.ctx.handleDumpCommand();
|
|
569
600
|
runtime.ctx.editor.setText("");
|
|
570
601
|
},
|
|
571
602
|
},
|
|
@@ -603,6 +634,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
603
634
|
{ name: "stop", description: "Stop sharing" },
|
|
604
635
|
],
|
|
605
636
|
allowArgs: true,
|
|
637
|
+
getTuiAutocompleteDescription: runtime => {
|
|
638
|
+
if (runtime.ctx.collabHost) {
|
|
639
|
+
return `Collab: hosting (${Math.max(0, runtime.ctx.collabHost.participants.length - 1)} guests)`;
|
|
640
|
+
}
|
|
641
|
+
if (runtime.ctx.collabGuest?.readOnly) return "Collab: read-only guest";
|
|
642
|
+
if (runtime.ctx.collabGuest) return "Collab: guest";
|
|
643
|
+
return "Collab: off";
|
|
644
|
+
},
|
|
606
645
|
handleTui: async (command, runtime) => {
|
|
607
646
|
const ctx = runtime.ctx;
|
|
608
647
|
ctx.editor.setText("");
|
|
@@ -702,6 +741,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
702
741
|
{
|
|
703
742
|
name: "leave",
|
|
704
743
|
description: "Leave the collab session",
|
|
744
|
+
getTuiAutocompleteDescription: runtime => {
|
|
745
|
+
if (runtime.ctx.collabHost) return "Leave collab: hosting";
|
|
746
|
+
if (runtime.ctx.collabGuest) return "Leave collab: guest";
|
|
747
|
+
return "Leave collab: not in collab";
|
|
748
|
+
},
|
|
705
749
|
handleTui: async (_command, runtime) => {
|
|
706
750
|
const ctx = runtime.ctx;
|
|
707
751
|
ctx.editor.setText("");
|
|
@@ -726,6 +770,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
726
770
|
{ name: "visible", description: "Switch to visible mode" },
|
|
727
771
|
],
|
|
728
772
|
allowArgs: true,
|
|
773
|
+
getTuiAutocompleteDescription: runtime => {
|
|
774
|
+
if (!runtime.ctx.settings.get("browser.enabled" as SettingPath)) return "Browser: disabled";
|
|
775
|
+
return runtime.ctx.settings.get("browser.headless" as SettingPath) ? "Browser: headless" : "Browser: visible";
|
|
776
|
+
},
|
|
729
777
|
handle: async (command, runtime) => {
|
|
730
778
|
const arg = command.args.toLowerCase();
|
|
731
779
|
const enabled = runtime.settings.get("browser.enabled" as SettingPath) as boolean;
|
|
@@ -817,6 +865,14 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
817
865
|
{ name: "rm", description: "Remove task/phase/all (fuzzy-matched)", usage: "[<task|phase>]" },
|
|
818
866
|
],
|
|
819
867
|
allowArgs: true,
|
|
868
|
+
getTuiAutocompleteDescription: runtime => {
|
|
869
|
+
const tasks = runtime.ctx.todoPhases.flatMap(phase => phase.tasks);
|
|
870
|
+
if (tasks.length === 0) return "Todos: none";
|
|
871
|
+
const pending = tasks.filter(task => task.status === "pending").length;
|
|
872
|
+
const inProgress = tasks.filter(task => task.status === "in_progress").length;
|
|
873
|
+
const completed = tasks.filter(task => task.status === "completed").length;
|
|
874
|
+
return `Todos: ${pending + inProgress} open (${inProgress} in progress, ${completed} done)`;
|
|
875
|
+
},
|
|
820
876
|
handle: handleTodoAcp,
|
|
821
877
|
handleTui: async (command, runtime) => {
|
|
822
878
|
await runtime.ctx.handleTodoCommand(command.args);
|
|
@@ -881,6 +937,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
881
937
|
name: "jobs",
|
|
882
938
|
description: "Show async background jobs status",
|
|
883
939
|
acpDescription: "Show background jobs",
|
|
940
|
+
getTuiAutocompleteDescription: runtime => {
|
|
941
|
+
const snapshot = runtime.ctx.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
942
|
+
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) return "Jobs: none";
|
|
943
|
+
return `Jobs: ${snapshot.running.length} running, ${snapshot.recent.length} recent`;
|
|
944
|
+
},
|
|
884
945
|
handle: async (_command, runtime) => {
|
|
885
946
|
const snapshot = runtime.session.getAsyncJobSnapshot({ recentLimit: 5 });
|
|
886
947
|
if (!snapshot || (snapshot.running.length === 0 && snapshot.recent.length === 0)) {
|
|
@@ -1016,6 +1077,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1016
1077
|
name: "tools",
|
|
1017
1078
|
description: "Show tools currently visible to the agent",
|
|
1018
1079
|
acpDescription: "Show available tools",
|
|
1080
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1081
|
+
const active = runtime.ctx.session.getActiveToolNames().length;
|
|
1082
|
+
const all = runtime.ctx.session.getAllToolNames().length;
|
|
1083
|
+
return all === 0 ? "Tools: none available" : `Tools: ${active} active / ${all} available`;
|
|
1084
|
+
},
|
|
1019
1085
|
handle: async (_command, runtime) => {
|
|
1020
1086
|
const active = runtime.session.getActiveToolNames();
|
|
1021
1087
|
const all = runtime.session.getAllToolNames();
|
|
@@ -1035,6 +1101,11 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1035
1101
|
name: "context",
|
|
1036
1102
|
description: "Show estimated context usage breakdown",
|
|
1037
1103
|
acpDescription: "Show context usage",
|
|
1104
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1105
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
1106
|
+
if (!usage) return "Context: unavailable";
|
|
1107
|
+
return `Context: ${Math.round(usage.percent)}% (${formatTokenCount(usage.tokens)}/${formatTokenCount(usage.contextWindow)})`;
|
|
1108
|
+
},
|
|
1038
1109
|
handle: async (_command, runtime) => {
|
|
1039
1110
|
await runtime.output(buildContextReportText(runtime));
|
|
1040
1111
|
return commandConsumed();
|
|
@@ -1094,6 +1165,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1094
1165
|
description: "Login with OAuth provider",
|
|
1095
1166
|
inlineHint: "[provider|redirect URL]",
|
|
1096
1167
|
allowArgs: true,
|
|
1168
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1169
|
+
runtime.ctx.oauthManualInput.hasPending()
|
|
1170
|
+
? `Login: waiting for ${runtime.ctx.oauthManualInput.pendingProviderId ?? "OAuth"} callback`
|
|
1171
|
+
: "Login: choose provider",
|
|
1097
1172
|
handleTui: (command, runtime) => {
|
|
1098
1173
|
const manualInput = runtime.ctx.oauthManualInput;
|
|
1099
1174
|
const args = command.args.trim();
|
|
@@ -1233,6 +1308,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1233
1308
|
{
|
|
1234
1309
|
name: "fresh",
|
|
1235
1310
|
description: "Reset provider stream state without changing the local transcript",
|
|
1311
|
+
getTuiAutocompleteDescription: runtime =>
|
|
1312
|
+
runtime.ctx.session.isStreaming ? "Fresh: unavailable while streaming" : "Fresh: ready",
|
|
1236
1313
|
handle: async (_command, runtime) => {
|
|
1237
1314
|
const result = runtime.session.freshSession();
|
|
1238
1315
|
if (!result) {
|
|
@@ -1268,6 +1345,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1268
1345
|
})),
|
|
1269
1346
|
acpInputHint: `[${COMPACT_MODES.map(mode => mode.name).join("|")}] [focus]`,
|
|
1270
1347
|
allowArgs: true,
|
|
1348
|
+
getTuiAutocompleteDescription: runtime => {
|
|
1349
|
+
const usage = runtime.ctx.session.getContextUsage();
|
|
1350
|
+
return usage ? `Compact: context ${Math.round(usage.percent)}% used` : "Compact: context unavailable";
|
|
1351
|
+
},
|
|
1271
1352
|
handle: async (command, runtime) => {
|
|
1272
1353
|
const parsed = parseCompactArgs(command.args);
|
|
1273
1354
|
if ("error" in parsed) return usage(parsed.error, runtime);
|
|
@@ -1354,6 +1435,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1354
1435
|
allowArgs: true,
|
|
1355
1436
|
handleTui: async (command, runtime) => {
|
|
1356
1437
|
const question = command.text.slice(`/${command.name}`.length).trim();
|
|
1438
|
+
if (question) {
|
|
1439
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1440
|
+
}
|
|
1357
1441
|
runtime.ctx.editor.setText("");
|
|
1358
1442
|
await runtime.ctx.handleBtwCommand(question);
|
|
1359
1443
|
},
|
|
@@ -1365,6 +1449,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1365
1449
|
allowArgs: true,
|
|
1366
1450
|
handleTui: async (command, runtime) => {
|
|
1367
1451
|
const work = command.text.slice(`/${command.name}`.length).trim();
|
|
1452
|
+
if (work) {
|
|
1453
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1454
|
+
}
|
|
1368
1455
|
runtime.ctx.editor.setText("");
|
|
1369
1456
|
await runtime.ctx.handleTanCommand(work);
|
|
1370
1457
|
},
|
|
@@ -1376,6 +1463,9 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1376
1463
|
allowArgs: true,
|
|
1377
1464
|
handleTui: async (command, runtime) => {
|
|
1378
1465
|
const complaint = command.text.slice(`/${command.name}`.length).trim();
|
|
1466
|
+
if (complaint) {
|
|
1467
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1468
|
+
}
|
|
1379
1469
|
runtime.ctx.editor.setText("");
|
|
1380
1470
|
await runtime.ctx.handleOmfgCommand(complaint);
|
|
1381
1471
|
},
|
|
@@ -1394,25 +1484,8 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1394
1484
|
{
|
|
1395
1485
|
name: "debug",
|
|
1396
1486
|
description: "Open debug tools selector",
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
{
|
|
1400
|
-
name: "dump-next-request",
|
|
1401
|
-
description: "Dump the next AI provider HTTP request as JSON",
|
|
1402
|
-
usage: "<path>",
|
|
1403
|
-
},
|
|
1404
|
-
],
|
|
1405
|
-
handle: async (command, runtime) =>
|
|
1406
|
-
handleDebugSubcommand(command.args, runtime.cwd, text => runtime.output(text)),
|
|
1407
|
-
handleTui: async (command, runtime) => {
|
|
1408
|
-
const args = command.args.trim();
|
|
1409
|
-
if (args.length === 0) {
|
|
1410
|
-
runtime.ctx.showDebugSelector();
|
|
1411
|
-
} else {
|
|
1412
|
-
await handleDebugSubcommand(args, runtime.ctx.sessionManager.getCwd(), text =>
|
|
1413
|
-
runtime.ctx.showStatus(text),
|
|
1414
|
-
);
|
|
1415
|
-
}
|
|
1487
|
+
handleTui: async (_command, runtime) => {
|
|
1488
|
+
await runtime.ctx.showDebugSelector();
|
|
1416
1489
|
runtime.ctx.editor.setText("");
|
|
1417
1490
|
},
|
|
1418
1491
|
},
|
|
@@ -1484,6 +1557,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1484
1557
|
}
|
|
1485
1558
|
},
|
|
1486
1559
|
handleTui: async (command, runtime) => {
|
|
1560
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1487
1561
|
runtime.ctx.editor.setText("");
|
|
1488
1562
|
await runtime.ctx.handleMemoryCommand(command.text);
|
|
1489
1563
|
},
|
|
@@ -1511,6 +1585,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1511
1585
|
runtime.ctx.editor.setText("");
|
|
1512
1586
|
return;
|
|
1513
1587
|
}
|
|
1588
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1514
1589
|
runtime.ctx.editor.setText("");
|
|
1515
1590
|
await runtime.ctx.handleRenameCommand(title);
|
|
1516
1591
|
},
|
|
@@ -1553,6 +1628,7 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
1553
1628
|
runtime.ctx.editor.setText("");
|
|
1554
1629
|
return;
|
|
1555
1630
|
}
|
|
1631
|
+
runtime.ctx.editor.addToHistory(command.text);
|
|
1556
1632
|
runtime.ctx.editor.setText("");
|
|
1557
1633
|
await runtime.ctx.handleMoveCommand(targetPath);
|
|
1558
1634
|
},
|
|
@@ -2097,6 +2173,10 @@ const BUILTIN_SLASH_COMMAND_REGISTRY: ReadonlyArray<SlashCommandSpec> = [
|
|
|
2097
2173
|
aliases: ["force:"],
|
|
2098
2174
|
inlineHint: "<tool-name> [prompt]",
|
|
2099
2175
|
allowArgs: true,
|
|
2176
|
+
getTuiAutocompleteDescription: runtime => {
|
|
2177
|
+
const count = runtime.ctx.session.getActiveToolNames().length;
|
|
2178
|
+
return count === 0 ? "Force: no active tools" : `Force: ${count} active tools`;
|
|
2179
|
+
},
|
|
2100
2180
|
handle: async (command, runtime) => {
|
|
2101
2181
|
const spaceIdx = command.args.indexOf(" ");
|
|
2102
2182
|
const toolName = spaceIdx === -1 ? command.args : command.args.slice(0, spaceIdx);
|
|
@@ -2225,34 +2305,38 @@ export const BUILTIN_SLASH_COMMAND_DEFS: ReadonlyArray<BuiltinSlashCommand> = BU
|
|
|
2225
2305
|
description: command.description,
|
|
2226
2306
|
subcommands: command.subcommands,
|
|
2227
2307
|
inlineHint: command.inlineHint,
|
|
2308
|
+
getTuiAutocompleteDescription: command.getTuiAutocompleteDescription,
|
|
2228
2309
|
}),
|
|
2229
2310
|
);
|
|
2230
2311
|
|
|
2312
|
+
function materializeTuiBuiltinSlashCommand(
|
|
2313
|
+
cmd: BuiltinSlashCommand,
|
|
2314
|
+
runtime?: TuiSlashCommandRuntime,
|
|
2315
|
+
): TuiBuiltinSlashCommand {
|
|
2316
|
+
const materialized: TuiBuiltinSlashCommand = { ...cmd };
|
|
2317
|
+
if (cmd.subcommands) {
|
|
2318
|
+
materialized.getArgumentCompletions = buildArgumentCompletions(cmd.subcommands);
|
|
2319
|
+
materialized.getInlineHint = buildSubcommandInlineHint(cmd.subcommands);
|
|
2320
|
+
} else if (cmd.inlineHint) {
|
|
2321
|
+
materialized.getInlineHint = buildStaticInlineHint(cmd.inlineHint);
|
|
2322
|
+
}
|
|
2323
|
+
if (runtime && cmd.getTuiAutocompleteDescription) {
|
|
2324
|
+
materialized.getAutocompleteDescription = () => cmd.getTuiAutocompleteDescription?.(runtime);
|
|
2325
|
+
}
|
|
2326
|
+
return materialized;
|
|
2327
|
+
}
|
|
2328
|
+
|
|
2231
2329
|
/**
|
|
2232
2330
|
* Materialized builtin slash commands with completion functions derived from
|
|
2233
2331
|
* declarative subcommand/hint definitions.
|
|
2234
2332
|
*/
|
|
2235
|
-
export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
return {
|
|
2243
|
-
...cmd,
|
|
2244
|
-
getArgumentCompletions: buildArgumentCompletions(cmd.subcommands),
|
|
2245
|
-
getInlineHint: buildSubcommandInlineHint(cmd.subcommands),
|
|
2246
|
-
};
|
|
2247
|
-
}
|
|
2248
|
-
if (cmd.inlineHint) {
|
|
2249
|
-
return {
|
|
2250
|
-
...cmd,
|
|
2251
|
-
getInlineHint: buildStaticInlineHint(cmd.inlineHint),
|
|
2252
|
-
};
|
|
2253
|
-
}
|
|
2254
|
-
return cmd;
|
|
2255
|
-
});
|
|
2333
|
+
export const BUILTIN_SLASH_COMMANDS: ReadonlyArray<TuiBuiltinSlashCommand> = BUILTIN_SLASH_COMMAND_DEFS.map(cmd =>
|
|
2334
|
+
materializeTuiBuiltinSlashCommand(cmd),
|
|
2335
|
+
);
|
|
2336
|
+
|
|
2337
|
+
export function buildTuiBuiltinSlashCommands(runtime: TuiSlashCommandRuntime): ReadonlyArray<TuiBuiltinSlashCommand> {
|
|
2338
|
+
return BUILTIN_SLASH_COMMAND_DEFS.map(cmd => materializeTuiBuiltinSlashCommand(cmd, runtime));
|
|
2339
|
+
}
|
|
2256
2340
|
|
|
2257
2341
|
/**
|
|
2258
2342
|
* Unified registry exposed for cross-mode tooling. Each spec carries at least
|