@oh-my-pi/pi-coding-agent 3.15.0 → 3.20.0
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 +61 -1
- package/docs/extensions.md +1055 -0
- package/docs/rpc.md +69 -13
- package/docs/session-tree-plan.md +1 -1
- package/examples/extensions/README.md +141 -0
- package/examples/extensions/api-demo.ts +87 -0
- package/examples/extensions/chalk-logger.ts +26 -0
- package/examples/extensions/hello.ts +33 -0
- package/examples/extensions/pirate.ts +44 -0
- package/examples/extensions/plan-mode.ts +551 -0
- package/examples/extensions/subagent/agents/reviewer.md +35 -0
- package/examples/extensions/todo.ts +299 -0
- package/examples/extensions/tools.ts +145 -0
- package/examples/extensions/with-deps/index.ts +36 -0
- package/examples/extensions/with-deps/package-lock.json +31 -0
- package/examples/extensions/with-deps/package.json +16 -0
- package/examples/sdk/02-custom-model.ts +3 -3
- package/examples/sdk/05-tools.ts +7 -3
- package/examples/sdk/06-extensions.ts +81 -0
- package/examples/sdk/06-hooks.ts +14 -13
- package/examples/sdk/08-prompt-templates.ts +42 -0
- package/examples/sdk/08-slash-commands.ts +17 -12
- package/examples/sdk/09-api-keys-and-oauth.ts +2 -2
- package/examples/sdk/12-full-control.ts +6 -6
- package/package.json +11 -7
- package/src/capability/extension-module.ts +34 -0
- package/src/cli/args.ts +22 -7
- package/src/cli/file-processor.ts +38 -67
- package/src/cli/list-models.ts +1 -1
- package/src/config.ts +25 -14
- package/src/core/agent-session.ts +505 -242
- package/src/core/auth-storage.ts +33 -21
- package/src/core/compaction/branch-summarization.ts +4 -4
- package/src/core/compaction/compaction.ts +3 -3
- package/src/core/custom-commands/bundled/wt/index.ts +430 -0
- package/src/core/custom-commands/loader.ts +9 -0
- package/src/core/custom-tools/wrapper.ts +5 -0
- package/src/core/event-bus.ts +59 -0
- package/src/core/export-html/vendor/highlight.min.js +1213 -0
- package/src/core/export-html/vendor/marked.min.js +6 -0
- package/src/core/extensions/index.ts +100 -0
- package/src/core/extensions/loader.ts +501 -0
- package/src/core/extensions/runner.ts +477 -0
- package/src/core/extensions/types.ts +712 -0
- package/src/core/extensions/wrapper.ts +147 -0
- package/src/core/hooks/types.ts +2 -2
- package/src/core/index.ts +10 -21
- package/src/core/keybindings.ts +199 -0
- package/src/core/messages.ts +26 -7
- package/src/core/model-registry.ts +123 -46
- package/src/core/model-resolver.ts +7 -5
- package/src/core/prompt-templates.ts +242 -0
- package/src/core/sdk.ts +378 -295
- package/src/core/session-manager.ts +72 -58
- package/src/core/settings-manager.ts +118 -22
- package/src/core/system-prompt.ts +24 -1
- package/src/core/terminal-notify.ts +37 -0
- package/src/core/tools/context.ts +4 -4
- package/src/core/tools/exa/mcp-client.ts +5 -4
- package/src/core/tools/exa/render.ts +176 -131
- package/src/core/tools/gemini-image.ts +361 -0
- package/src/core/tools/git.ts +216 -0
- package/src/core/tools/index.ts +28 -15
- package/src/core/tools/lsp/config.ts +5 -4
- package/src/core/tools/lsp/index.ts +17 -12
- package/src/core/tools/lsp/render.ts +39 -47
- package/src/core/tools/read.ts +66 -29
- package/src/core/tools/render-utils.ts +268 -0
- package/src/core/tools/renderers.ts +243 -225
- package/src/core/tools/task/discovery.ts +2 -2
- package/src/core/tools/task/executor.ts +66 -58
- package/src/core/tools/task/index.ts +29 -10
- package/src/core/tools/task/model-resolver.ts +8 -13
- package/src/core/tools/task/omp-command.ts +24 -0
- package/src/core/tools/task/render.ts +35 -60
- package/src/core/tools/task/types.ts +3 -0
- package/src/core/tools/web-fetch.ts +29 -28
- package/src/core/tools/web-search/index.ts +6 -5
- package/src/core/tools/web-search/providers/exa.ts +6 -5
- package/src/core/tools/web-search/render.ts +66 -111
- package/src/core/voice-controller.ts +135 -0
- package/src/core/voice-supervisor.ts +1003 -0
- package/src/core/voice.ts +308 -0
- package/src/discovery/builtin.ts +75 -1
- package/src/discovery/claude.ts +47 -1
- package/src/discovery/codex.ts +54 -2
- package/src/discovery/gemini.ts +55 -2
- package/src/discovery/helpers.ts +100 -1
- package/src/discovery/index.ts +2 -0
- package/src/index.ts +14 -9
- package/src/lib/worktree/collapse.ts +179 -0
- package/src/lib/worktree/constants.ts +14 -0
- package/src/lib/worktree/errors.ts +23 -0
- package/src/lib/worktree/git.ts +110 -0
- package/src/lib/worktree/index.ts +23 -0
- package/src/lib/worktree/operations.ts +216 -0
- package/src/lib/worktree/session.ts +114 -0
- package/src/lib/worktree/stats.ts +67 -0
- package/src/main.ts +61 -37
- package/src/migrations.ts +37 -7
- package/src/modes/interactive/components/bash-execution.ts +6 -4
- package/src/modes/interactive/components/custom-editor.ts +55 -0
- package/src/modes/interactive/components/custom-message.ts +95 -0
- package/src/modes/interactive/components/extensions/extension-list.ts +5 -0
- package/src/modes/interactive/components/extensions/inspector-panel.ts +18 -12
- package/src/modes/interactive/components/extensions/state-manager.ts +12 -0
- package/src/modes/interactive/components/extensions/types.ts +1 -0
- package/src/modes/interactive/components/footer.ts +324 -0
- package/src/modes/interactive/components/hook-editor.ts +1 -0
- package/src/modes/interactive/components/hook-selector.ts +3 -3
- package/src/modes/interactive/components/model-selector.ts +7 -6
- package/src/modes/interactive/components/oauth-selector.ts +3 -3
- package/src/modes/interactive/components/settings-defs.ts +55 -6
- package/src/modes/interactive/components/status-line/separators.ts +4 -4
- package/src/modes/interactive/components/status-line.ts +45 -35
- package/src/modes/interactive/components/tool-execution.ts +95 -23
- package/src/modes/interactive/interactive-mode.ts +644 -113
- package/src/modes/interactive/theme/defaults/alabaster.json +99 -0
- package/src/modes/interactive/theme/defaults/amethyst.json +103 -0
- package/src/modes/interactive/theme/defaults/anthracite.json +100 -0
- package/src/modes/interactive/theme/defaults/basalt.json +90 -0
- package/src/modes/interactive/theme/defaults/birch.json +101 -0
- package/src/modes/interactive/theme/defaults/dark-abyss.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-aurora.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-cavern.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-copper.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-cosmos.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-eclipse.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-ember.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-equinox.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-lavender.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-lunar.json +95 -0
- package/src/modes/interactive/theme/defaults/dark-midnight.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-nebula.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-rainforest.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-reef.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-sakura.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-slate.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-solstice.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-starfall.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-swamp.json +96 -0
- package/src/modes/interactive/theme/defaults/dark-taiga.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-terminal.json +94 -0
- package/src/modes/interactive/theme/defaults/dark-tundra.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-twilight.json +97 -0
- package/src/modes/interactive/theme/defaults/dark-volcanic.json +97 -0
- package/src/modes/interactive/theme/defaults/graphite.json +99 -0
- package/src/modes/interactive/theme/defaults/index.ts +128 -0
- package/src/modes/interactive/theme/defaults/light-aurora-day.json +97 -0
- package/src/modes/interactive/theme/defaults/light-canyon.json +97 -0
- package/src/modes/interactive/theme/defaults/light-cirrus.json +96 -0
- package/src/modes/interactive/theme/defaults/light-coral.json +94 -0
- package/src/modes/interactive/theme/defaults/light-dawn.json +96 -0
- package/src/modes/interactive/theme/defaults/light-dunes.json +97 -0
- package/src/modes/interactive/theme/defaults/light-eucalyptus.json +94 -0
- package/src/modes/interactive/theme/defaults/light-frost.json +94 -0
- package/src/modes/interactive/theme/defaults/light-glacier.json +97 -0
- package/src/modes/interactive/theme/defaults/light-haze.json +96 -0
- package/src/modes/interactive/theme/defaults/light-honeycomb.json +94 -0
- package/src/modes/interactive/theme/defaults/light-lagoon.json +97 -0
- package/src/modes/interactive/theme/defaults/light-lavender.json +94 -0
- package/src/modes/interactive/theme/defaults/light-meadow.json +97 -0
- package/src/modes/interactive/theme/defaults/light-mint.json +94 -0
- package/src/modes/interactive/theme/defaults/light-opal.json +97 -0
- package/src/modes/interactive/theme/defaults/light-orchard.json +97 -0
- package/src/modes/interactive/theme/defaults/light-paper.json +94 -0
- package/src/modes/interactive/theme/defaults/light-prism.json +96 -0
- package/src/modes/interactive/theme/defaults/light-sand.json +94 -0
- package/src/modes/interactive/theme/defaults/light-savanna.json +97 -0
- package/src/modes/interactive/theme/defaults/light-soleil.json +96 -0
- package/src/modes/interactive/theme/defaults/light-wetland.json +97 -0
- package/src/modes/interactive/theme/defaults/light-zenith.json +95 -0
- package/src/modes/interactive/theme/defaults/limestone.json +100 -0
- package/src/modes/interactive/theme/defaults/mahogany.json +104 -0
- package/src/modes/interactive/theme/defaults/marble.json +99 -0
- package/src/modes/interactive/theme/defaults/obsidian.json +90 -0
- package/src/modes/interactive/theme/defaults/onyx.json +90 -0
- package/src/modes/interactive/theme/defaults/pearl.json +99 -0
- package/src/modes/interactive/theme/defaults/porcelain.json +90 -0
- package/src/modes/interactive/theme/defaults/quartz.json +102 -0
- package/src/modes/interactive/theme/defaults/sandstone.json +101 -0
- package/src/modes/interactive/theme/defaults/titanium.json +89 -0
- package/src/modes/print-mode.ts +14 -72
- package/src/modes/rpc/rpc-client.ts +23 -9
- package/src/modes/rpc/rpc-mode.ts +137 -125
- package/src/modes/rpc/rpc-types.ts +46 -24
- package/src/prompts/task.md +1 -0
- package/src/prompts/tools/gemini-image.md +4 -0
- package/src/prompts/tools/git.md +9 -0
- package/src/prompts/voice-summary.md +12 -0
- package/src/utils/image-convert.ts +26 -0
- package/src/utils/image-resize.ts +215 -0
- package/src/utils/shell-snapshot.ts +22 -20
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
import type { AgentToolContext } from "@oh-my-pi/pi-agent-core";
|
|
2
2
|
import type { CustomToolContext } from "../custom-tools/types";
|
|
3
|
-
import type {
|
|
3
|
+
import type { ExtensionUIContext } from "../extensions/types";
|
|
4
4
|
|
|
5
5
|
declare module "@oh-my-pi/pi-agent-core" {
|
|
6
6
|
interface AgentToolContext extends CustomToolContext {
|
|
7
|
-
ui?:
|
|
7
|
+
ui?: ExtensionUIContext;
|
|
8
8
|
hasUI?: boolean;
|
|
9
9
|
}
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
export interface ToolContextStore {
|
|
13
13
|
getContext(): AgentToolContext;
|
|
14
|
-
setUIContext(uiContext:
|
|
14
|
+
setUIContext(uiContext: ExtensionUIContext, hasUI: boolean): void;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export function createToolContextStore(getBaseContext: () => CustomToolContext): ToolContextStore {
|
|
18
|
-
let uiContext:
|
|
18
|
+
let uiContext: ExtensionUIContext | undefined;
|
|
19
19
|
let hasUI = false;
|
|
20
20
|
|
|
21
21
|
return {
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
* Client for interacting with Exa MCP servers via JSON-RPC 2.0 over HTTPS.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
8
|
+
import { homedir } from "node:os";
|
|
7
9
|
import type { TSchema } from "@sinclair/typebox";
|
|
8
10
|
import type { CustomTool } from "../../custom-tools/types";
|
|
9
11
|
import { logger } from "../../logger";
|
|
@@ -26,14 +28,13 @@ export async function findApiKey(): Promise<string | null> {
|
|
|
26
28
|
|
|
27
29
|
// Try loading from .env files in cwd and home
|
|
28
30
|
const cwd = process.cwd();
|
|
29
|
-
const home =
|
|
31
|
+
const home = homedir();
|
|
30
32
|
|
|
31
33
|
for (const dir of [cwd, home]) {
|
|
32
34
|
const envPath = `${dir}/.env`;
|
|
33
35
|
try {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const content = await file.text();
|
|
36
|
+
if (existsSync(envPath)) {
|
|
37
|
+
const content = readFileSync(envPath, "utf-8");
|
|
37
38
|
const match = content.match(/^EXA_API_KEY=(.+)$/m);
|
|
38
39
|
if (match?.[1]) {
|
|
39
40
|
return match[1].trim().replace(/^["']|["']$/g, "");
|
|
@@ -9,29 +9,37 @@ import { Text } from "@oh-my-pi/pi-tui";
|
|
|
9
9
|
import type { Theme } from "../../../modes/interactive/theme/theme";
|
|
10
10
|
import type { RenderResultOptions } from "../../custom-tools/types";
|
|
11
11
|
import { logger } from "../../logger";
|
|
12
|
+
import {
|
|
13
|
+
formatCount,
|
|
14
|
+
formatExpandHint,
|
|
15
|
+
formatMoreItems,
|
|
16
|
+
getDomain,
|
|
17
|
+
getPreviewLines,
|
|
18
|
+
getStyledStatusIcon,
|
|
19
|
+
PREVIEW_LIMITS,
|
|
20
|
+
TRUNCATE_LENGTHS,
|
|
21
|
+
truncate,
|
|
22
|
+
} from "../render-utils";
|
|
12
23
|
import type { ExaRenderDetails } from "./types";
|
|
13
24
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
25
|
+
const COLLAPSED_PREVIEW_LINES = PREVIEW_LIMITS.COLLAPSED_LINES;
|
|
26
|
+
const COLLAPSED_PREVIEW_LINE_LEN = TRUNCATE_LENGTHS.LONG;
|
|
27
|
+
const EXPANDED_TEXT_LINES = 5;
|
|
28
|
+
const EXPANDED_TEXT_LINE_LEN = 90;
|
|
29
|
+
const MAX_TITLE_LEN = TRUNCATE_LENGTHS.TITLE;
|
|
30
|
+
const MAX_HIGHLIGHT_LEN = TRUNCATE_LENGTHS.CONTENT;
|
|
31
|
+
|
|
32
|
+
function renderErrorMessage(message: string, theme: Theme): Text {
|
|
33
|
+
const clean = message.replace(/^Error:\s*/, "").trim();
|
|
34
|
+
return new Text(
|
|
35
|
+
`${getStyledStatusIcon("error", theme)} ${theme.fg("error", `Error: ${clean || "Unknown error"}`)}`,
|
|
36
|
+
0,
|
|
37
|
+
0,
|
|
38
|
+
);
|
|
19
39
|
}
|
|
20
40
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
try {
|
|
24
|
-
const u = new URL(url);
|
|
25
|
-
return u.hostname.replace(/^www\./, "");
|
|
26
|
-
} catch {
|
|
27
|
-
return url;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/** Get first N lines of text as preview */
|
|
32
|
-
function getPreviewLines(text: string, maxLines: number, maxLineLen: number, ellipsis: string): string[] {
|
|
33
|
-
const lines = text.split("\n").filter((l) => l.trim());
|
|
34
|
-
return lines.slice(0, maxLines).map((l) => truncate(l.trim(), maxLineLen, ellipsis));
|
|
41
|
+
function renderEmptyMessage(message: string, theme: Theme): Text {
|
|
42
|
+
return new Text(`${getStyledStatusIcon("warning", theme)} ${theme.fg("muted", message)}`, 0, 0);
|
|
35
43
|
}
|
|
36
44
|
|
|
37
45
|
/** Render Exa result with tree-based layout */
|
|
@@ -43,26 +51,42 @@ export function renderExaResult(
|
|
|
43
51
|
const { expanded } = options;
|
|
44
52
|
const details = result.details;
|
|
45
53
|
|
|
46
|
-
// Handle error case
|
|
47
54
|
if (details?.error) {
|
|
48
55
|
logger.error("Exa render error", { error: details.error, toolName: details.toolName });
|
|
49
|
-
return
|
|
56
|
+
return renderErrorMessage(details.error, uiTheme);
|
|
50
57
|
}
|
|
51
58
|
|
|
52
59
|
const response = details?.response;
|
|
53
60
|
if (!response) {
|
|
54
|
-
// Non-search response: show raw result
|
|
55
61
|
if (details?.raw) {
|
|
56
62
|
const rawText = typeof details.raw === "string" ? details.raw : JSON.stringify(details.raw, null, 2);
|
|
57
|
-
const
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
)
|
|
63
|
+
const rawLines = rawText.split("\n").filter((l) => l.trim());
|
|
64
|
+
const maxLines = expanded ? rawLines.length : Math.min(rawLines.length, COLLAPSED_PREVIEW_LINES);
|
|
65
|
+
const displayLines = rawLines.slice(0, maxLines);
|
|
66
|
+
const remaining = rawLines.length - maxLines;
|
|
67
|
+
const expandHint = formatExpandHint(expanded, remaining > 0, uiTheme);
|
|
68
|
+
|
|
69
|
+
let text = `${getStyledStatusIcon("info", uiTheme)} ${uiTheme.fg("dim", "Raw response")}${expandHint}`;
|
|
70
|
+
|
|
71
|
+
for (let i = 0; i < displayLines.length; i++) {
|
|
72
|
+
const isLast = i === displayLines.length - 1 && remaining === 0;
|
|
73
|
+
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
74
|
+
text += `\n ${uiTheme.fg("dim", branch)} ${uiTheme.fg(
|
|
75
|
+
"toolOutput",
|
|
76
|
+
truncate(displayLines[i], COLLAPSED_PREVIEW_LINE_LEN, uiTheme.format.ellipsis),
|
|
77
|
+
)}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (remaining > 0) {
|
|
81
|
+
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg(
|
|
82
|
+
"muted",
|
|
83
|
+
formatMoreItems(remaining, "line", uiTheme),
|
|
84
|
+
)}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return new Text(text, 0, 0);
|
|
64
88
|
}
|
|
65
|
-
return
|
|
89
|
+
return renderEmptyMessage("No response data", uiTheme);
|
|
66
90
|
}
|
|
67
91
|
|
|
68
92
|
const results = response.results ?? [];
|
|
@@ -70,118 +94,135 @@ export function renderExaResult(
|
|
|
70
94
|
const cost = response.costDollars?.total;
|
|
71
95
|
const time = response.searchTime;
|
|
72
96
|
|
|
73
|
-
|
|
74
|
-
const icon =
|
|
75
|
-
resultCount > 0 ? uiTheme.fg("success", uiTheme.format.bullet) : uiTheme.fg("warning", uiTheme.format.bullet);
|
|
76
|
-
const expandHint = expanded ? "" : uiTheme.fg("dim", " (Ctrl+O for full results)");
|
|
77
|
-
const toolLabel = details?.toolName ?? "Exa Search";
|
|
97
|
+
const icon = getStyledStatusIcon(resultCount > 0 ? "success" : "warning", uiTheme);
|
|
78
98
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
)
|
|
99
|
+
const metaParts = [formatCount("result", resultCount)];
|
|
100
|
+
if (cost !== undefined) metaParts.push(`cost:$${cost.toFixed(4)}`);
|
|
101
|
+
if (time !== undefined) metaParts.push(`time:${time.toFixed(2)}s`);
|
|
102
|
+
const summaryText = metaParts.join(uiTheme.sep.dot);
|
|
83
103
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
104
|
+
let hasMorePreview = false;
|
|
105
|
+
if (!expanded && resultCount > 0) {
|
|
106
|
+
const previewText = results[0].text ?? results[0].title ?? "";
|
|
107
|
+
const totalLines = previewText.split("\n").filter((l) => l.trim()).length;
|
|
108
|
+
hasMorePreview = totalLines > COLLAPSED_PREVIEW_LINES || resultCount > 1;
|
|
89
109
|
}
|
|
110
|
+
const expandHint = formatExpandHint(expanded, hasMorePreview, uiTheme);
|
|
90
111
|
|
|
91
|
-
let text =
|
|
112
|
+
let text = `${icon} ${uiTheme.fg("dim", summaryText)}${expandHint}`;
|
|
92
113
|
|
|
93
114
|
if (!expanded) {
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
if (resultCount === 0) {
|
|
116
|
+
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", "No results")}`;
|
|
117
|
+
return new Text(text, 0, 0);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const first = results[0];
|
|
121
|
+
const previewText = first.text ?? first.title ?? "";
|
|
122
|
+
const previewLines = previewText
|
|
123
|
+
? getPreviewLines(previewText, COLLAPSED_PREVIEW_LINES, COLLAPSED_PREVIEW_LINE_LEN, uiTheme.format.ellipsis)
|
|
124
|
+
: [];
|
|
125
|
+
const safePreviewLines = previewLines.length > 0 ? previewLines : ["No preview text"];
|
|
126
|
+
const totalLines = previewText.split("\n").filter((l) => l.trim()).length;
|
|
127
|
+
const remainingLines = Math.max(0, totalLines - previewLines.length);
|
|
128
|
+
const extraItems: string[] = [];
|
|
129
|
+
if (remainingLines > 0) {
|
|
130
|
+
extraItems.push(formatMoreItems(remainingLines, "line", uiTheme));
|
|
131
|
+
}
|
|
132
|
+
if (resultCount > 1) {
|
|
133
|
+
extraItems.push(formatMoreItems(resultCount - 1, "result", uiTheme));
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
for (let i = 0; i < safePreviewLines.length; i++) {
|
|
137
|
+
const isLast = i === safePreviewLines.length - 1 && extraItems.length === 0;
|
|
138
|
+
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
139
|
+
const line = safePreviewLines[i];
|
|
140
|
+
const color = line === "No preview text" ? "muted" : "toolOutput";
|
|
141
|
+
text += `\n ${uiTheme.fg("dim", branch)} ${uiTheme.fg(color, line)}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
for (let i = 0; i < extraItems.length; i++) {
|
|
145
|
+
const isLast = i === extraItems.length - 1;
|
|
146
|
+
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
147
|
+
text += `\n ${uiTheme.fg("dim", branch)} ${uiTheme.fg("muted", extraItems[i])}`;
|
|
148
|
+
}
|
|
103
149
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
150
|
+
return new Text(text, 0, 0);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (resultCount === 0) {
|
|
154
|
+
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("muted", "No results")}`;
|
|
155
|
+
return new Text(text, 0, 0);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (let i = 0; i < results.length; i++) {
|
|
159
|
+
const res = results[i];
|
|
160
|
+
const isLast = i === results.length - 1;
|
|
161
|
+
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
162
|
+
const cont = isLast ? " " : uiTheme.tree.vertical;
|
|
163
|
+
|
|
164
|
+
const title = truncate(res.title ?? "Untitled", MAX_TITLE_LEN, uiTheme.format.ellipsis);
|
|
165
|
+
const domain = res.url ? getDomain(res.url) : "";
|
|
166
|
+
const domainPart = domain ? uiTheme.fg("dim", ` (${domain})`) : "";
|
|
167
|
+
|
|
168
|
+
text += `\n ${uiTheme.fg("dim", branch)} ${uiTheme.fg("accent", title)}${domainPart}`;
|
|
169
|
+
|
|
170
|
+
if (res.url) {
|
|
171
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
172
|
+
"mdLinkUrl",
|
|
173
|
+
res.url,
|
|
174
|
+
)}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (res.author) {
|
|
178
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
179
|
+
"muted",
|
|
180
|
+
`Author: ${res.author}`,
|
|
181
|
+
)}`;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
if (res.publishedDate) {
|
|
185
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
186
|
+
"muted",
|
|
187
|
+
`Published: ${res.publishedDate}`,
|
|
188
|
+
)}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
if (res.text) {
|
|
192
|
+
const textLines = res.text.split("\n").filter((l) => l.trim());
|
|
193
|
+
const displayLines = textLines.slice(0, EXPANDED_TEXT_LINES);
|
|
194
|
+
for (const line of displayLines) {
|
|
195
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
196
|
+
"toolOutput",
|
|
197
|
+
truncate(line.trim(), EXPANDED_TEXT_LINE_LEN, uiTheme.format.ellipsis),
|
|
198
|
+
)}`;
|
|
199
|
+
}
|
|
200
|
+
if (textLines.length > EXPANDED_TEXT_LINES) {
|
|
201
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
107
202
|
"muted",
|
|
108
|
-
|
|
203
|
+
formatMoreItems(textLines.length - EXPANDED_TEXT_LINES, "line", uiTheme),
|
|
109
204
|
)}`;
|
|
110
205
|
}
|
|
206
|
+
}
|
|
111
207
|
|
|
112
|
-
|
|
113
|
-
|
|
208
|
+
if (res.highlights?.length) {
|
|
209
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
210
|
+
"accent",
|
|
211
|
+
"Highlights",
|
|
212
|
+
)}`;
|
|
213
|
+
const maxHighlights = Math.min(res.highlights.length, 3);
|
|
214
|
+
for (let j = 0; j < maxHighlights; j++) {
|
|
215
|
+
const h = res.highlights[j];
|
|
216
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
114
217
|
"muted",
|
|
115
|
-
`${
|
|
218
|
+
`${uiTheme.format.dash} ${truncate(h, MAX_HIGHLIGHT_LEN, uiTheme.format.ellipsis)}`,
|
|
116
219
|
)}`;
|
|
117
220
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
text += `\n ${uiTheme.fg("dim", uiTheme.tree.last)} ${uiTheme.fg("accent", "Results")}`;
|
|
124
|
-
|
|
125
|
-
for (let i = 0; i < results.length; i++) {
|
|
126
|
-
const res = results[i];
|
|
127
|
-
const isLast = i === results.length - 1;
|
|
128
|
-
const branch = isLast ? uiTheme.tree.last : uiTheme.tree.branch;
|
|
129
|
-
const cont = isLast ? " " : uiTheme.tree.vertical;
|
|
130
|
-
|
|
131
|
-
// Title + domain
|
|
132
|
-
const title = truncate(res.title ?? "Untitled", 60, uiTheme.format.ellipsis);
|
|
133
|
-
const domain = res.url ? getDomain(res.url) : "";
|
|
134
|
-
const domainPart = domain ? uiTheme.fg("dim", ` (${domain})`) : "";
|
|
135
|
-
|
|
136
|
-
text += `\n ${uiTheme.fg("dim", " ")} ${uiTheme.fg("dim", branch)} ${uiTheme.fg("accent", title)}${domainPart}`;
|
|
137
|
-
|
|
138
|
-
// URL
|
|
139
|
-
if (res.url) {
|
|
140
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg("mdLinkUrl", res.url)}`;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Author
|
|
144
|
-
if (res.author) {
|
|
145
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("muted", `Author: ${res.author}`)}`;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Published date
|
|
149
|
-
if (res.publishedDate) {
|
|
150
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("muted", `Published: ${res.publishedDate}`)}`;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Text content
|
|
154
|
-
if (res.text) {
|
|
155
|
-
const textLines = res.text.split("\n").filter((l) => l.trim());
|
|
156
|
-
const displayLines = textLines.slice(0, 5); // Show first 5 lines
|
|
157
|
-
for (const line of displayLines) {
|
|
158
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${truncate(line.trim(), 90, uiTheme.format.ellipsis)}`;
|
|
159
|
-
}
|
|
160
|
-
if (textLines.length > 5) {
|
|
161
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg(
|
|
162
|
-
"muted",
|
|
163
|
-
`${uiTheme.format.ellipsis} ${textLines.length - 5} more lines`,
|
|
164
|
-
)}`;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Highlights
|
|
169
|
-
if (res.highlights?.length) {
|
|
170
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("accent", "Highlights:")}`;
|
|
171
|
-
for (let j = 0; j < Math.min(res.highlights.length, 3); j++) {
|
|
172
|
-
const h = res.highlights[j];
|
|
173
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg(
|
|
174
|
-
"muted",
|
|
175
|
-
`${uiTheme.format.bullet} ${truncate(h, 80, uiTheme.format.ellipsis)}`,
|
|
176
|
-
)}`;
|
|
177
|
-
}
|
|
178
|
-
if (res.highlights.length > 3) {
|
|
179
|
-
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg(
|
|
180
|
-
"muted",
|
|
181
|
-
`${uiTheme.format.ellipsis} ${res.highlights.length - 3} more`,
|
|
182
|
-
)}`;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
221
|
+
if (res.highlights.length > maxHighlights) {
|
|
222
|
+
text += `\n ${uiTheme.fg("dim", cont)} ${uiTheme.fg("dim", uiTheme.tree.hook)} ${uiTheme.fg(
|
|
223
|
+
"muted",
|
|
224
|
+
formatMoreItems(res.highlights.length - maxHighlights, "highlight", uiTheme),
|
|
225
|
+
)}`;
|
|
185
226
|
}
|
|
186
227
|
}
|
|
187
228
|
}
|
|
@@ -191,10 +232,14 @@ export function renderExaResult(
|
|
|
191
232
|
|
|
192
233
|
/** Render Exa call (query/args preview) */
|
|
193
234
|
export function renderExaCall(args: Record<string, unknown>, toolName: string, uiTheme: Theme): Component {
|
|
194
|
-
const
|
|
235
|
+
const toolLabel = toolName || "Exa Search";
|
|
236
|
+
const query = typeof args.query === "string" ? truncate(args.query, 80, uiTheme.format.ellipsis) : "?";
|
|
195
237
|
const numResults = typeof args.num_results === "number" ? args.num_results : undefined;
|
|
196
|
-
const detail = numResults ? uiTheme.fg("dim", ` (${numResults} results)`) : "";
|
|
197
238
|
|
|
198
|
-
|
|
239
|
+
let text = `${uiTheme.fg("toolTitle", toolLabel)} ${uiTheme.fg("accent", query)}`;
|
|
240
|
+
if (numResults !== undefined) {
|
|
241
|
+
text += ` ${uiTheme.fg("muted", `results:${numResults}`)}`;
|
|
242
|
+
}
|
|
243
|
+
|
|
199
244
|
return new Text(text, 0, 0);
|
|
200
245
|
}
|