@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.
Files changed (151) hide show
  1. package/CHANGELOG.md +73 -0
  2. package/README.md +1 -1
  3. package/examples/custom-tools/subagent/index.ts +1 -1
  4. package/package.json +5 -3
  5. package/src/cli/args.ts +5 -6
  6. package/src/cli/file-processor.ts +3 -3
  7. package/src/cli/list-models.ts +2 -2
  8. package/src/cli/plugin-cli.ts +1 -1
  9. package/src/cli/session-picker.ts +2 -2
  10. package/src/cli.ts +1 -1
  11. package/src/config.ts +3 -3
  12. package/src/core/agent-session.ts +157 -15
  13. package/src/core/bash-executor.ts +50 -10
  14. package/src/core/compaction/branch-summarization.ts +5 -5
  15. package/src/core/compaction/compaction.ts +3 -3
  16. package/src/core/compaction/index.ts +3 -3
  17. package/src/core/custom-commands/bundled/review/index.ts +156 -0
  18. package/src/core/custom-commands/index.ts +15 -0
  19. package/src/core/custom-commands/loader.ts +232 -0
  20. package/src/core/custom-commands/types.ts +112 -0
  21. package/src/core/custom-tools/index.ts +3 -3
  22. package/src/core/custom-tools/loader.ts +10 -8
  23. package/src/core/custom-tools/types.ts +11 -6
  24. package/src/core/custom-tools/wrapper.ts +2 -1
  25. package/src/core/exec.ts +22 -12
  26. package/src/core/export-html/index.ts +5 -5
  27. package/src/core/file-mentions.ts +54 -0
  28. package/src/core/hooks/index.ts +5 -5
  29. package/src/core/hooks/loader.ts +21 -16
  30. package/src/core/hooks/runner.ts +6 -6
  31. package/src/core/hooks/tool-wrapper.ts +2 -2
  32. package/src/core/hooks/types.ts +12 -15
  33. package/src/core/index.ts +6 -6
  34. package/src/core/logger.ts +112 -0
  35. package/src/core/mcp/client.ts +3 -3
  36. package/src/core/mcp/config.ts +1 -1
  37. package/src/core/mcp/index.ts +12 -12
  38. package/src/core/mcp/loader.ts +2 -2
  39. package/src/core/mcp/manager.ts +6 -6
  40. package/src/core/mcp/tool-bridge.ts +3 -3
  41. package/src/core/mcp/transports/http.ts +1 -1
  42. package/src/core/mcp/transports/index.ts +2 -2
  43. package/src/core/mcp/transports/stdio.ts +1 -1
  44. package/src/core/messages.ts +22 -0
  45. package/src/core/model-registry.ts +2 -2
  46. package/src/core/model-resolver.ts +2 -2
  47. package/src/core/plugins/doctor.ts +1 -1
  48. package/src/core/plugins/index.ts +6 -6
  49. package/src/core/plugins/installer.ts +4 -4
  50. package/src/core/plugins/loader.ts +4 -9
  51. package/src/core/plugins/manager.ts +5 -5
  52. package/src/core/plugins/paths.ts +3 -3
  53. package/src/core/sdk.ts +77 -35
  54. package/src/core/session-manager.ts +6 -6
  55. package/src/core/settings-manager.ts +16 -3
  56. package/src/core/skills.ts +5 -5
  57. package/src/core/slash-commands.ts +60 -45
  58. package/src/core/system-prompt.ts +6 -6
  59. package/src/core/title-generator.ts +2 -2
  60. package/src/core/tools/bash.ts +32 -155
  61. package/src/core/tools/context.ts +2 -2
  62. package/src/core/tools/edit-diff.ts +3 -3
  63. package/src/core/tools/edit.ts +18 -5
  64. package/src/core/tools/exa/company.ts +3 -3
  65. package/src/core/tools/exa/index.ts +16 -17
  66. package/src/core/tools/exa/linkedin.ts +3 -3
  67. package/src/core/tools/exa/mcp-client.ts +9 -9
  68. package/src/core/tools/exa/render.ts +5 -5
  69. package/src/core/tools/exa/researcher.ts +3 -3
  70. package/src/core/tools/exa/search.ts +6 -5
  71. package/src/core/tools/exa/types.ts +5 -6
  72. package/src/core/tools/exa/websets.ts +3 -3
  73. package/src/core/tools/find.ts +3 -3
  74. package/src/core/tools/grep.ts +3 -3
  75. package/src/core/tools/index.ts +48 -34
  76. package/src/core/tools/ls.ts +4 -4
  77. package/src/core/tools/lsp/client.ts +161 -90
  78. package/src/core/tools/lsp/config.ts +1 -1
  79. package/src/core/tools/lsp/edits.ts +2 -2
  80. package/src/core/tools/lsp/index.ts +15 -13
  81. package/src/core/tools/lsp/render.ts +2 -2
  82. package/src/core/tools/lsp/rust-analyzer.ts +3 -3
  83. package/src/core/tools/lsp/utils.ts +1 -1
  84. package/src/core/tools/notebook.ts +1 -1
  85. package/src/core/tools/output.ts +175 -0
  86. package/src/core/tools/read.ts +7 -7
  87. package/src/core/tools/renderers.ts +92 -13
  88. package/src/core/tools/review.ts +268 -0
  89. package/src/core/tools/task/agents.ts +1 -1
  90. package/src/core/tools/task/bundled-agents/reviewer.md +52 -37
  91. package/src/core/tools/task/discovery.ts +2 -2
  92. package/src/core/tools/task/executor.ts +145 -28
  93. package/src/core/tools/task/index.ts +78 -30
  94. package/src/core/tools/task/model-resolver.ts +30 -20
  95. package/src/core/tools/task/parallel.ts +1 -1
  96. package/src/core/tools/task/render.ts +219 -30
  97. package/src/core/tools/task/subprocess-tool-registry.ts +89 -0
  98. package/src/core/tools/task/types.ts +36 -2
  99. package/src/core/tools/web-fetch.ts +5 -3
  100. package/src/core/tools/web-search/auth.ts +1 -1
  101. package/src/core/tools/web-search/index.ts +17 -15
  102. package/src/core/tools/web-search/providers/anthropic.ts +2 -2
  103. package/src/core/tools/web-search/providers/exa.ts +3 -5
  104. package/src/core/tools/web-search/providers/perplexity.ts +1 -1
  105. package/src/core/tools/web-search/render.ts +3 -3
  106. package/src/core/tools/write.ts +4 -4
  107. package/src/index.ts +29 -18
  108. package/src/main.ts +37 -32
  109. package/src/migrations.ts +3 -3
  110. package/src/modes/index.ts +5 -5
  111. package/src/modes/interactive/components/armin.ts +1 -1
  112. package/src/modes/interactive/components/assistant-message.ts +1 -1
  113. package/src/modes/interactive/components/bash-execution.ts +4 -4
  114. package/src/modes/interactive/components/bordered-loader.ts +2 -2
  115. package/src/modes/interactive/components/branch-summary-message.ts +2 -2
  116. package/src/modes/interactive/components/compaction-summary-message.ts +2 -2
  117. package/src/modes/interactive/components/diff.ts +1 -1
  118. package/src/modes/interactive/components/dynamic-border.ts +1 -1
  119. package/src/modes/interactive/components/footer.ts +5 -5
  120. package/src/modes/interactive/components/hook-editor.ts +2 -2
  121. package/src/modes/interactive/components/hook-input.ts +2 -2
  122. package/src/modes/interactive/components/hook-message.ts +3 -3
  123. package/src/modes/interactive/components/hook-selector.ts +2 -2
  124. package/src/modes/interactive/components/model-selector.ts +281 -59
  125. package/src/modes/interactive/components/oauth-selector.ts +3 -3
  126. package/src/modes/interactive/components/plugin-settings.ts +4 -4
  127. package/src/modes/interactive/components/queue-mode-selector.ts +2 -2
  128. package/src/modes/interactive/components/session-selector.ts +4 -4
  129. package/src/modes/interactive/components/settings-defs.ts +1 -1
  130. package/src/modes/interactive/components/settings-selector.ts +5 -5
  131. package/src/modes/interactive/components/show-images-selector.ts +2 -2
  132. package/src/modes/interactive/components/theme-selector.ts +2 -2
  133. package/src/modes/interactive/components/thinking-selector.ts +2 -2
  134. package/src/modes/interactive/components/tool-execution.ts +26 -8
  135. package/src/modes/interactive/components/tree-selector.ts +3 -3
  136. package/src/modes/interactive/components/user-message-selector.ts +2 -2
  137. package/src/modes/interactive/components/user-message.ts +1 -1
  138. package/src/modes/interactive/components/welcome.ts +2 -2
  139. package/src/modes/interactive/interactive-mode.ts +85 -41
  140. package/src/modes/interactive/theme/theme.ts +8 -7
  141. package/src/modes/print-mode.ts +4 -3
  142. package/src/modes/rpc/rpc-client.ts +4 -4
  143. package/src/modes/rpc/rpc-mode.ts +21 -11
  144. package/src/modes/rpc/rpc-types.ts +3 -3
  145. package/src/utils/changelog.ts +2 -2
  146. package/src/utils/clipboard.ts +1 -1
  147. package/src/utils/shell-snapshot.ts +218 -0
  148. package/src/utils/shell.ts +93 -13
  149. package/src/utils/tools-manager.ts +1 -1
  150. package/examples/custom-tools/subagent/agents/reviewer.md +0 -35
  151. 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.js";
8
- import { findSmolModel } from "./model-resolver.js";
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
 
@@ -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 type { Subprocess } from "bun";
7
- import { getShellConfig, killProcessTree } from "../../utils/shell.js";
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
- const { shell, args } = getShellConfig();
78
- const child: Subprocess = Bun.spawn([shell, ...args, command], {
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
- if (signal) {
107
- if (signal.aborted) {
108
- child.kill();
109
- throw new Error("Command aborted");
110
- }
111
- signal.addEventListener("abort", onAbort, { once: true });
112
- }
113
-
114
- // Set timeout if provided
115
- let timeoutHandle: Timer | undefined;
116
- if (timeout !== undefined && timeout > 0) {
117
- timeoutHandle = setTimeout(() => {
118
- timedOut = true;
119
- onAbort();
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
- if (timedOut) {
203
- let output = fullOutput;
204
- if (output) output += "\n\n";
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(fullOutput);
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: tempFilePath,
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(fullOutput.split("\n").pop() || "", "utf-8"));
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: ${tempFilePath}]`;
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 !== null) {
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.js";
3
- import type { HookUIContext } from "../hooks/types.js";
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 { constants } from "fs";
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");
@@ -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.js";
15
- import type { FileDiagnosticsResult } from "./lsp/index.js";
16
- import { resolveToCwd } from "./path-utils.js";
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.js";
9
- import type { ExaRenderDetails } from "./types.js";
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("company_research_exa", params, apiKey);
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.js";
13
- import type { ExaSettings } from "../../settings-manager.js";
14
- import { companyTool } from "./company.js";
15
- import { linkedinTool } from "./linkedin.js";
16
- import { researcherTools } from "./researcher.js";
17
- import { searchTools } from "./search.js";
18
- import type { ExaRenderDetails } from "./types.js";
19
- import { websetsTools } from "./websets.js";
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.js";
46
- export { linkedinTool } from "./linkedin.js";
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.js";
58
- export { renderExaCall, renderExaResult } from "./render.js";
59
- export { researcherTools } from "./researcher.js";
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.js";
60
+ export { searchTools } from "./search";
62
61
  // Re-export types and utilities
63
- export type { ExaRenderDetails, ExaSearchResponse, ExaSearchResult, MCPToolWrapperConfig } from "./types.js";
64
- export { websetsTools } from "./websets.js";
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.js";
9
- import type { ExaRenderDetails } from "./types.js";
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("linkedin_search_exa", params, apiKey);
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.js";
9
- import { logExaError } from "./logger.js";
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.js";
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
- logExaError(errorMsg, { url, method, params });
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
- logExaError("Failed to parse MCP response", { url, method, responseText: text.slice(0, 500) });
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
- logExaError("MCP tools/list error", { toolNames, error: response.error });
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
- logExaError("Websets MCP tools/list error", { error: response.error });
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
- logExaError("MCP tools/call error", { toolName, args, error: response.error });
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
- logExaError("Websets MCP tools/call error", { toolName, args, error: response.error });
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.js";
10
- import type { RenderResultOptions } from "../../custom-tools/types.js";
11
- import { logViewError } from "./logger.js";
12
- import type { ExaRenderDetails } from "./types.js";
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
- logViewError("Exa render error", { error: details.error, toolName: details.toolName });
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.js";
9
- import { callExaTool, findApiKey } from "./mcp-client.js";
10
- import type { ExaRenderDetails } from "./types.js";
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.js";
9
- import type { ExaRenderDetails } from "./types.js";
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("web_search", params, apiKey);
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 response = await callExaTool("deep_search_exa", params, apiKey);
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("crawling_exa", params, apiKey);
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., "web_search") */
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
- web_search: "exa_search",
125
- deep_search_exa: "exa_search_deep",
124
+ web_search_exa: "exa_search",
126
125
  get_code_context_exa: "exa_search_code",
127
- crawling_exa: "exa_crawl",
126
+ crawling: "exa_crawl",
128
127
  // LinkedIn
129
- linkedin_search_exa: "exa_linkedin",
128
+ linkedin_search: "exa_linkedin",
130
129
  // Company
131
- company_research_exa: "exa_company",
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.js";
9
- import { callWebsetsTool, findApiKey } from "./mcp-client.js";
10
- import type { ExaRenderDetails } from "./types.js";
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(
@@ -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.js";
7
- import { resolveToCwd } from "./path-utils.js";
8
- import { DEFAULT_MAX_BYTES, formatSize, type TruncationResult, truncateHead } from "./truncate.js";
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({
@@ -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.js";
7
- import { resolveToCwd } from "./path-utils.js";
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.js";
15
+ } from "./truncate";
16
16
 
17
17
  const grepSchema = Type.Object({
18
18
  pattern: Type.String({ description: "Search pattern (regex or literal string)" }),