@oh-my-pi/pi-coding-agent 1.341.0 → 2.0.1337
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 +73 -0
- package/README.md +1 -1
- package/examples/custom-tools/subagent/index.ts +1 -1
- package/package.json +5 -3
- package/src/cli/args.ts +5 -6
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +2 -2
- package/src/cli/plugin-cli.ts +1 -1
- package/src/cli/session-picker.ts +2 -2
- package/src/cli.ts +1 -1
- package/src/config.ts +3 -3
- package/src/core/agent-session.ts +157 -15
- package/src/core/bash-executor.ts +50 -10
- package/src/core/compaction/branch-summarization.ts +5 -5
- package/src/core/compaction/compaction.ts +3 -3
- package/src/core/compaction/index.ts +3 -3
- package/src/core/custom-commands/bundled/review/index.ts +156 -0
- package/src/core/custom-commands/index.ts +15 -0
- package/src/core/custom-commands/loader.ts +232 -0
- package/src/core/custom-commands/types.ts +112 -0
- package/src/core/custom-tools/index.ts +3 -3
- package/src/core/custom-tools/loader.ts +10 -8
- package/src/core/custom-tools/types.ts +11 -6
- package/src/core/custom-tools/wrapper.ts +2 -1
- package/src/core/exec.ts +22 -12
- package/src/core/export-html/index.ts +5 -5
- package/src/core/file-mentions.ts +54 -0
- package/src/core/hooks/index.ts +5 -5
- package/src/core/hooks/loader.ts +21 -16
- package/src/core/hooks/runner.ts +6 -6
- package/src/core/hooks/tool-wrapper.ts +2 -2
- package/src/core/hooks/types.ts +12 -15
- package/src/core/index.ts +6 -6
- package/src/core/logger.ts +112 -0
- package/src/core/mcp/client.ts +3 -3
- package/src/core/mcp/config.ts +1 -1
- package/src/core/mcp/index.ts +12 -12
- package/src/core/mcp/loader.ts +2 -2
- package/src/core/mcp/manager.ts +6 -6
- package/src/core/mcp/tool-bridge.ts +3 -3
- package/src/core/mcp/transports/http.ts +1 -1
- package/src/core/mcp/transports/index.ts +2 -2
- package/src/core/mcp/transports/stdio.ts +1 -1
- package/src/core/messages.ts +22 -0
- package/src/core/model-registry.ts +2 -2
- package/src/core/model-resolver.ts +2 -2
- package/src/core/plugins/doctor.ts +1 -1
- package/src/core/plugins/index.ts +6 -6
- package/src/core/plugins/installer.ts +4 -4
- package/src/core/plugins/loader.ts +4 -9
- package/src/core/plugins/manager.ts +5 -5
- package/src/core/plugins/paths.ts +3 -3
- package/src/core/sdk.ts +77 -35
- package/src/core/session-manager.ts +6 -6
- package/src/core/settings-manager.ts +16 -3
- package/src/core/skills.ts +5 -5
- package/src/core/slash-commands.ts +60 -45
- package/src/core/system-prompt.ts +6 -6
- package/src/core/title-generator.ts +2 -2
- package/src/core/tools/bash.ts +32 -155
- package/src/core/tools/context.ts +2 -2
- package/src/core/tools/edit-diff.ts +3 -3
- package/src/core/tools/edit.ts +18 -5
- package/src/core/tools/exa/company.ts +3 -3
- package/src/core/tools/exa/index.ts +16 -17
- package/src/core/tools/exa/linkedin.ts +3 -3
- package/src/core/tools/exa/mcp-client.ts +9 -9
- package/src/core/tools/exa/render.ts +5 -5
- package/src/core/tools/exa/researcher.ts +3 -3
- package/src/core/tools/exa/search.ts +6 -5
- package/src/core/tools/exa/types.ts +5 -6
- package/src/core/tools/exa/websets.ts +3 -3
- package/src/core/tools/find.ts +3 -3
- package/src/core/tools/grep.ts +3 -3
- package/src/core/tools/index.ts +48 -34
- package/src/core/tools/ls.ts +4 -4
- package/src/core/tools/lsp/client.ts +161 -90
- package/src/core/tools/lsp/config.ts +1 -1
- package/src/core/tools/lsp/edits.ts +2 -2
- package/src/core/tools/lsp/index.ts +15 -13
- package/src/core/tools/lsp/render.ts +2 -2
- package/src/core/tools/lsp/rust-analyzer.ts +3 -3
- package/src/core/tools/lsp/utils.ts +1 -1
- package/src/core/tools/notebook.ts +1 -1
- package/src/core/tools/output.ts +175 -0
- package/src/core/tools/read.ts +7 -7
- package/src/core/tools/renderers.ts +92 -13
- package/src/core/tools/review.ts +268 -0
- package/src/core/tools/task/agents.ts +1 -1
- package/src/core/tools/task/bundled-agents/reviewer.md +52 -37
- package/src/core/tools/task/discovery.ts +2 -2
- package/src/core/tools/task/executor.ts +145 -28
- package/src/core/tools/task/index.ts +78 -30
- package/src/core/tools/task/model-resolver.ts +30 -20
- package/src/core/tools/task/parallel.ts +1 -1
- package/src/core/tools/task/render.ts +219 -30
- package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
- package/src/core/tools/task/types.ts +36 -2
- package/src/core/tools/web-fetch.ts +5 -3
- package/src/core/tools/web-search/auth.ts +1 -1
- package/src/core/tools/web-search/index.ts +17 -15
- package/src/core/tools/web-search/providers/anthropic.ts +2 -2
- package/src/core/tools/web-search/providers/exa.ts +3 -5
- package/src/core/tools/web-search/providers/perplexity.ts +1 -1
- package/src/core/tools/web-search/render.ts +3 -3
- package/src/core/tools/write.ts +4 -4
- package/src/index.ts +29 -18
- package/src/main.ts +37 -32
- package/src/migrations.ts +3 -3
- package/src/modes/index.ts +5 -5
- package/src/modes/interactive/components/armin.ts +1 -1
- package/src/modes/interactive/components/assistant-message.ts +1 -1
- package/src/modes/interactive/components/bash-execution.ts +4 -4
- package/src/modes/interactive/components/bordered-loader.ts +2 -2
- package/src/modes/interactive/components/branch-summary-message.ts +2 -2
- package/src/modes/interactive/components/compaction-summary-message.ts +2 -2
- package/src/modes/interactive/components/diff.ts +1 -1
- package/src/modes/interactive/components/dynamic-border.ts +1 -1
- package/src/modes/interactive/components/footer.ts +5 -5
- package/src/modes/interactive/components/hook-editor.ts +2 -2
- package/src/modes/interactive/components/hook-input.ts +2 -2
- package/src/modes/interactive/components/hook-message.ts +3 -3
- package/src/modes/interactive/components/hook-selector.ts +2 -2
- package/src/modes/interactive/components/model-selector.ts +281 -59
- package/src/modes/interactive/components/oauth-selector.ts +3 -3
- package/src/modes/interactive/components/plugin-settings.ts +4 -4
- package/src/modes/interactive/components/queue-mode-selector.ts +2 -2
- package/src/modes/interactive/components/session-selector.ts +4 -4
- package/src/modes/interactive/components/settings-defs.ts +1 -1
- package/src/modes/interactive/components/settings-selector.ts +5 -5
- package/src/modes/interactive/components/show-images-selector.ts +2 -2
- package/src/modes/interactive/components/theme-selector.ts +2 -2
- package/src/modes/interactive/components/thinking-selector.ts +2 -2
- package/src/modes/interactive/components/tool-execution.ts +26 -8
- package/src/modes/interactive/components/tree-selector.ts +3 -3
- package/src/modes/interactive/components/user-message-selector.ts +2 -2
- package/src/modes/interactive/components/user-message.ts +1 -1
- package/src/modes/interactive/components/welcome.ts +2 -2
- package/src/modes/interactive/interactive-mode.ts +85 -41
- package/src/modes/interactive/theme/theme.ts +8 -7
- package/src/modes/print-mode.ts +4 -3
- package/src/modes/rpc/rpc-client.ts +4 -4
- package/src/modes/rpc/rpc-mode.ts +21 -11
- package/src/modes/rpc/rpc-types.ts +3 -3
- package/src/utils/changelog.ts +2 -2
- package/src/utils/clipboard.ts +1 -1
- package/src/utils/shell-snapshot.ts +218 -0
- package/src/utils/shell.ts +93 -13
- package/src/utils/tools-manager.ts +1 -1
- package/examples/custom-tools/subagent/agents/reviewer.md +0 -35
- package/src/core/tools/exa/logger.ts +0 -56
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
import type { Model } from "@oh-my-pi/pi-ai";
|
|
6
6
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
7
|
-
import type { ModelRegistry } from "./model-registry
|
|
8
|
-
import { findSmolModel } from "./model-resolver
|
|
7
|
+
import type { ModelRegistry } from "./model-registry";
|
|
8
|
+
import { findSmolModel } from "./model-resolver";
|
|
9
9
|
|
|
10
10
|
const TITLE_SYSTEM_PROMPT = `Generate a very short title (3-6 words) for a coding session based on the user's first message. The title should capture the main task or topic. Output ONLY the title, nothing else. No quotes, no punctuation at the end.
|
|
11
11
|
|
package/src/core/tools/bash.ts
CHANGED
|
@@ -1,20 +1,7 @@
|
|
|
1
|
-
import { createWriteStream } from "node:fs";
|
|
2
|
-
import { tmpdir } from "node:os";
|
|
3
|
-
import { join } from "node:path";
|
|
4
1
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
5
2
|
import { Type } from "@sinclair/typebox";
|
|
6
|
-
import
|
|
7
|
-
import {
|
|
8
|
-
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateTail } from "./truncate.js";
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Generate a unique temp file path for bash output
|
|
12
|
-
*/
|
|
13
|
-
function getTempFilePath(): string {
|
|
14
|
-
const randomId = crypto.getRandomValues(new Uint8Array(8));
|
|
15
|
-
const id = Array.from(randomId, (b) => b.toString(16).padStart(2, "0")).join("");
|
|
16
|
-
return join(tmpdir(), `pi-bash-${id}.log`);
|
|
17
|
-
}
|
|
3
|
+
import { executeBash } from "../bash-executor";
|
|
4
|
+
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateTail } from "./truncate";
|
|
18
5
|
|
|
19
6
|
const bashSchema = Type.Object({
|
|
20
7
|
command: Type.String({ description: "Bash command to execute" }),
|
|
@@ -74,140 +61,34 @@ Usage notes:
|
|
|
74
61
|
signal?: AbortSignal,
|
|
75
62
|
onUpdate?,
|
|
76
63
|
) => {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
cwd,
|
|
80
|
-
stdin: "ignore",
|
|
81
|
-
stdout: "pipe",
|
|
82
|
-
stderr: "pipe",
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
// We'll stream to a temp file if output gets large
|
|
86
|
-
let tempFilePath: string | undefined;
|
|
87
|
-
let tempFileStream: ReturnType<typeof createWriteStream> | undefined;
|
|
88
|
-
let totalBytes = 0;
|
|
89
|
-
|
|
90
|
-
// Keep a rolling buffer of the last chunks for tail truncation
|
|
91
|
-
const chunks: Buffer[] = [];
|
|
92
|
-
let chunksBytes = 0;
|
|
93
|
-
const maxChunksBytes = DEFAULT_MAX_BYTES * 2;
|
|
94
|
-
|
|
95
|
-
let timedOut = false;
|
|
96
|
-
let aborted = false;
|
|
97
|
-
|
|
98
|
-
// Handle abort signal
|
|
99
|
-
const onAbort = () => {
|
|
100
|
-
aborted = true;
|
|
101
|
-
if (child.pid) {
|
|
102
|
-
killProcessTree(child.pid);
|
|
103
|
-
}
|
|
104
|
-
};
|
|
64
|
+
// Track output for streaming updates
|
|
65
|
+
let currentOutput = "";
|
|
105
66
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
}, timeout * 1000);
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const handleData = (data: Buffer) => {
|
|
124
|
-
totalBytes += data.length;
|
|
125
|
-
|
|
126
|
-
// Start writing to temp file once we exceed the threshold
|
|
127
|
-
if (totalBytes > DEFAULT_MAX_BYTES && !tempFilePath) {
|
|
128
|
-
tempFilePath = getTempFilePath();
|
|
129
|
-
tempFileStream = createWriteStream(tempFilePath);
|
|
130
|
-
for (const chunk of chunks) {
|
|
131
|
-
tempFileStream.write(chunk);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
if (tempFileStream) {
|
|
136
|
-
tempFileStream.write(data);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
// Keep rolling buffer of recent data
|
|
140
|
-
chunks.push(data);
|
|
141
|
-
chunksBytes += data.length;
|
|
142
|
-
|
|
143
|
-
while (chunksBytes > maxChunksBytes && chunks.length > 1) {
|
|
144
|
-
const removed = chunks.shift()!;
|
|
145
|
-
chunksBytes -= removed.length;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Stream partial output to callback
|
|
149
|
-
if (onUpdate) {
|
|
150
|
-
const fullBuffer = Buffer.concat(chunks);
|
|
151
|
-
const fullText = fullBuffer.toString("utf-8");
|
|
152
|
-
const truncation = truncateTail(fullText);
|
|
153
|
-
onUpdate({
|
|
154
|
-
content: [{ type: "text", text: truncation.content || "" }],
|
|
155
|
-
details: {
|
|
156
|
-
truncation: truncation.truncated ? truncation : undefined,
|
|
157
|
-
fullOutputPath: tempFilePath,
|
|
158
|
-
},
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Read streams using Bun's ReadableStream API
|
|
164
|
-
const stdoutReader = (child.stdout as ReadableStream<Uint8Array>).getReader();
|
|
165
|
-
const stderrReader = (child.stderr as ReadableStream<Uint8Array>).getReader();
|
|
166
|
-
|
|
167
|
-
await Promise.all([
|
|
168
|
-
(async () => {
|
|
169
|
-
while (true) {
|
|
170
|
-
const { done, value } = await stdoutReader.read();
|
|
171
|
-
if (done) break;
|
|
172
|
-
handleData(Buffer.from(value));
|
|
173
|
-
}
|
|
174
|
-
})(),
|
|
175
|
-
(async () => {
|
|
176
|
-
while (true) {
|
|
177
|
-
const { done, value } = await stderrReader.read();
|
|
178
|
-
if (done) break;
|
|
179
|
-
handleData(Buffer.from(value));
|
|
67
|
+
const result = await executeBash(command, {
|
|
68
|
+
cwd,
|
|
69
|
+
timeout: timeout ? timeout * 1000 : undefined, // Convert to milliseconds
|
|
70
|
+
signal,
|
|
71
|
+
onChunk: (chunk) => {
|
|
72
|
+
currentOutput += chunk;
|
|
73
|
+
if (onUpdate) {
|
|
74
|
+
const truncation = truncateTail(currentOutput);
|
|
75
|
+
onUpdate({
|
|
76
|
+
content: [{ type: "text", text: truncation.content || "" }],
|
|
77
|
+
details: {
|
|
78
|
+
truncation: truncation.truncated ? truncation : undefined,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
180
81
|
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
const exitCode = await child.exited;
|
|
185
|
-
|
|
186
|
-
// Cleanup
|
|
187
|
-
if (timeoutHandle) clearTimeout(timeoutHandle);
|
|
188
|
-
if (signal) signal.removeEventListener("abort", onAbort);
|
|
189
|
-
if (tempFileStream) tempFileStream.end();
|
|
190
|
-
|
|
191
|
-
// Combine all buffered chunks
|
|
192
|
-
const fullBuffer = Buffer.concat(chunks);
|
|
193
|
-
const fullOutput = fullBuffer.toString("utf-8");
|
|
194
|
-
|
|
195
|
-
if (aborted && !timedOut) {
|
|
196
|
-
let output = fullOutput;
|
|
197
|
-
if (output) output += "\n\n";
|
|
198
|
-
output += "Command aborted";
|
|
199
|
-
throw new Error(output);
|
|
200
|
-
}
|
|
82
|
+
},
|
|
83
|
+
});
|
|
201
84
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
output += `Command timed out after ${timeout} seconds`;
|
|
206
|
-
throw new Error(output);
|
|
85
|
+
// Handle errors
|
|
86
|
+
if (result.cancelled) {
|
|
87
|
+
throw new Error(result.output || "Command aborted");
|
|
207
88
|
}
|
|
208
89
|
|
|
209
|
-
// Apply tail truncation
|
|
210
|
-
const truncation = truncateTail(
|
|
90
|
+
// Apply tail truncation for final output
|
|
91
|
+
const truncation = truncateTail(result.output);
|
|
211
92
|
let outputText = truncation.content || "(no output)";
|
|
212
93
|
|
|
213
94
|
let details: BashToolDetails | undefined;
|
|
@@ -215,28 +96,24 @@ Usage notes:
|
|
|
215
96
|
if (truncation.truncated) {
|
|
216
97
|
details = {
|
|
217
98
|
truncation,
|
|
218
|
-
fullOutputPath:
|
|
99
|
+
fullOutputPath: result.fullOutputPath,
|
|
219
100
|
};
|
|
220
101
|
|
|
221
102
|
const startLine = truncation.totalLines - truncation.outputLines + 1;
|
|
222
103
|
const endLine = truncation.totalLines;
|
|
223
104
|
|
|
224
105
|
if (truncation.lastLinePartial) {
|
|
225
|
-
const lastLineSize = formatSize(Buffer.byteLength(
|
|
226
|
-
outputText += `\n\n[Showing last ${formatSize(
|
|
227
|
-
truncation.outputBytes,
|
|
228
|
-
)} of line ${endLine} (line is ${lastLineSize}). Full output: ${tempFilePath}]`;
|
|
106
|
+
const lastLineSize = formatSize(Buffer.byteLength(result.output.split("\n").pop() || "", "utf-8"));
|
|
107
|
+
outputText += `\n\n[Showing last ${formatSize(truncation.outputBytes)} of line ${endLine} (line is ${lastLineSize}). Full output: ${result.fullOutputPath}]`;
|
|
229
108
|
} else if (truncation.truncatedBy === "lines") {
|
|
230
|
-
outputText += `\n\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${
|
|
109
|
+
outputText += `\n\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines}. Full output: ${result.fullOutputPath}]`;
|
|
231
110
|
} else {
|
|
232
|
-
outputText += `\n\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(
|
|
233
|
-
DEFAULT_MAX_BYTES,
|
|
234
|
-
)} limit). Full output: ${tempFilePath}]`;
|
|
111
|
+
outputText += `\n\n[Showing lines ${startLine}-${endLine} of ${truncation.totalLines} (${formatSize(DEFAULT_MAX_BYTES)} limit). Full output: ${result.fullOutputPath}]`;
|
|
235
112
|
}
|
|
236
113
|
}
|
|
237
114
|
|
|
238
|
-
if (exitCode !== 0 && exitCode !==
|
|
239
|
-
outputText += `\n\nCommand exited with code ${exitCode}`;
|
|
115
|
+
if (result.exitCode !== 0 && result.exitCode !== undefined) {
|
|
116
|
+
outputText += `\n\nCommand exited with code ${result.exitCode}`;
|
|
240
117
|
throw new Error(outputText);
|
|
241
118
|
}
|
|
242
119
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { AgentToolContext } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { CustomToolContext } from "../custom-tools/types
|
|
3
|
-
import type { HookUIContext } from "../hooks/types
|
|
2
|
+
import type { CustomToolContext } from "../custom-tools/types";
|
|
3
|
+
import type { HookUIContext } from "../hooks/types";
|
|
4
4
|
|
|
5
5
|
declare module "@oh-my-pi/pi-agent-core" {
|
|
6
6
|
interface AgentToolContext extends CustomToolContext {
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
* Used by both edit.ts (for execution) and tool-execution.ts (for preview rendering).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { constants } from "node:fs";
|
|
7
|
+
import { access, readFile } from "node:fs/promises";
|
|
6
8
|
import * as Diff from "diff";
|
|
7
|
-
import {
|
|
8
|
-
import { access, readFile } from "fs/promises";
|
|
9
|
-
import { resolveToCwd } from "./path-utils.js";
|
|
9
|
+
import { resolveToCwd } from "./path-utils";
|
|
10
10
|
|
|
11
11
|
export function detectLineEnding(content: string): "\r\n" | "\n" {
|
|
12
12
|
const crlfIdx = content.indexOf("\r\n");
|
package/src/core/tools/edit.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import { constants } from "node:fs";
|
|
2
|
+
import { access, readFile, writeFile } from "node:fs/promises";
|
|
1
3
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
2
4
|
import { Type } from "@sinclair/typebox";
|
|
3
|
-
import { constants } from "fs";
|
|
4
|
-
import { access, readFile, writeFile } from "fs/promises";
|
|
5
5
|
import {
|
|
6
6
|
DEFAULT_FUZZY_THRESHOLD,
|
|
7
7
|
detectLineEnding,
|
|
@@ -11,9 +11,9 @@ import {
|
|
|
11
11
|
normalizeToLF,
|
|
12
12
|
restoreLineEndings,
|
|
13
13
|
stripBom,
|
|
14
|
-
} from "./edit-diff
|
|
15
|
-
import type { FileDiagnosticsResult } from "./lsp/index
|
|
16
|
-
import { resolveToCwd } from "./path-utils
|
|
14
|
+
} from "./edit-diff";
|
|
15
|
+
import type { FileDiagnosticsResult } from "./lsp/index";
|
|
16
|
+
import { resolveToCwd } from "./path-utils";
|
|
17
17
|
|
|
18
18
|
const editSchema = Type.Object({
|
|
19
19
|
path: Type.String({ description: "Path to the file to edit (relative or absolute)" }),
|
|
@@ -63,6 +63,19 @@ Usage:
|
|
|
63
63
|
) => {
|
|
64
64
|
const absolutePath = resolveToCwd(path, cwd);
|
|
65
65
|
|
|
66
|
+
// Reject .ipynb files - use NotebookEdit tool instead
|
|
67
|
+
if (absolutePath.endsWith(".ipynb")) {
|
|
68
|
+
return {
|
|
69
|
+
content: [
|
|
70
|
+
{
|
|
71
|
+
type: "text",
|
|
72
|
+
text: "Cannot edit Jupyter notebooks with the Edit tool. Use the NotebookEdit tool instead.",
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
details: undefined,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
66
79
|
return new Promise<{
|
|
67
80
|
content: Array<{ type: "text"; text: string }>;
|
|
68
81
|
details: EditToolDetails | undefined;
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
8
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
9
|
-
import type { ExaRenderDetails } from "./types
|
|
8
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
9
|
+
import type { ExaRenderDetails } from "./types";
|
|
10
10
|
|
|
11
11
|
/** exa_company - Company research */
|
|
12
12
|
export const companyTool: CustomTool<any, ExaRenderDetails> = {
|
|
@@ -34,7 +34,7 @@ Parameters:
|
|
|
34
34
|
details: { error: "EXA_API_KEY not found", toolName: "exa_company" },
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
const response = await callExaTool("
|
|
37
|
+
const response = await callExaTool("company_research", params, apiKey);
|
|
38
38
|
|
|
39
39
|
if (isSearchResponse(response)) {
|
|
40
40
|
const formatted = formatSearchResults(response);
|
|
@@ -9,14 +9,14 @@
|
|
|
9
9
|
* - 14 websets tools (CRUD, items, search, enrichment, monitor)
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
13
|
-
import type { ExaSettings } from "../../settings-manager
|
|
14
|
-
import { companyTool } from "./company
|
|
15
|
-
import { linkedinTool } from "./linkedin
|
|
16
|
-
import { researcherTools } from "./researcher
|
|
17
|
-
import { searchTools } from "./search
|
|
18
|
-
import type { ExaRenderDetails } from "./types
|
|
19
|
-
import { websetsTools } from "./websets
|
|
12
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
13
|
+
import type { ExaSettings } from "../../settings-manager";
|
|
14
|
+
import { companyTool } from "./company";
|
|
15
|
+
import { linkedinTool } from "./linkedin";
|
|
16
|
+
import { researcherTools } from "./researcher";
|
|
17
|
+
import { searchTools } from "./search";
|
|
18
|
+
import type { ExaRenderDetails } from "./types";
|
|
19
|
+
import { websetsTools } from "./websets";
|
|
20
20
|
|
|
21
21
|
/** All Exa tools (22 total) - static export for backward compatibility */
|
|
22
22
|
export const exaTools: CustomTool<any, ExaRenderDetails>[] = [
|
|
@@ -42,9 +42,8 @@ export function getExaTools(settings: Required<ExaSettings>): CustomTool<any, Ex
|
|
|
42
42
|
return tools;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
export { companyTool } from "./company
|
|
46
|
-
export { linkedinTool } from "./linkedin
|
|
47
|
-
export { logExaError, logViewError } from "./logger.js";
|
|
45
|
+
export { companyTool } from "./company";
|
|
46
|
+
export { linkedinTool } from "./linkedin";
|
|
48
47
|
export {
|
|
49
48
|
callExaTool,
|
|
50
49
|
callWebsetsTool,
|
|
@@ -54,11 +53,11 @@ export {
|
|
|
54
53
|
findApiKey,
|
|
55
54
|
formatSearchResults,
|
|
56
55
|
isSearchResponse,
|
|
57
|
-
} from "./mcp-client
|
|
58
|
-
export { renderExaCall, renderExaResult } from "./render
|
|
59
|
-
export { researcherTools } from "./researcher
|
|
56
|
+
} from "./mcp-client";
|
|
57
|
+
export { renderExaCall, renderExaResult } from "./render";
|
|
58
|
+
export { researcherTools } from "./researcher";
|
|
60
59
|
// Re-export individual modules for selective importing
|
|
61
|
-
export { searchTools } from "./search
|
|
60
|
+
export { searchTools } from "./search";
|
|
62
61
|
// Re-export types and utilities
|
|
63
|
-
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult, MCPToolWrapperConfig } from "./types
|
|
64
|
-
export { websetsTools } from "./websets
|
|
62
|
+
export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult, MCPToolWrapperConfig } from "./types";
|
|
63
|
+
export { websetsTools } from "./websets";
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
8
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
9
|
-
import type { ExaRenderDetails } from "./types
|
|
8
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
9
|
+
import type { ExaRenderDetails } from "./types";
|
|
10
10
|
|
|
11
11
|
/** exa_linkedin - LinkedIn search */
|
|
12
12
|
export const linkedinTool: CustomTool<any, ExaRenderDetails> = {
|
|
@@ -34,7 +34,7 @@ Parameters:
|
|
|
34
34
|
details: { error: "EXA_API_KEY not found", toolName: "exa_linkedin" },
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
|
-
const response = await callExaTool("
|
|
37
|
+
const response = await callExaTool("linkedin_search", params, apiKey);
|
|
38
38
|
|
|
39
39
|
if (isSearchResponse(response)) {
|
|
40
40
|
const formatted = formatSearchResults(response);
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { TSchema } from "@sinclair/typebox";
|
|
8
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
9
|
-
import {
|
|
8
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
9
|
+
import { logger } from "../../logger";
|
|
10
10
|
import type {
|
|
11
11
|
ExaRenderDetails,
|
|
12
12
|
ExaSearchResponse,
|
|
@@ -15,7 +15,7 @@ import type {
|
|
|
15
15
|
MCPTool,
|
|
16
16
|
MCPToolsResponse,
|
|
17
17
|
MCPToolWrapperConfig,
|
|
18
|
-
} from "./types
|
|
18
|
+
} from "./types";
|
|
19
19
|
|
|
20
20
|
/** Find EXA_API_KEY from process.env or .env files */
|
|
21
21
|
export async function findApiKey(): Promise<string | null> {
|
|
@@ -89,7 +89,7 @@ export async function callMCP(url: string, method: string, params?: Record<strin
|
|
|
89
89
|
|
|
90
90
|
if (!response.ok) {
|
|
91
91
|
const errorMsg = `MCP request failed: ${response.status} ${response.statusText}`;
|
|
92
|
-
|
|
92
|
+
logger.error(errorMsg, { url, method, params });
|
|
93
93
|
throw new Error(errorMsg);
|
|
94
94
|
}
|
|
95
95
|
|
|
@@ -97,7 +97,7 @@ export async function callMCP(url: string, method: string, params?: Record<strin
|
|
|
97
97
|
const result = parseSSE(text);
|
|
98
98
|
|
|
99
99
|
if (!result) {
|
|
100
|
-
|
|
100
|
+
logger.error("Failed to parse MCP response", { url, method, responseText: text.slice(0, 500) });
|
|
101
101
|
throw new Error("Failed to parse MCP response");
|
|
102
102
|
}
|
|
103
103
|
|
|
@@ -110,7 +110,7 @@ export async function fetchExaTools(apiKey: string, toolNames: string[]): Promis
|
|
|
110
110
|
const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
|
|
111
111
|
|
|
112
112
|
if (response.error) {
|
|
113
|
-
|
|
113
|
+
logger.error("MCP tools/list error", { toolNames, error: response.error });
|
|
114
114
|
throw new Error(`MCP error: ${response.error.message}`);
|
|
115
115
|
}
|
|
116
116
|
|
|
@@ -123,7 +123,7 @@ export async function fetchWebsetsTools(apiKey: string): Promise<MCPTool[]> {
|
|
|
123
123
|
const response = (await callMCP(url, "tools/list")) as MCPToolsResponse;
|
|
124
124
|
|
|
125
125
|
if (response.error) {
|
|
126
|
-
|
|
126
|
+
logger.error("Websets MCP tools/list error", { error: response.error });
|
|
127
127
|
throw new Error(`MCP error: ${response.error.message}`);
|
|
128
128
|
}
|
|
129
129
|
|
|
@@ -139,7 +139,7 @@ export async function callExaTool(toolName: string, args: Record<string, unknown
|
|
|
139
139
|
})) as MCPCallResponse;
|
|
140
140
|
|
|
141
141
|
if (response.error) {
|
|
142
|
-
|
|
142
|
+
logger.error("MCP tools/call error", { toolName, args, error: response.error });
|
|
143
143
|
throw new Error(`MCP error: ${response.error.message}`);
|
|
144
144
|
}
|
|
145
145
|
|
|
@@ -159,7 +159,7 @@ export async function callWebsetsTool(
|
|
|
159
159
|
})) as MCPCallResponse;
|
|
160
160
|
|
|
161
161
|
if (response.error) {
|
|
162
|
-
|
|
162
|
+
logger.error("Websets MCP tools/call error", { toolName, args, error: response.error });
|
|
163
163
|
throw new Error(`MCP error: ${response.error.message}`);
|
|
164
164
|
}
|
|
165
165
|
|
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
|
|
7
7
|
import type { Component } from "@oh-my-pi/pi-tui";
|
|
8
8
|
import { Text } from "@oh-my-pi/pi-tui";
|
|
9
|
-
import type { Theme } from "../../../modes/interactive/theme/theme
|
|
10
|
-
import type { RenderResultOptions } from "../../custom-tools/types
|
|
11
|
-
import {
|
|
12
|
-
import type { ExaRenderDetails } from "./types
|
|
9
|
+
import type { Theme } from "../../../modes/interactive/theme/theme";
|
|
10
|
+
import type { RenderResultOptions } from "../../custom-tools/types";
|
|
11
|
+
import { logger } from "../../logger";
|
|
12
|
+
import type { ExaRenderDetails } from "./types";
|
|
13
13
|
|
|
14
14
|
// Tree formatting constants
|
|
15
15
|
const TREE_MID = "├─";
|
|
@@ -51,7 +51,7 @@ export function renderExaResult(
|
|
|
51
51
|
|
|
52
52
|
// Handle error case
|
|
53
53
|
if (details?.error) {
|
|
54
|
-
|
|
54
|
+
logger.error("Exa render error", { error: details.error, toolName: details.toolName });
|
|
55
55
|
return new Text(theme.fg("error", `Error: ${details.error}`), 0, 0);
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
8
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
9
|
-
import { callExaTool, findApiKey } from "./mcp-client
|
|
10
|
-
import type { ExaRenderDetails } from "./types
|
|
8
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
9
|
+
import { callExaTool, findApiKey } from "./mcp-client";
|
|
10
|
+
import type { ExaRenderDetails } from "./types";
|
|
11
11
|
|
|
12
12
|
const researcherStartTool: CustomTool<any, ExaRenderDetails> = {
|
|
13
13
|
name: "exa_researcher_start",
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
8
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
9
|
-
import type { ExaRenderDetails } from "./types
|
|
8
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
9
|
+
import type { ExaRenderDetails } from "./types";
|
|
10
10
|
|
|
11
11
|
/** exa_search - Basic neural/keyword search */
|
|
12
12
|
const exaSearchTool: CustomTool<any, ExaRenderDetails> = {
|
|
@@ -91,7 +91,7 @@ Parameters:
|
|
|
91
91
|
details: { error: "EXA_API_KEY not found", toolName: "exa_search" },
|
|
92
92
|
};
|
|
93
93
|
}
|
|
94
|
-
const response = await callExaTool("
|
|
94
|
+
const response = await callExaTool("web_search_exa", params, apiKey);
|
|
95
95
|
|
|
96
96
|
if (isSearchResponse(response)) {
|
|
97
97
|
const formatted = formatSearchResults(response);
|
|
@@ -187,7 +187,8 @@ Similar parameters to exa_search, optimized for research depth.`,
|
|
|
187
187
|
details: { error: "EXA_API_KEY not found", toolName: "exa_search_deep" },
|
|
188
188
|
};
|
|
189
189
|
}
|
|
190
|
-
const
|
|
190
|
+
const args = { ...params, type: "deep" };
|
|
191
|
+
const response = await callExaTool("web_search_exa", args, apiKey);
|
|
191
192
|
|
|
192
193
|
if (isSearchResponse(response)) {
|
|
193
194
|
const formatted = formatSearchResults(response);
|
|
@@ -305,7 +306,7 @@ Parameters:
|
|
|
305
306
|
details: { error: "EXA_API_KEY not found", toolName: "exa_crawl" },
|
|
306
307
|
};
|
|
307
308
|
}
|
|
308
|
-
const response = await callExaTool("
|
|
309
|
+
const response = await callExaTool("crawling", params, apiKey);
|
|
309
310
|
|
|
310
311
|
if (isSearchResponse(response)) {
|
|
311
312
|
const formatted = formatSearchResults(response);
|
|
@@ -23,7 +23,7 @@ export interface MCPToolWrapperConfig {
|
|
|
23
23
|
name: string;
|
|
24
24
|
/** Display label for UI */
|
|
25
25
|
label: string;
|
|
26
|
-
/** MCP tool name to call (e.g., "
|
|
26
|
+
/** MCP tool name to call (e.g., "web_search_exa") */
|
|
27
27
|
mcpToolName: string;
|
|
28
28
|
/** Whether this is a websets tool (uses different MCP endpoint) */
|
|
29
29
|
isWebsetsTool?: boolean;
|
|
@@ -121,14 +121,13 @@ export interface WebsetEnrichment {
|
|
|
121
121
|
/** Tool name mappings: MCP name -> our tool name */
|
|
122
122
|
export const EXA_TOOL_MAPPINGS = {
|
|
123
123
|
// Search tools
|
|
124
|
-
|
|
125
|
-
deep_search_exa: "exa_search_deep",
|
|
124
|
+
web_search_exa: "exa_search",
|
|
126
125
|
get_code_context_exa: "exa_search_code",
|
|
127
|
-
|
|
126
|
+
crawling: "exa_crawl",
|
|
128
127
|
// LinkedIn
|
|
129
|
-
|
|
128
|
+
linkedin_search: "exa_linkedin",
|
|
130
129
|
// Company
|
|
131
|
-
|
|
130
|
+
company_research: "exa_company",
|
|
132
131
|
// Researcher
|
|
133
132
|
deep_researcher_start: "exa_researcher_start",
|
|
134
133
|
deep_researcher_check: "exa_researcher_poll",
|
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
8
|
-
import type { CustomTool } from "../../custom-tools/types
|
|
9
|
-
import { callWebsetsTool, findApiKey } from "./mcp-client
|
|
10
|
-
import type { ExaRenderDetails } from "./types
|
|
8
|
+
import type { CustomTool } from "../../custom-tools/types";
|
|
9
|
+
import { callWebsetsTool, findApiKey } from "./mcp-client";
|
|
10
|
+
import type { ExaRenderDetails } from "./types";
|
|
11
11
|
|
|
12
12
|
/** Helper to create a websets tool with proper execute signature */
|
|
13
13
|
function createWebsetTool(
|
package/src/core/tools/find.ts
CHANGED
|
@@ -3,9 +3,9 @@ import path from "node:path";
|
|
|
3
3
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
4
4
|
import { Type } from "@sinclair/typebox";
|
|
5
5
|
import { globSync } from "glob";
|
|
6
|
-
import { ensureTool } from "../../utils/tools-manager
|
|
7
|
-
import { resolveToCwd } from "./path-utils
|
|
8
|
-
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate
|
|
6
|
+
import { ensureTool } from "../../utils/tools-manager";
|
|
7
|
+
import { resolveToCwd } from "./path-utils";
|
|
8
|
+
import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate";
|
|
9
9
|
|
|
10
10
|
const findSchema = Type.Object({
|
|
11
11
|
pattern: Type.String({
|
package/src/core/tools/grep.ts
CHANGED
|
@@ -3,8 +3,8 @@ import nodePath from "node:path";
|
|
|
3
3
|
import type { AgentTool } from "@oh-my-pi/pi-agent-core";
|
|
4
4
|
import { Type } from "@sinclair/typebox";
|
|
5
5
|
import type { Subprocess } from "bun";
|
|
6
|
-
import { ensureTool } from "../../utils/tools-manager
|
|
7
|
-
import { resolveToCwd } from "./path-utils
|
|
6
|
+
import { ensureTool } from "../../utils/tools-manager";
|
|
7
|
+
import { resolveToCwd } from "./path-utils";
|
|
8
8
|
import {
|
|
9
9
|
DEFAULT_MAX_BYTES,
|
|
10
10
|
formatSize,
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
type TruncationResult,
|
|
13
13
|
truncateHead,
|
|
14
14
|
truncateLine,
|
|
15
|
-
} from "./truncate
|
|
15
|
+
} from "./truncate";
|
|
16
16
|
|
|
17
17
|
const grepSchema = Type.Object({
|
|
18
18
|
pattern: Type.String({ description: "Search pattern (regex or literal string)" }),
|