@jarcelao/pi-exa-api 0.3.0 → 0.3.1

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/README.md CHANGED
@@ -11,7 +11,7 @@ pi install npm:@jarcelao/pi-exa-api
11
11
  ```
12
12
 
13
13
  > [!NOTE]
14
- > This extension is tested up to `pi-coding-agent` v0.70.6
14
+ > This extension is tested up to `pi-coding-agent` v0.74.0
15
15
 
16
16
  ## Configuration
17
17
 
@@ -2,8 +2,8 @@
2
2
  * Result formatting utilities
3
3
  */
4
4
 
5
- import { keyHint } from "@mariozechner/pi-coding-agent";
6
- import type { Theme } from "@mariozechner/pi-coding-agent";
5
+ import { keyHint } from "@earendil-works/pi-coding-agent";
6
+ import type { Theme } from "@earendil-works/pi-coding-agent";
7
7
 
8
8
  import type {
9
9
  ExaSearchResult,
@@ -9,34 +9,14 @@
9
9
  * Also registers the /exa-status command to check API key configuration.
10
10
  */
11
11
 
12
- import type { ExtensionAPI, ExtensionContext } from "@mariozechner/pi-coding-agent";
12
+ import type { ExtensionAPI, ExtensionContext } from "@earendil-works/pi-coding-agent";
13
13
 
14
14
  import { getApiKey } from "./api-key.ts";
15
- import { createMissingApiKeyError } from "./errors.ts";
16
- import { mapSearchContentType, mapFetchContentType } from "./content-types.ts";
17
- import {
18
- formatSearchResults,
19
- formatFetchResult,
20
- formatCodeContextResult,
21
- parseCostDollars,
22
- } from "./formatters.ts";
23
15
 
24
16
  import { createExaSearchTool } from "./tools/search.ts";
25
17
  import { createExaFetchTool } from "./tools/fetch.ts";
26
18
  import { createExaCodeContextTool } from "./tools/code-context.ts";
27
19
 
28
- // Re-export all utilities for testing
29
- export {
30
- getApiKey,
31
- createMissingApiKeyError,
32
- mapSearchContentType,
33
- mapFetchContentType,
34
- formatSearchResults,
35
- formatFetchResult,
36
- formatCodeContextResult,
37
- parseCostDollars,
38
- };
39
-
40
20
  export default function exaSearchExtension(pi: ExtensionAPI): void {
41
21
  pi.on("session_start", async (_event: unknown, ctx: ExtensionContext) => {
42
22
  const hasKey = !!getApiKey();
@@ -2,28 +2,20 @@
2
2
  * Exa Code Context tool definition
3
3
  */
4
4
 
5
- import { mkdtemp, writeFile } from "node:fs/promises";
6
- import { tmpdir } from "node:os";
7
- import { join } from "node:path";
8
- import type { Theme } from "@mariozechner/pi-coding-agent";
9
- import {
10
- DEFAULT_MAX_BYTES,
11
- DEFAULT_MAX_LINES,
12
- defineTool,
13
- formatSize,
14
- truncateHead,
15
- } from "@mariozechner/pi-coding-agent";
16
- import { Text } from "@mariozechner/pi-tui";
5
+ import type { Theme } from "@earendil-works/pi-coding-agent";
6
+ import { defineTool } from "@earendil-works/pi-coding-agent";
17
7
  import { Type, type Static } from "typebox";
18
8
 
19
- import { getApiKey } from "../api-key.ts";
20
- import { createMissingApiKeyError } from "../errors.ts";
21
- import {
22
- formatCodeContextResult,
23
- formatToolOutputPreview,
24
- parseCostDollars,
25
- } from "../formatters.ts";
9
+ import { formatCodeContextResult, parseCostDollars } from "../formatters.ts";
26
10
  import type { CodeContextDetails, CodeContextResponse } from "../types.ts";
11
+ import {
12
+ requireApiKey,
13
+ truncateAndSave,
14
+ renderToolCall,
15
+ renderToolResult,
16
+ formatCost,
17
+ EXA_CONTEXT_API_URL,
18
+ } from "./shared.ts";
27
19
 
28
20
  // Tool parameter schema
29
21
  export const ExaCodeContextParams = Type.Object({
@@ -62,15 +54,9 @@ export function createExaCodeContextTool() {
62
54
  _onUpdate: unknown,
63
55
  _ctx: unknown,
64
56
  ) {
65
- const apiKey = getApiKey();
66
- if (!apiKey) {
67
- throw createMissingApiKeyError();
68
- }
57
+ const apiKey = requireApiKey();
69
58
 
70
59
  // Ensure tokensNum is the correct type: number or "dynamic"
71
- // The schema accepts both string and number, but the Exa API requires:
72
- // - A number (e.g., 5000)
73
- // - The literal string "dynamic"
74
60
  let tokensNum: string | number = params.tokensNum ?? "dynamic";
75
61
  if (typeof tokensNum === "string" && tokensNum !== "dynamic") {
76
62
  tokensNum = Number(tokensNum);
@@ -78,7 +64,7 @@ export function createExaCodeContextTool() {
78
64
 
79
65
  let response: CodeContextResponse;
80
66
  try {
81
- const httpResponse = await fetch("https://api.exa.ai/context", {
67
+ const httpResponse = await fetch(EXA_CONTEXT_API_URL, {
82
68
  method: "POST",
83
69
  headers: {
84
70
  "Content-Type": "application/json",
@@ -101,24 +87,8 @@ export function createExaCodeContextTool() {
101
87
  throw new Error(`Exa Context API error: ${message}`);
102
88
  }
103
89
 
104
- let output = formatCodeContextResult(response);
105
-
106
- const truncation = truncateHead(output, {
107
- maxLines: DEFAULT_MAX_LINES,
108
- maxBytes: DEFAULT_MAX_BYTES,
109
- });
110
-
111
- let result = truncation.content;
112
-
113
- if (truncation.truncated) {
114
- const tempDir = await mkdtemp(join(tmpdir(), "pi-exa-"));
115
- const tempFile = join(tempDir, "output.txt");
116
- await writeFile(tempFile, output, "utf8");
117
- result += `\n\n[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines`;
118
- result += ` (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}).`;
119
- result += ` Full output saved to: ${tempFile}]`;
120
- }
121
-
90
+ const output = formatCodeContextResult(response);
91
+ const result = await truncateAndSave(output);
122
92
  const cost = parseCostDollars(response.costDollars);
123
93
 
124
94
  return {
@@ -133,31 +103,20 @@ export function createExaCodeContextTool() {
133
103
  },
134
104
 
135
105
  renderCall(args: CodeContextParams, theme: Theme) {
136
- const preview = args.query.length > 50 ? args.query.slice(0, 50) + "..." : args.query;
137
106
  const desc = `${args.tokensNum ?? "dynamic"} tokens`;
138
- const text =
139
- theme.fg("toolTitle", theme.bold("exa_code_context ")) +
140
- theme.fg("muted", preview) +
141
- theme.fg("dim", ` ${desc}`);
142
- return new Text(text, 0, 0);
107
+ return renderToolCall("exa_code_context", args.query, desc, theme);
143
108
  },
144
109
 
145
110
  renderResult(result, options, theme, context) {
146
111
  const details = result.details as CodeContextDetails | undefined;
147
- const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
148
-
149
- let header = "";
150
- if (details) {
151
- const cost = details.cost ? ` $${details.cost.total.toFixed(6)}` : "";
152
- header = theme.fg(
153
- "success",
154
- `✓ ${details.resultsCount} sources ${details.outputTokens} tokens${cost}`,
155
- );
156
- }
157
-
158
- const preview = formatToolOutputPreview(result, options, theme);
159
- text.setText(preview ? `${header}\n${preview}` : header);
160
- return text;
112
+ const cost = details ? formatCost(details.cost) : "";
113
+ const header = details
114
+ ? theme.fg(
115
+ "success",
116
+ `✓ ${details.resultsCount} sources${details.outputTokens} tokens${cost}`,
117
+ )
118
+ : "";
119
+ return renderToolResult(header, result, options, theme, context);
161
120
  },
162
121
  });
163
122
  }
@@ -2,26 +2,21 @@
2
2
  * Exa Fetch tool definition
3
3
  */
4
4
 
5
- import { mkdtemp, writeFile } from "node:fs/promises";
6
- import { tmpdir } from "node:os";
7
- import { join } from "node:path";
8
- import type { ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
9
- import {
10
- DEFAULT_MAX_BYTES,
11
- DEFAULT_MAX_LINES,
12
- defineTool,
13
- formatSize,
14
- truncateHead,
15
- } from "@mariozechner/pi-coding-agent";
16
- import { Text } from "@mariozechner/pi-tui";
5
+ import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
6
+ import { defineTool } from "@earendil-works/pi-coding-agent";
17
7
  import { Type, type Static } from "typebox";
18
8
  import Exa from "exa-js";
19
9
 
20
- import { getApiKey } from "../api-key.ts";
21
- import { createMissingApiKeyError } from "../errors.ts";
22
10
  import { mapFetchContentType } from "../content-types.ts";
23
- import { formatFetchResult, formatToolOutputPreview } from "../formatters.ts";
11
+ import { formatFetchResult } from "../formatters.ts";
24
12
  import type { FetchContentType, FetchDetails, ExaSearchResult } from "../types.ts";
13
+ import {
14
+ requireApiKey,
15
+ truncateAndSave,
16
+ renderToolCall,
17
+ renderToolResult,
18
+ formatCost,
19
+ } from "./shared.ts";
25
20
 
26
21
  // Tool parameter schema
27
22
  export const ExaFetchParams = Type.Object({
@@ -58,11 +53,7 @@ export function createExaFetchTool() {
58
53
  _onUpdate: unknown,
59
54
  _ctx: ExtensionContext,
60
55
  ) {
61
- const apiKey = getApiKey();
62
- if (!apiKey) {
63
- throw createMissingApiKeyError();
64
- }
65
-
56
+ const apiKey = requireApiKey();
66
57
  const exa = new Exa(apiKey);
67
58
 
68
59
  const contentsOptions: {
@@ -96,23 +87,8 @@ export function createExaFetchTool() {
96
87
  }
97
88
 
98
89
  const result = response.results[0] as ExaSearchResult;
99
- let output = formatFetchResult(result, (params.contentType ?? "text") as FetchContentType);
100
-
101
- const truncation = truncateHead(output, {
102
- maxLines: DEFAULT_MAX_LINES,
103
- maxBytes: DEFAULT_MAX_BYTES,
104
- });
105
-
106
- let content = truncation.content;
107
-
108
- if (truncation.truncated) {
109
- const tempDir = await mkdtemp(join(tmpdir(), "pi-exa-"));
110
- const tempFile = join(tempDir, "output.txt");
111
- await writeFile(tempFile, output, "utf8");
112
- content += `\n\n[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines`;
113
- content += ` (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}).`;
114
- content += ` Full output saved to: ${tempFile}]`;
115
- }
90
+ const output = formatFetchResult(result, (params.contentType ?? "text") as FetchContentType);
91
+ const content = await truncateAndSave(output);
116
92
 
117
93
  return {
118
94
  content: [{ type: "text", text: content }],
@@ -125,30 +101,19 @@ export function createExaFetchTool() {
125
101
  },
126
102
 
127
103
  renderCall(args: FetchParams, theme: Theme) {
128
- const urlPreview = args.url.length > 40 ? args.url.slice(0, 40) + "..." : args.url;
129
104
  const desc = args.contentType ?? "text";
130
- const text =
131
- theme.fg("toolTitle", theme.bold("exa_fetch ")) +
132
- theme.fg("muted", urlPreview) +
133
- theme.fg("dim", ` ${desc}`);
134
- return new Text(text, 0, 0);
105
+ return renderToolCall("exa_fetch", args.url, desc, theme);
135
106
  },
136
107
 
137
108
  renderResult(result, options, theme, context) {
138
109
  const details = result.details as FetchDetails | undefined;
139
- const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
140
-
141
- let header = "";
142
- if (details) {
143
- const cost = details.cost ? ` • $${details.cost.total.toFixed(6)}` : "";
144
- header = details.title
110
+ const cost = details ? formatCost(details.cost) : "";
111
+ const header = details
112
+ ? details.title
145
113
  ? theme.fg("success", `✓ ${details.title}${cost}`)
146
- : theme.fg("success", `✓ Fetched${cost}`);
147
- }
148
-
149
- const preview = formatToolOutputPreview(result, options, theme);
150
- text.setText(preview ? `${header}\n${preview}` : header);
151
- return text;
114
+ : theme.fg("success", `✓ Fetched${cost}`)
115
+ : "";
116
+ return renderToolResult(header, result, options, theme, context);
152
117
  },
153
118
  });
154
119
  }
@@ -2,26 +2,21 @@
2
2
  * Exa Search tool definition
3
3
  */
4
4
 
5
- import { mkdtemp, writeFile } from "node:fs/promises";
6
- import { tmpdir } from "node:os";
7
- import { join } from "node:path";
8
- import type { ExtensionContext, Theme } from "@mariozechner/pi-coding-agent";
9
- import {
10
- DEFAULT_MAX_BYTES,
11
- DEFAULT_MAX_LINES,
12
- defineTool,
13
- formatSize,
14
- truncateHead,
15
- } from "@mariozechner/pi-coding-agent";
16
- import { Text } from "@mariozechner/pi-tui";
5
+ import type { ExtensionContext, Theme } from "@earendil-works/pi-coding-agent";
6
+ import { defineTool } from "@earendil-works/pi-coding-agent";
17
7
  import { Type, type Static } from "typebox";
18
8
  import Exa from "exa-js";
19
9
 
20
- import { getApiKey } from "../api-key.ts";
21
- import { createMissingApiKeyError } from "../errors.ts";
22
10
  import { mapSearchContentType } from "../content-types.ts";
23
- import { formatSearchResults, formatToolOutputPreview } from "../formatters.ts";
11
+ import { formatSearchResults } from "../formatters.ts";
24
12
  import type { SearchContentType, SearchDetails, ExaSearchResult } from "../types.ts";
13
+ import {
14
+ requireApiKey,
15
+ truncateAndSave,
16
+ renderToolCall,
17
+ renderToolResult,
18
+ formatCost,
19
+ } from "./shared.ts";
25
20
 
26
21
  // Tool parameter schema
27
22
  export const ExaSearchParams = Type.Object({
@@ -63,11 +58,7 @@ export function createExaSearchTool() {
63
58
  _onUpdate: unknown,
64
59
  _ctx: ExtensionContext,
65
60
  ) {
66
- const apiKey = getApiKey();
67
- if (!apiKey) {
68
- throw createMissingApiKeyError();
69
- }
70
-
61
+ const apiKey = requireApiKey();
71
62
  const numResults = Math.max(1, Math.min(100, params.numResults ?? 10));
72
63
  const exa = new Exa(apiKey);
73
64
 
@@ -89,26 +80,12 @@ export function createExaSearchTool() {
89
80
  throw new Error(`Exa API error: ${message}`);
90
81
  }
91
82
 
92
- let output = formatSearchResults({
83
+ const output = formatSearchResults({
93
84
  results: response.results as ExaSearchResult[],
94
85
  costDollars: response.costDollars as { total: number } | undefined,
95
86
  });
96
87
 
97
- const truncation = truncateHead(output, {
98
- maxLines: DEFAULT_MAX_LINES,
99
- maxBytes: DEFAULT_MAX_BYTES,
100
- });
101
-
102
- let result = truncation.content;
103
-
104
- if (truncation.truncated) {
105
- const tempDir = await mkdtemp(join(tmpdir(), "pi-exa-"));
106
- const tempFile = join(tempDir, "output.txt");
107
- await writeFile(tempFile, output, "utf8");
108
- result += `\n\n[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines`;
109
- result += ` (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}).`;
110
- result += ` Full output saved to: ${tempFile}]`;
111
- }
88
+ const result = await truncateAndSave(output);
112
89
 
113
90
  return {
114
91
  content: [{ type: "text", text: result }],
@@ -121,28 +98,15 @@ export function createExaSearchTool() {
121
98
  },
122
99
 
123
100
  renderCall(args: SearchParams, theme: Theme) {
124
- const preview = args.query.length > 50 ? args.query.slice(0, 50) + "..." : args.query;
125
101
  const desc = `${args.numResults ?? 10} results • ${args.contentType ?? "highlights"}`;
126
- const text =
127
- theme.fg("toolTitle", theme.bold("exa_search ")) +
128
- theme.fg("muted", preview) +
129
- theme.fg("dim", ` ${desc}`);
130
- return new Text(text, 0, 0);
102
+ return renderToolCall("exa_search", args.query, desc, theme);
131
103
  },
132
104
 
133
105
  renderResult(result, options, theme, context) {
134
106
  const details = result.details as SearchDetails | undefined;
135
- const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
136
-
137
- let header = "";
138
- if (details) {
139
- const cost = details.cost ? ` • $${details.cost.total.toFixed(6)}` : "";
140
- header = theme.fg("success", `✓ ${details.numResults} results${cost}`);
141
- }
142
-
143
- const preview = formatToolOutputPreview(result, options, theme);
144
- text.setText(preview ? `${header}\n${preview}` : header);
145
- return text;
107
+ const cost = details ? formatCost(details.cost) : "";
108
+ const header = details ? theme.fg("success", `✓ ${details.numResults} results${cost}`) : "";
109
+ return renderToolResult(header, result, options, theme, context);
146
110
  },
147
111
  });
148
112
  }
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Shared utilities for Exa tools
3
+ */
4
+
5
+ import { mkdtemp, writeFile } from "node:fs/promises";
6
+ import { tmpdir } from "node:os";
7
+ import { join } from "node:path";
8
+ import {
9
+ DEFAULT_MAX_BYTES,
10
+ DEFAULT_MAX_LINES,
11
+ formatSize,
12
+ truncateHead,
13
+ } from "@earendil-works/pi-coding-agent";
14
+ import { Text } from "@earendil-works/pi-tui";
15
+ import type { Theme } from "@earendil-works/pi-coding-agent";
16
+
17
+ import { getApiKey } from "../api-key.ts";
18
+ import { createMissingApiKeyError } from "../errors.ts";
19
+ import { formatToolOutputPreview } from "../formatters.ts";
20
+
21
+ /** Exa Context API base URL */
22
+ export const EXA_CONTEXT_API_URL = "https://api.exa.ai/context";
23
+
24
+ /**
25
+ * Require an API key to be configured, throwing if missing.
26
+ * @returns The API key
27
+ */
28
+ export function requireApiKey(): string {
29
+ const apiKey = getApiKey();
30
+ if (!apiKey) {
31
+ throw createMissingApiKeyError();
32
+ }
33
+ return apiKey;
34
+ }
35
+
36
+ /**
37
+ * Truncate output and save to temp file if needed.
38
+ * @param output - The full output string
39
+ * @returns Object with the (possibly truncated) content string
40
+ */
41
+ export async function truncateAndSave(output: string): Promise<string> {
42
+ const truncation = truncateHead(output, {
43
+ maxLines: DEFAULT_MAX_LINES,
44
+ maxBytes: DEFAULT_MAX_BYTES,
45
+ });
46
+
47
+ let content = truncation.content;
48
+
49
+ if (truncation.truncated) {
50
+ const tempDir = await mkdtemp(join(tmpdir(), "pi-exa-"));
51
+ const tempFile = join(tempDir, "output.txt");
52
+ await writeFile(tempFile, output, "utf8");
53
+ content += `\n\n[Output truncated: ${truncation.outputLines} of ${truncation.totalLines} lines`;
54
+ content += ` (${formatSize(truncation.outputBytes)} of ${formatSize(truncation.totalBytes)}).`;
55
+ content += ` Full output saved to: ${tempFile}]`;
56
+ }
57
+
58
+ return content;
59
+ }
60
+
61
+ /**
62
+ * Format the tool call preview for TUI display.
63
+ * @param label - The tool label (e.g., "exa_search")
64
+ * @param preview - The main preview text
65
+ * @param desc - The description text
66
+ * @param theme - The theme object
67
+ */
68
+ export function renderToolCall(label: string, preview: string, desc: string, theme: Theme): Text {
69
+ const truncatedPreview = preview.length > 50 ? preview.slice(0, 50) + "..." : preview;
70
+ const text =
71
+ theme.fg("toolTitle", theme.bold(`${label} `)) +
72
+ theme.fg("muted", truncatedPreview) +
73
+ theme.fg("dim", ` ${desc}`);
74
+ return new Text(text, 0, 0);
75
+ }
76
+
77
+ /**
78
+ * Format the tool result for TUI display with optional header and preview.
79
+ * @param details - The details object with optional cost info
80
+ * @param headerText - The header text to display
81
+ * @param result - The full result object
82
+ * @param options - The display options
83
+ * @param theme - The theme object
84
+ * @param context - The context object
85
+ */
86
+ export function renderToolResult(
87
+ headerText: string,
88
+ result: { content: Array<{ type: string; text?: string }> },
89
+ options: { expanded: boolean },
90
+ theme: Theme,
91
+ context: { lastComponent?: unknown },
92
+ ): Text {
93
+ const text = (context.lastComponent as Text | undefined) ?? new Text("", 0, 0);
94
+ const preview = formatToolOutputPreview(result, options, theme);
95
+ text.setText(preview ? `${headerText}\n${preview}` : headerText);
96
+ return text;
97
+ }
98
+
99
+ /**
100
+ * Format cost for display.
101
+ * @param cost - Cost object with total property
102
+ * @returns Formatted cost string or empty string
103
+ */
104
+ export function formatCost(cost?: { total: number }): string {
105
+ return cost ? ` • $${cost.total.toFixed(6)}` : "";
106
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jarcelao/pi-exa-api",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Web search and content fetching for pi via the Exa API",
5
5
  "keywords": [
6
6
  "pi-package"
@@ -22,18 +22,18 @@
22
22
  "format:check": "oxfmt --check"
23
23
  },
24
24
  "dependencies": {
25
- "exa-js": "^2.10.1"
25
+ "exa-js": "^2.12.1"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^25.5.0",
29
- "oxfmt": "^0.41.0",
29
+ "oxfmt": "^0.48.0",
30
30
  "oxlint": "^1.56.0",
31
- "typescript": "^5.9.3",
31
+ "typescript": "^6.0.3",
32
32
  "vitest": "^4.1.0"
33
33
  },
34
34
  "peerDependencies": {
35
- "@mariozechner/pi-coding-agent": "*",
36
- "@mariozechner/pi-tui": "*",
35
+ "@earendil-works/pi-coding-agent": "*",
36
+ "@earendil-works/pi-tui": "*",
37
37
  "typebox": "*"
38
38
  },
39
39
  "pi": {
@@ -1,5 +1,6 @@
1
1
  import { describe, it, expect, afterEach } from "vitest";
2
- import { getApiKey, createMissingApiKeyError } from "../extensions/index.ts";
2
+ import { getApiKey } from "../extensions/api-key.ts";
3
+ import { createMissingApiKeyError } from "../extensions/errors.ts";
3
4
 
4
5
  describe("API Key Management", () => {
5
6
  afterEach(() => {
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from "vitest";
2
- import { mapSearchContentType, mapFetchContentType } from "../extensions/index.ts";
2
+ import { mapSearchContentType, mapFetchContentType } from "../extensions/content-types.ts";
3
3
 
4
4
  describe("ContentType Mapping (Search)", () => {
5
5
  it('should map "text" to contents.text', () => {
@@ -23,7 +23,7 @@ function createMockExtensionAPI() {
23
23
 
24
24
  function setup() {
25
25
  const api = createMockExtensionAPI();
26
- exaSearchExtension(api as unknown as import("@mariozechner/pi-coding-agent").ExtensionAPI);
26
+ exaSearchExtension(api as unknown as import("@earendil-works/pi-coding-agent").ExtensionAPI);
27
27
  return api;
28
28
  }
29
29
 
@@ -4,7 +4,7 @@ import {
4
4
  formatFetchResult,
5
5
  formatCodeContextResult,
6
6
  parseCostDollars,
7
- } from "../extensions/index.ts";
7
+ } from "../extensions/formatters.ts";
8
8
 
9
9
  describe("formatSearchResults", () => {
10
10
  it("should format basic result fields", () => {
@@ -1,19 +0,0 @@
1
- /**
2
- * Temp file utilities
3
- */
4
-
5
- import { mkdtemp, writeFile } from "node:fs/promises";
6
- import { tmpdir } from "node:os";
7
- import { join } from "node:path";
8
-
9
- /**
10
- * Write content to a temporary file.
11
- * @param content - The content to write
12
- * @returns The path to the created temporary file
13
- */
14
- export async function writeTempFile(content: string): Promise<string> {
15
- const tempDir = await mkdtemp(join(tmpdir(), "pi-exa-"));
16
- const tempFile = join(tempDir, "output.txt");
17
- await writeFile(tempFile, content, "utf8");
18
- return tempFile;
19
- }