@oh-my-pi/pi-coding-agent 12.18.3 → 12.19.2
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 +53 -0
- package/package.json +35 -27
- package/src/async/index.ts +1 -0
- package/src/async/job-manager.ts +341 -0
- package/src/cli/file-processor.ts +3 -3
- package/src/cli/list-models.ts +3 -17
- package/src/cli/stats-cli.ts +3 -22
- package/src/cli/web-search-cli.ts +8 -16
- package/src/commit/agentic/agent.ts +6 -9
- package/src/commit/agentic/index.ts +44 -50
- package/src/commit/agentic/state.ts +0 -9
- package/src/commit/agentic/tools/propose-commit.ts +1 -30
- package/src/commit/agentic/tools/schemas.ts +31 -0
- package/src/commit/agentic/tools/split-commit.ts +1 -30
- package/src/commit/agentic/validation.ts +1 -18
- package/src/commit/analysis/conventional.ts +3 -50
- package/src/commit/analysis/summary.ts +2 -13
- package/src/commit/changelog/detect.ts +4 -1
- package/src/commit/changelog/generate.ts +2 -25
- package/src/commit/changelog/index.ts +1 -2
- package/src/commit/cli.ts +4 -12
- package/src/commit/map-reduce/reduce-phase.ts +2 -43
- package/src/commit/pipeline.ts +7 -15
- package/src/commit/utils.ts +44 -0
- package/src/config/prompt-templates.ts +1 -81
- package/src/config/settings-schema.ts +20 -1
- package/src/config.ts +2 -3
- package/src/debug/index.ts +1 -6
- package/src/debug/system-info.ts +2 -6
- package/src/discovery/builtin.ts +5 -9
- package/src/discovery/helpers.ts +0 -26
- package/src/discovery/ssh.ts +1 -8
- package/src/exa/company.ts +8 -39
- package/src/exa/factory.ts +64 -0
- package/src/exa/index.ts +0 -16
- package/src/exa/linkedin.ts +8 -39
- package/src/exa/mcp-client.ts +0 -64
- package/src/exa/researcher.ts +17 -59
- package/src/exa/search.ts +30 -154
- package/src/extensibility/custom-tools/loader.ts +3 -41
- package/src/extensibility/extensions/loader.ts +2 -9
- package/src/extensibility/hooks/loader.ts +3 -20
- package/src/extensibility/hooks/runner.ts +3 -19
- package/src/extensibility/plugins/installer.ts +2 -1
- package/src/extensibility/plugins/loader.ts +29 -117
- package/src/extensibility/skills.ts +2 -89
- package/src/extensibility/slash-commands.ts +1 -63
- package/src/extensibility/utils.ts +38 -0
- package/src/index.ts +9 -25
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/jobs-protocol.ts +118 -0
- package/src/ipy/kernel.ts +2 -0
- package/src/lsp/config.ts +1 -5
- package/src/lsp/lspmux.ts +0 -17
- package/src/lsp/utils.ts +2 -24
- package/src/main.ts +16 -24
- package/src/mcp/client.ts +1 -46
- package/src/mcp/render.ts +8 -1
- package/src/mcp/tool-cache.ts +1 -5
- package/src/mcp/transports/http.ts +2 -7
- package/src/mcp/transports/stdio.ts +2 -7
- package/src/modes/components/bash-execution.ts +2 -16
- package/src/modes/components/extensions/inspector-panel.ts +8 -18
- package/src/modes/components/footer.ts +10 -50
- package/src/modes/components/model-selector.ts +2 -21
- package/src/modes/components/python-execution.ts +2 -16
- package/src/modes/components/settings-selector.ts +1 -10
- package/src/modes/components/status-line/segments.ts +8 -25
- package/src/modes/components/status-line.ts +14 -31
- package/src/modes/components/tool-execution.ts +8 -2
- package/src/modes/controllers/command-controller.ts +71 -30
- package/src/modes/controllers/event-controller.ts +34 -4
- package/src/modes/controllers/mcp-command-controller.ts +3 -34
- package/src/modes/controllers/selector-controller.ts +2 -2
- package/src/modes/controllers/ssh-command-controller.ts +3 -34
- package/src/modes/interactive-mode.ts +6 -2
- package/src/modes/rpc/rpc-client.ts +1 -5
- package/src/modes/shared.ts +73 -0
- package/src/modes/types.ts +1 -0
- package/src/modes/utils/ui-helpers.ts +26 -2
- package/src/patch/hashline.ts +6 -286
- package/src/patch/index.ts +6 -57
- package/src/patch/normalize.ts +22 -65
- package/src/patch/shared.ts +16 -16
- package/src/prompts/system/custom-system-prompt.md +0 -10
- package/src/prompts/system/system-prompt.md +69 -89
- package/src/prompts/tools/async-result.md +5 -0
- package/src/prompts/tools/bash.md +5 -0
- package/src/prompts/tools/cancel-job.md +7 -0
- package/src/prompts/tools/hashline.md +0 -16
- package/src/prompts/tools/poll-jobs.md +7 -0
- package/src/prompts/tools/task.md +4 -0
- package/src/sdk.ts +70 -6
- package/src/session/agent-session.ts +43 -6
- package/src/session/agent-storage.ts +69 -278
- package/src/session/auth-storage.ts +14 -1430
- package/src/session/session-manager.ts +69 -5
- package/src/session/session-storage.ts +1 -5
- package/src/session/streaming-output.ts +637 -76
- package/src/slash-commands/builtin-registry.ts +8 -0
- package/src/ssh/connection-manager.ts +4 -12
- package/src/ssh/sshfs-mount.ts +3 -7
- package/src/ssh/utils.ts +8 -0
- package/src/system-prompt.ts +24 -90
- package/src/task/executor.ts +11 -1
- package/src/task/index.ts +258 -13
- package/src/task/parallel.ts +32 -0
- package/src/task/render.ts +15 -7
- package/src/task/types.ts +5 -0
- package/src/tools/ask.ts +4 -7
- package/src/tools/bash-interactive.ts +4 -5
- package/src/tools/bash.ts +125 -41
- package/src/tools/cancel-job.ts +93 -0
- package/src/tools/fetch.ts +7 -27
- package/src/tools/find.ts +3 -3
- package/src/tools/gemini-image.ts +15 -14
- package/src/tools/grep.ts +3 -3
- package/src/tools/index.ts +13 -29
- package/src/tools/json-tree.ts +12 -1
- package/src/tools/jtd-to-json-schema.ts +10 -74
- package/src/tools/jtd-to-typescript.ts +10 -72
- package/src/tools/jtd-utils.ts +102 -0
- package/src/tools/notebook.ts +4 -9
- package/src/tools/output-meta.ts +52 -26
- package/src/tools/path-utils.ts +13 -7
- package/src/tools/poll-jobs.ts +178 -0
- package/src/tools/python.ts +32 -35
- package/src/tools/read.ts +61 -82
- package/src/tools/render-utils.ts +8 -159
- package/src/tools/ssh.ts +7 -20
- package/src/tools/submit-result.ts +1 -1
- package/src/tools/tool-errors.ts +0 -30
- package/src/tools/tool-result.ts +1 -2
- package/src/tools/write.ts +8 -10
- package/src/tui/code-cell.ts +8 -3
- package/src/tui/status-line.ts +4 -4
- package/src/tui/types.ts +0 -1
- package/src/tui/utils.ts +1 -14
- package/src/utils/command-args.ts +76 -0
- package/src/utils/file-mentions.ts +15 -19
- package/src/utils/frontmatter.ts +5 -10
- package/src/utils/shell-snapshot.ts +0 -11
- package/src/utils/title-generator.ts +0 -12
- package/src/web/scrapers/artifacthub.ts +7 -16
- package/src/web/scrapers/arxiv.ts +3 -8
- package/src/web/scrapers/aur.ts +8 -22
- package/src/web/scrapers/biorxiv.ts +5 -14
- package/src/web/scrapers/bluesky.ts +13 -36
- package/src/web/scrapers/brew.ts +5 -10
- package/src/web/scrapers/cheatsh.ts +2 -12
- package/src/web/scrapers/chocolatey.ts +63 -26
- package/src/web/scrapers/choosealicense.ts +3 -18
- package/src/web/scrapers/cisa-kev.ts +4 -18
- package/src/web/scrapers/clojars.ts +6 -33
- package/src/web/scrapers/coingecko.ts +25 -33
- package/src/web/scrapers/crates-io.ts +7 -26
- package/src/web/scrapers/crossref.ts +4 -18
- package/src/web/scrapers/devto.ts +11 -41
- package/src/web/scrapers/discogs.ts +7 -10
- package/src/web/scrapers/discourse.ts +6 -31
- package/src/web/scrapers/dockerhub.ts +12 -35
- package/src/web/scrapers/fdroid.ts +8 -33
- package/src/web/scrapers/firefox-addons.ts +10 -34
- package/src/web/scrapers/flathub.ts +7 -24
- package/src/web/scrapers/github-gist.ts +2 -12
- package/src/web/scrapers/github.ts +9 -47
- package/src/web/scrapers/gitlab.ts +130 -185
- package/src/web/scrapers/go-pkg.ts +12 -22
- package/src/web/scrapers/hackage.ts +88 -43
- package/src/web/scrapers/hackernews.ts +25 -45
- package/src/web/scrapers/hex.ts +19 -36
- package/src/web/scrapers/huggingface.ts +26 -91
- package/src/web/scrapers/iacr.ts +3 -8
- package/src/web/scrapers/jetbrains-marketplace.ts +9 -20
- package/src/web/scrapers/lemmy.ts +5 -23
- package/src/web/scrapers/lobsters.ts +16 -28
- package/src/web/scrapers/mastodon.ts +24 -43
- package/src/web/scrapers/maven.ts +6 -21
- package/src/web/scrapers/mdn.ts +7 -11
- package/src/web/scrapers/metacpan.ts +9 -41
- package/src/web/scrapers/musicbrainz.ts +4 -28
- package/src/web/scrapers/npm.ts +8 -25
- package/src/web/scrapers/nuget.ts +14 -37
- package/src/web/scrapers/nvd.ts +6 -28
- package/src/web/scrapers/ollama.ts +7 -34
- package/src/web/scrapers/open-vsx.ts +5 -19
- package/src/web/scrapers/opencorporates.ts +30 -14
- package/src/web/scrapers/openlibrary.ts +49 -33
- package/src/web/scrapers/orcid.ts +4 -18
- package/src/web/scrapers/osv.ts +7 -24
- package/src/web/scrapers/packagist.ts +9 -24
- package/src/web/scrapers/pub-dev.ts +7 -50
- package/src/web/scrapers/pubmed.ts +54 -21
- package/src/web/scrapers/pypi.ts +8 -26
- package/src/web/scrapers/rawg.ts +11 -19
- package/src/web/scrapers/readthedocs.ts +4 -9
- package/src/web/scrapers/reddit.ts +5 -15
- package/src/web/scrapers/repology.ts +8 -20
- package/src/web/scrapers/rfc.ts +5 -14
- package/src/web/scrapers/rubygems.ts +6 -21
- package/src/web/scrapers/searchcode.ts +8 -36
- package/src/web/scrapers/sec-edgar.ts +4 -18
- package/src/web/scrapers/semantic-scholar.ts +15 -35
- package/src/web/scrapers/snapcraft.ts +5 -19
- package/src/web/scrapers/sourcegraph.ts +5 -43
- package/src/web/scrapers/spdx.ts +4 -18
- package/src/web/scrapers/spotify.ts +4 -23
- package/src/web/scrapers/stackoverflow.ts +8 -13
- package/src/web/scrapers/terraform.ts +9 -37
- package/src/web/scrapers/tldr.ts +3 -7
- package/src/web/scrapers/twitter.ts +3 -7
- package/src/web/scrapers/types.ts +105 -27
- package/src/web/scrapers/utils.ts +97 -103
- package/src/web/scrapers/vimeo.ts +7 -27
- package/src/web/scrapers/vscode-marketplace.ts +8 -17
- package/src/web/scrapers/w3c.ts +6 -14
- package/src/web/scrapers/wikidata.ts +5 -19
- package/src/web/scrapers/wikipedia.ts +2 -12
- package/src/web/scrapers/youtube.ts +5 -34
- package/src/web/search/index.ts +0 -9
- package/src/web/search/providers/anthropic.ts +3 -2
- package/src/web/search/providers/brave.ts +3 -18
- package/src/web/search/providers/exa.ts +1 -12
- package/src/web/search/providers/kimi.ts +5 -44
- package/src/web/search/providers/perplexity.ts +1 -12
- package/src/web/search/providers/synthetic.ts +3 -26
- package/src/web/search/providers/utils.ts +36 -0
- package/src/web/search/providers/zai.ts +9 -50
- package/src/web/search/types.ts +0 -28
- package/src/web/search/utils.ts +17 -0
- package/src/tools/output-utils.ts +0 -63
- package/src/tools/truncate.ts +0 -385
- package/src/web/search/auth.ts +0 -178
package/src/exa/linkedin.ts
CHANGED
|
@@ -5,53 +5,22 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
8
|
-
import {
|
|
8
|
+
import { createExaTool } from "./factory";
|
|
9
9
|
import type { ExaRenderDetails } from "./types";
|
|
10
10
|
|
|
11
11
|
/** exa_linkedin - LinkedIn search */
|
|
12
|
-
export const linkedinTool: CustomTool<any, ExaRenderDetails> =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
12
|
+
export const linkedinTool: CustomTool<any, ExaRenderDetails> = createExaTool(
|
|
13
|
+
"exa_linkedin",
|
|
14
|
+
"Exa LinkedIn",
|
|
15
|
+
`Search LinkedIn for people, companies, and professional content using Exa.
|
|
16
16
|
|
|
17
17
|
Returns LinkedIn search results with profiles, posts, and company information.
|
|
18
18
|
|
|
19
19
|
Parameters:
|
|
20
20
|
- query: LinkedIn search query (e.g., "Software Engineer at OpenAI", "Y Combinator companies")`,
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
Type.Object({
|
|
23
23
|
query: Type.String({ description: "LinkedIn search query" }),
|
|
24
24
|
}),
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
try {
|
|
28
|
-
const apiKey = await findApiKey();
|
|
29
|
-
if (!apiKey) {
|
|
30
|
-
return {
|
|
31
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
32
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_linkedin" },
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
const response = await callExaTool("linkedin_search", params, apiKey);
|
|
36
|
-
|
|
37
|
-
if (isSearchResponse(response)) {
|
|
38
|
-
const formatted = formatSearchResults(response);
|
|
39
|
-
return {
|
|
40
|
-
content: [{ type: "text" as const, text: formatted }],
|
|
41
|
-
details: { response, toolName: "exa_linkedin" },
|
|
42
|
-
};
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
return {
|
|
46
|
-
content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
|
|
47
|
-
details: { raw: response, toolName: "exa_linkedin" },
|
|
48
|
-
};
|
|
49
|
-
} catch (error) {
|
|
50
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
51
|
-
return {
|
|
52
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
53
|
-
details: { error: message, toolName: "exa_linkedin" },
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
},
|
|
57
|
-
};
|
|
25
|
+
"linkedin_search",
|
|
26
|
+
);
|
package/src/exa/mcp-client.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { callMCP } from "../mcp/json-rpc";
|
|
|
5
5
|
import type {
|
|
6
6
|
ExaRenderDetails,
|
|
7
7
|
ExaSearchResponse,
|
|
8
|
-
ExaSearchResult,
|
|
9
8
|
MCPCallResponse,
|
|
10
9
|
MCPTool,
|
|
11
10
|
MCPToolsResponse,
|
|
@@ -79,69 +78,6 @@ export async function callWebsetsTool(
|
|
|
79
78
|
return response.result;
|
|
80
79
|
}
|
|
81
80
|
|
|
82
|
-
/** Parse Exa markdown format into SearchResponse */
|
|
83
|
-
export function parseExaMarkdown(text: string): ExaSearchResponse | null {
|
|
84
|
-
const results: ExaSearchResult[] = [];
|
|
85
|
-
const lines = text.split("\n");
|
|
86
|
-
let currentResult: Partial<ExaSearchResult> | null = null;
|
|
87
|
-
|
|
88
|
-
for (let i = 0; i < lines.length; i++) {
|
|
89
|
-
const line = lines[i].trim();
|
|
90
|
-
|
|
91
|
-
// Match result header: ## Title
|
|
92
|
-
if (line.startsWith("## ")) {
|
|
93
|
-
if (currentResult?.title) {
|
|
94
|
-
results.push(currentResult as ExaSearchResult);
|
|
95
|
-
}
|
|
96
|
-
currentResult = { title: line.slice(3).trim() };
|
|
97
|
-
continue;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
if (!currentResult) continue;
|
|
101
|
-
|
|
102
|
-
// Match URL: **URL:** ...
|
|
103
|
-
if (line.startsWith("**URL:**")) {
|
|
104
|
-
currentResult.url = line.slice(8).trim();
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
// Match Author: **Author:** ...
|
|
109
|
-
if (line.startsWith("**Author:**")) {
|
|
110
|
-
currentResult.author = line.slice(11).trim();
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Match Published Date: **Published Date:** ...
|
|
115
|
-
if (line.startsWith("**Published Date:**")) {
|
|
116
|
-
currentResult.publishedDate = line.slice(19).trim();
|
|
117
|
-
continue;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
// Match Text: **Text:** ...
|
|
121
|
-
if (line.startsWith("**Text:**")) {
|
|
122
|
-
currentResult.text = line.slice(9).trim();
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Accumulate text content
|
|
127
|
-
if (currentResult.text && line && !line.startsWith("**")) {
|
|
128
|
-
currentResult.text += ` ${line}`;
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// Add last result
|
|
133
|
-
if (currentResult?.title) {
|
|
134
|
-
results.push(currentResult as ExaSearchResult);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
if (results.length === 0) return null;
|
|
138
|
-
|
|
139
|
-
return {
|
|
140
|
-
results,
|
|
141
|
-
statuses: results.map((r, i) => ({ id: r.id ?? `result-${i}`, status: "success" })),
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
|
|
145
81
|
/** Format search results for LLM */
|
|
146
82
|
export function formatSearchResults(data: ExaSearchResponse): string {
|
|
147
83
|
const results = data.results ?? [];
|
package/src/exa/researcher.ts
CHANGED
|
@@ -5,15 +5,14 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
8
|
-
import {
|
|
8
|
+
import { createExaTool } from "./factory";
|
|
9
9
|
import type { ExaRenderDetails } from "./types";
|
|
10
10
|
|
|
11
|
-
const researcherStartTool
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
parameters: Type.Object({
|
|
11
|
+
const researcherStartTool = createExaTool(
|
|
12
|
+
"exa_researcher_start",
|
|
13
|
+
"Start Deep Research",
|
|
14
|
+
"Start an asynchronous deep research task using Exa's researcher. Returns a task_id for polling completion.",
|
|
15
|
+
Type.Object({
|
|
17
16
|
query: Type.String({ description: "Research query to investigate" }),
|
|
18
17
|
depth: Type.Optional(
|
|
19
18
|
Type.Number({
|
|
@@ -30,60 +29,19 @@ const researcherStartTool: CustomTool<any, ExaRenderDetails> = {
|
|
|
30
29
|
}),
|
|
31
30
|
),
|
|
32
31
|
}),
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
if (!apiKey) {
|
|
37
|
-
return {
|
|
38
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
39
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_researcher_start" },
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
const result = await callExaTool("deep_researcher_start", params as Record<string, unknown>, apiKey);
|
|
43
|
-
return {
|
|
44
|
-
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
45
|
-
details: { raw: result, toolName: "exa_researcher_start" },
|
|
46
|
-
};
|
|
47
|
-
} catch (error) {
|
|
48
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
49
|
-
return {
|
|
50
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
51
|
-
details: { error: message, toolName: "exa_researcher_start" },
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
},
|
|
55
|
-
};
|
|
32
|
+
"deep_researcher_start",
|
|
33
|
+
{ formatResponse: false },
|
|
34
|
+
);
|
|
56
35
|
|
|
57
|
-
const researcherPollTool
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
parameters: Type.Object({
|
|
36
|
+
const researcherPollTool = createExaTool(
|
|
37
|
+
"exa_researcher_poll",
|
|
38
|
+
"Poll Research Status",
|
|
39
|
+
"Poll the status of an asynchronous research task. Returns status (pending|running|completed|failed) and result if completed.",
|
|
40
|
+
Type.Object({
|
|
63
41
|
task_id: Type.String({ description: "Task ID returned from exa_researcher_start" }),
|
|
64
42
|
}),
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
if (!apiKey) {
|
|
69
|
-
return {
|
|
70
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
71
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_researcher_poll" },
|
|
72
|
-
};
|
|
73
|
-
}
|
|
74
|
-
const result = await callExaTool("deep_researcher_check", params as Record<string, unknown>, apiKey);
|
|
75
|
-
return {
|
|
76
|
-
content: [{ type: "text" as const, text: JSON.stringify(result, null, 2) }],
|
|
77
|
-
details: { raw: result, toolName: "exa_researcher_poll" },
|
|
78
|
-
};
|
|
79
|
-
} catch (error) {
|
|
80
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
81
|
-
return {
|
|
82
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
83
|
-
details: { error: message, toolName: "exa_researcher_poll" },
|
|
84
|
-
};
|
|
85
|
-
}
|
|
86
|
-
},
|
|
87
|
-
};
|
|
43
|
+
"deep_researcher_check",
|
|
44
|
+
{ formatResponse: false },
|
|
45
|
+
);
|
|
88
46
|
|
|
89
47
|
export const researcherTools: CustomTool<any, ExaRenderDetails>[] = [researcherStartTool, researcherPollTool];
|
package/src/exa/search.ts
CHANGED
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
import { StringEnum } from "@oh-my-pi/pi-ai";
|
|
7
7
|
import { Type } from "@sinclair/typebox";
|
|
8
8
|
import type { CustomTool } from "../extensibility/custom-tools/types";
|
|
9
|
-
import {
|
|
9
|
+
import { createExaTool } from "./factory";
|
|
10
10
|
import type { ExaRenderDetails } from "./types";
|
|
11
11
|
|
|
12
12
|
/** exa_search - Basic neural/keyword search */
|
|
13
|
-
const exaSearchTool
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
const exaSearchTool = createExaTool(
|
|
14
|
+
"exa_search",
|
|
15
|
+
"Exa Search",
|
|
16
|
+
`Search the web using Exa's neural or keyword search.
|
|
17
17
|
|
|
18
18
|
Returns structured search results with optional text content and highlights.
|
|
19
19
|
|
|
@@ -29,7 +29,7 @@ Parameters:
|
|
|
29
29
|
- highlights: Include highlighted relevant snippets (default: false)
|
|
30
30
|
- num_results: Maximum number of results to return (default: 10, max: 100)`,
|
|
31
31
|
|
|
32
|
-
|
|
32
|
+
Type.Object({
|
|
33
33
|
query: Type.String({ description: "Search query" }),
|
|
34
34
|
type: Type.Optional(
|
|
35
35
|
StringEnum(["keyword", "neural", "auto"], {
|
|
@@ -79,51 +79,20 @@ Parameters:
|
|
|
79
79
|
}),
|
|
80
80
|
),
|
|
81
81
|
}),
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
try {
|
|
85
|
-
const apiKey = await findApiKey();
|
|
86
|
-
if (!apiKey) {
|
|
87
|
-
return {
|
|
88
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
89
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_search" },
|
|
90
|
-
};
|
|
91
|
-
}
|
|
92
|
-
const response = await callExaTool("web_search_exa", params, apiKey);
|
|
93
|
-
|
|
94
|
-
if (isSearchResponse(response)) {
|
|
95
|
-
const formatted = formatSearchResults(response);
|
|
96
|
-
return {
|
|
97
|
-
content: [{ type: "text" as const, text: formatted }],
|
|
98
|
-
details: { response, toolName: "exa_search" },
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return {
|
|
103
|
-
content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
|
|
104
|
-
details: { raw: response, toolName: "exa_search" },
|
|
105
|
-
};
|
|
106
|
-
} catch (error) {
|
|
107
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
108
|
-
return {
|
|
109
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
110
|
-
details: { error: message, toolName: "exa_search" },
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
};
|
|
82
|
+
"web_search_exa",
|
|
83
|
+
);
|
|
115
84
|
|
|
116
85
|
/** exa_search_deep - AI-synthesized deep research */
|
|
117
|
-
const exaSearchDeepTool
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
86
|
+
const exaSearchDeepTool = createExaTool(
|
|
87
|
+
"exa_search_deep",
|
|
88
|
+
"Exa Deep Search",
|
|
89
|
+
`Perform AI-synthesized deep research using Exa.
|
|
121
90
|
|
|
122
91
|
Returns comprehensive research with synthesized answers and multiple sources.
|
|
123
92
|
|
|
124
93
|
Similar parameters to exa_search, optimized for research depth.`,
|
|
125
94
|
|
|
126
|
-
|
|
95
|
+
Type.Object({
|
|
127
96
|
query: Type.String({ description: "Research query" }),
|
|
128
97
|
type: Type.Optional(
|
|
129
98
|
StringEnum(["keyword", "neural", "auto"], {
|
|
@@ -173,46 +142,15 @@ Similar parameters to exa_search, optimized for research depth.`,
|
|
|
173
142
|
}),
|
|
174
143
|
),
|
|
175
144
|
}),
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
const apiKey = await findApiKey();
|
|
180
|
-
if (!apiKey) {
|
|
181
|
-
return {
|
|
182
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
183
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_search_deep" },
|
|
184
|
-
};
|
|
185
|
-
}
|
|
186
|
-
const args = { ...params, type: "deep" };
|
|
187
|
-
const response = await callExaTool("web_search_exa", args, apiKey);
|
|
188
|
-
|
|
189
|
-
if (isSearchResponse(response)) {
|
|
190
|
-
const formatted = formatSearchResults(response);
|
|
191
|
-
return {
|
|
192
|
-
content: [{ type: "text" as const, text: formatted }],
|
|
193
|
-
details: { response, toolName: "exa_search_deep" },
|
|
194
|
-
};
|
|
195
|
-
}
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
|
|
199
|
-
details: { raw: response, toolName: "exa_search_deep" },
|
|
200
|
-
};
|
|
201
|
-
} catch (error) {
|
|
202
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
203
|
-
return {
|
|
204
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
205
|
-
details: { error: message, toolName: "exa_search_deep" },
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
};
|
|
145
|
+
"web_search_exa",
|
|
146
|
+
{ transformParams: params => ({ ...params, type: "deep" }) },
|
|
147
|
+
);
|
|
210
148
|
|
|
211
149
|
/** exa_search_code - Code-focused search */
|
|
212
|
-
const exaSearchCodeTool
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
150
|
+
const exaSearchCodeTool = createExaTool(
|
|
151
|
+
"exa_search_code",
|
|
152
|
+
"Exa Code Search",
|
|
153
|
+
`Search for code examples and technical documentation using Exa.
|
|
216
154
|
|
|
217
155
|
Optimized for finding code snippets, API documentation, and technical content.
|
|
218
156
|
|
|
@@ -220,7 +158,7 @@ Parameters:
|
|
|
220
158
|
- query: Code or technical search query (required)
|
|
221
159
|
- code_context: Additional context about what you're looking for`,
|
|
222
160
|
|
|
223
|
-
|
|
161
|
+
Type.Object({
|
|
224
162
|
query: Type.String({ description: "Code or technical search query" }),
|
|
225
163
|
code_context: Type.Optional(
|
|
226
164
|
Type.String({
|
|
@@ -228,45 +166,14 @@ Parameters:
|
|
|
228
166
|
}),
|
|
229
167
|
),
|
|
230
168
|
}),
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
try {
|
|
234
|
-
const apiKey = await findApiKey();
|
|
235
|
-
if (!apiKey) {
|
|
236
|
-
return {
|
|
237
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
238
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_search_code" },
|
|
239
|
-
};
|
|
240
|
-
}
|
|
241
|
-
const response = await callExaTool("get_code_context_exa", params, apiKey);
|
|
242
|
-
|
|
243
|
-
if (isSearchResponse(response)) {
|
|
244
|
-
const formatted = formatSearchResults(response);
|
|
245
|
-
return {
|
|
246
|
-
content: [{ type: "text" as const, text: formatted }],
|
|
247
|
-
details: { response, toolName: "exa_search_code" },
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
return {
|
|
252
|
-
content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
|
|
253
|
-
details: { raw: response, toolName: "exa_search_code" },
|
|
254
|
-
};
|
|
255
|
-
} catch (error) {
|
|
256
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
257
|
-
return {
|
|
258
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
259
|
-
details: { error: message, toolName: "exa_search_code" },
|
|
260
|
-
};
|
|
261
|
-
}
|
|
262
|
-
},
|
|
263
|
-
};
|
|
169
|
+
"get_code_context_exa",
|
|
170
|
+
);
|
|
264
171
|
|
|
265
172
|
/** exa_crawl - URL content extraction */
|
|
266
|
-
const exaCrawlTool
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
173
|
+
const exaCrawlTool = createExaTool(
|
|
174
|
+
"exa_crawl",
|
|
175
|
+
"Exa Crawl",
|
|
176
|
+
`Extract content from a specific URL using Exa.
|
|
270
177
|
|
|
271
178
|
Returns the page content with optional text and highlights.
|
|
272
179
|
|
|
@@ -275,7 +182,7 @@ Parameters:
|
|
|
275
182
|
- text: Include full page text content (default: false)
|
|
276
183
|
- highlights: Include highlighted relevant snippets (default: false)`,
|
|
277
184
|
|
|
278
|
-
|
|
185
|
+
Type.Object({
|
|
279
186
|
url: Type.String({ description: "URL to crawl and extract content from" }),
|
|
280
187
|
text: Type.Optional(
|
|
281
188
|
Type.Boolean({
|
|
@@ -288,39 +195,8 @@ Parameters:
|
|
|
288
195
|
}),
|
|
289
196
|
),
|
|
290
197
|
}),
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
try {
|
|
294
|
-
const apiKey = await findApiKey();
|
|
295
|
-
if (!apiKey) {
|
|
296
|
-
return {
|
|
297
|
-
content: [{ type: "text" as const, text: "Error: EXA_API_KEY not found" }],
|
|
298
|
-
details: { error: "EXA_API_KEY not found", toolName: "exa_crawl" },
|
|
299
|
-
};
|
|
300
|
-
}
|
|
301
|
-
const response = await callExaTool("crawling", params, apiKey);
|
|
302
|
-
|
|
303
|
-
if (isSearchResponse(response)) {
|
|
304
|
-
const formatted = formatSearchResults(response);
|
|
305
|
-
return {
|
|
306
|
-
content: [{ type: "text" as const, text: formatted }],
|
|
307
|
-
details: { response, toolName: "exa_crawl" },
|
|
308
|
-
};
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
return {
|
|
312
|
-
content: [{ type: "text" as const, text: JSON.stringify(response, null, 2) }],
|
|
313
|
-
details: { raw: response, toolName: "exa_crawl" },
|
|
314
|
-
};
|
|
315
|
-
} catch (error) {
|
|
316
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
317
|
-
return {
|
|
318
|
-
content: [{ type: "text" as const, text: `Error: ${message}` }],
|
|
319
|
-
details: { error: message, toolName: "exa_crawl" },
|
|
320
|
-
};
|
|
321
|
-
}
|
|
322
|
-
},
|
|
323
|
-
};
|
|
198
|
+
"crawling",
|
|
199
|
+
);
|
|
324
200
|
|
|
325
201
|
export const searchTools: CustomTool<any, ExaRenderDetails>[] = [
|
|
326
202
|
exaSearchTool,
|
|
@@ -10,51 +10,13 @@ import { logger } from "@oh-my-pi/pi-utils";
|
|
|
10
10
|
import * as typebox from "@sinclair/typebox";
|
|
11
11
|
import { toolCapability } from "../../capability/tool";
|
|
12
12
|
import { type CustomTool, loadCapability } from "../../discovery";
|
|
13
|
-
import { expandPath } from "../../discovery/helpers";
|
|
14
13
|
import type { ExecOptions } from "../../exec/exec";
|
|
15
14
|
import { execCommand } from "../../exec/exec";
|
|
16
15
|
import type { HookUIContext } from "../../extensibility/hooks/types";
|
|
17
16
|
import { getAllPluginToolPaths } from "../../extensibility/plugins/loader";
|
|
18
|
-
import {
|
|
17
|
+
import { createNoOpUIContext, resolvePath } from "../utils";
|
|
19
18
|
import type { CustomToolAPI, CustomToolFactory, LoadedCustomTool, ToolLoadError } from "./types";
|
|
20
19
|
|
|
21
|
-
/**
|
|
22
|
-
* Resolve tool path.
|
|
23
|
-
* - Absolute paths used as-is
|
|
24
|
-
* - Paths starting with ~ expanded to home directory
|
|
25
|
-
* - Relative paths resolved from cwd
|
|
26
|
-
*/
|
|
27
|
-
function resolveToolPath(toolPath: string, cwd: string): string {
|
|
28
|
-
const expanded = expandPath(toolPath);
|
|
29
|
-
|
|
30
|
-
if (path.isAbsolute(expanded)) {
|
|
31
|
-
return expanded;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
// Relative paths resolved from cwd
|
|
35
|
-
return path.resolve(cwd, expanded);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Create a no-op UI context for headless modes.
|
|
40
|
-
*/
|
|
41
|
-
function createNoOpUIContext(): HookUIContext {
|
|
42
|
-
return {
|
|
43
|
-
select: async () => undefined,
|
|
44
|
-
confirm: async () => false,
|
|
45
|
-
input: async () => undefined,
|
|
46
|
-
notify: () => {},
|
|
47
|
-
setStatus: () => {},
|
|
48
|
-
custom: async () => undefined as never,
|
|
49
|
-
setEditorText: () => {},
|
|
50
|
-
getEditorText: () => "",
|
|
51
|
-
editor: async () => undefined,
|
|
52
|
-
get theme() {
|
|
53
|
-
return theme;
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
20
|
/**
|
|
59
21
|
* Load a single tool module using native Bun import.
|
|
60
22
|
*/
|
|
@@ -64,7 +26,7 @@ async function loadTool(
|
|
|
64
26
|
sharedApi: CustomToolAPI,
|
|
65
27
|
source?: { provider: string; providerName: string; level: "user" | "project" },
|
|
66
28
|
): Promise<{ tools: LoadedCustomTool[] | null; error: ToolLoadError | null }> {
|
|
67
|
-
const resolvedPath =
|
|
29
|
+
const resolvedPath = resolvePath(toolPath, cwd);
|
|
68
30
|
|
|
69
31
|
// Skip declarative tool files (.md, .json) - these are metadata only, not executable modules
|
|
70
32
|
if (resolvedPath.endsWith(".md") || resolvedPath.endsWith(".json")) {
|
|
@@ -228,7 +190,7 @@ export async function discoverAndLoadCustomTools(configuredPaths: string[], cwd:
|
|
|
228
190
|
|
|
229
191
|
// 3. Explicitly configured paths (can override/add)
|
|
230
192
|
for (const configPath of configuredPaths) {
|
|
231
|
-
addPath(
|
|
193
|
+
addPath(resolvePath(configPath, cwd), { provider: "config", providerName: "Config", level: "project" });
|
|
232
194
|
}
|
|
233
195
|
|
|
234
196
|
return loadCustomTools(allPathsWithSources, cwd, builtInToolNames);
|
|
@@ -13,11 +13,12 @@ import type { TSchema } from "@sinclair/typebox";
|
|
|
13
13
|
import * as TypeBox from "@sinclair/typebox";
|
|
14
14
|
import { type ExtensionModule, extensionModuleCapability } from "../../capability/extension-module";
|
|
15
15
|
import { loadCapability } from "../../discovery";
|
|
16
|
-
import {
|
|
16
|
+
import { getExtensionNameFromPath } from "../../discovery/helpers";
|
|
17
17
|
import type { ExecOptions } from "../../exec/exec";
|
|
18
18
|
import { execCommand } from "../../exec/exec";
|
|
19
19
|
import type { CustomMessage } from "../../session/messages";
|
|
20
20
|
import { EventBus } from "../../utils/event-bus";
|
|
21
|
+
import { resolvePath } from "../utils";
|
|
21
22
|
import type {
|
|
22
23
|
Extension,
|
|
23
24
|
ExtensionAPI,
|
|
@@ -30,14 +31,6 @@ import type {
|
|
|
30
31
|
ToolDefinition,
|
|
31
32
|
} from "./types";
|
|
32
33
|
|
|
33
|
-
function resolvePath(extPath: string, cwd: string): string {
|
|
34
|
-
const expanded = expandPath(extPath);
|
|
35
|
-
if (path.isAbsolute(expanded)) {
|
|
36
|
-
return expanded;
|
|
37
|
-
}
|
|
38
|
-
return path.resolve(cwd, expanded);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
34
|
type HandlerFn = (...args: unknown[]) => Promise<unknown>;
|
|
42
35
|
|
|
43
36
|
export class ExtensionRuntimeNotInitializedError extends Error {
|
|
@@ -8,9 +8,9 @@ import * as typebox from "@sinclair/typebox";
|
|
|
8
8
|
import { hookCapability } from "../../capability/hook";
|
|
9
9
|
import type { Hook } from "../../discovery";
|
|
10
10
|
import { loadCapability } from "../../discovery";
|
|
11
|
-
import { expandPath } from "../../discovery/helpers";
|
|
12
11
|
import type { HookMessage } from "../../session/messages";
|
|
13
12
|
import type { SessionManager } from "../../session/session-manager";
|
|
13
|
+
import { resolvePath } from "../utils";
|
|
14
14
|
import { execCommand } from "./runner";
|
|
15
15
|
import type { ExecOptions, HookAPI, HookFactory, HookMessageRenderer, RegisteredCommand } from "./types";
|
|
16
16
|
|
|
@@ -83,23 +83,6 @@ export interface LoadHooksResult {
|
|
|
83
83
|
errors: Array<{ path: string; error: string }>;
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
/**
|
|
87
|
-
* Resolve hook path.
|
|
88
|
-
* - Absolute paths used as-is
|
|
89
|
-
* - Paths starting with ~ expanded to home directory
|
|
90
|
-
* - Relative paths resolved from cwd
|
|
91
|
-
*/
|
|
92
|
-
function resolveHookPath(hookPath: string, cwd: string): string {
|
|
93
|
-
const expanded = expandPath(hookPath);
|
|
94
|
-
|
|
95
|
-
if (path.isAbsolute(expanded)) {
|
|
96
|
-
return expanded;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Relative paths resolved from cwd
|
|
100
|
-
return path.resolve(cwd, expanded);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
86
|
/**
|
|
104
87
|
* Create a HookAPI instance that collects handlers, renderers, and commands.
|
|
105
88
|
* Returns the API, maps, and functions to set handlers later.
|
|
@@ -174,7 +157,7 @@ function createHookAPI(
|
|
|
174
157
|
* Load a single hook module using native Bun import.
|
|
175
158
|
*/
|
|
176
159
|
async function loadHook(hookPath: string, cwd: string): Promise<{ hook: LoadedHook | null; error: string | null }> {
|
|
177
|
-
const resolvedPath =
|
|
160
|
+
const resolvedPath = resolvePath(hookPath, cwd);
|
|
178
161
|
|
|
179
162
|
try {
|
|
180
163
|
// Import the module using native Bun import
|
|
@@ -267,7 +250,7 @@ export async function discoverAndLoadHooks(configuredPaths: string[], cwd: strin
|
|
|
267
250
|
addPaths(discovered.items.map(hook => hook.path));
|
|
268
251
|
|
|
269
252
|
// 2. Explicitly configured paths (can override/add)
|
|
270
|
-
addPaths(configuredPaths.map(p =>
|
|
253
|
+
addPaths(configuredPaths.map(p => resolvePath(p, cwd)));
|
|
271
254
|
|
|
272
255
|
return loadHooks(allPaths, cwd);
|
|
273
256
|
}
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
import type { AgentMessage } from "@oh-my-pi/pi-agent-core";
|
|
5
5
|
import type { Model } from "@oh-my-pi/pi-ai";
|
|
6
6
|
import type { ModelRegistry } from "../../config/model-registry";
|
|
7
|
-
import { theme } from "../../modes/theme/theme";
|
|
8
7
|
import type { SessionManager } from "../../session/session-manager";
|
|
8
|
+
import { createNoOpUIContext } from "../utils";
|
|
9
9
|
import type {
|
|
10
10
|
AppendEntryHandler,
|
|
11
11
|
BranchHandler,
|
|
@@ -42,22 +42,6 @@ export type HookErrorListener = (error: HookError) => void;
|
|
|
42
42
|
// Re-export execCommand for backward compatibility
|
|
43
43
|
export { execCommand } from "../../exec/exec";
|
|
44
44
|
|
|
45
|
-
/** No-op UI context used when no UI is available */
|
|
46
|
-
const noOpUIContext: HookUIContext = {
|
|
47
|
-
select: async () => undefined,
|
|
48
|
-
confirm: async () => false,
|
|
49
|
-
input: async () => undefined,
|
|
50
|
-
notify: () => {},
|
|
51
|
-
setStatus: () => {},
|
|
52
|
-
custom: async () => undefined as never,
|
|
53
|
-
setEditorText: () => {},
|
|
54
|
-
getEditorText: () => "",
|
|
55
|
-
editor: async () => undefined,
|
|
56
|
-
get theme() {
|
|
57
|
-
return theme;
|
|
58
|
-
},
|
|
59
|
-
};
|
|
60
|
-
|
|
61
45
|
/**
|
|
62
46
|
* HookRunner executes hooks and manages event emission.
|
|
63
47
|
*/
|
|
@@ -80,7 +64,7 @@ export class HookRunner {
|
|
|
80
64
|
private readonly sessionManager: SessionManager,
|
|
81
65
|
private readonly modelRegistry: ModelRegistry,
|
|
82
66
|
) {
|
|
83
|
-
this.#uiContext =
|
|
67
|
+
this.#uiContext = createNoOpUIContext();
|
|
84
68
|
this.#hasUI = false;
|
|
85
69
|
}
|
|
86
70
|
|
|
@@ -134,7 +118,7 @@ export class HookRunner {
|
|
|
134
118
|
hook.setSendMessageHandler(options.sendMessageHandler);
|
|
135
119
|
hook.setAppendEntryHandler(options.appendEntryHandler);
|
|
136
120
|
}
|
|
137
|
-
this.#uiContext = options.uiContext ??
|
|
121
|
+
this.#uiContext = options.uiContext ?? createNoOpUIContext();
|
|
138
122
|
this.#hasUI = options.hasUI ?? false;
|
|
139
123
|
}
|
|
140
124
|
|