chore: initial commit
pi extension exposing a single search tool. Providers: Kagi (headless Firefox against kagi.com session-token endpoint) and SearXNG (JSON API). Config lives at ~/.pi/pi-search/config.json with env overrides.
This commit is contained in:
57
index.ts
Normal file
57
index.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
// Pi-Search Extension - Registers a single `search` tool. Provider (kagi or
|
||||
// searxng) is chosen via ~/.pi/pi-search/config.json or PI_SEARCH_PROVIDER.
|
||||
import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
|
||||
import { Type } from "typebox";
|
||||
import { ConfigError, loadConfig, resolveSettings } from "./src/config.ts";
|
||||
import { searchKagi } from "./src/providers/kagi.ts";
|
||||
import { searchSearxng } from "./src/providers/searxng.ts";
|
||||
import { SearchError, type SearchResult } from "./src/types.ts";
|
||||
|
||||
function formatResults(results: SearchResult[]): string {
|
||||
if (results.length === 0) return "(no results)";
|
||||
return results
|
||||
.map((r) => `## [${r.title}](${r.url})\n> ${r.description}`)
|
||||
.join("\n\n");
|
||||
}
|
||||
|
||||
async function runSearch(query: string): Promise<SearchResult[]> {
|
||||
const config = loadConfig();
|
||||
const settings = resolveSettings(config);
|
||||
switch (settings.provider) {
|
||||
case "kagi":
|
||||
return searchKagi({ query, token: settings.kagiToken ?? "" });
|
||||
case "searxng":
|
||||
return searchSearxng({ query, baseUrl: settings.searxngBaseUrl ?? "" });
|
||||
}
|
||||
}
|
||||
|
||||
export default function (pi: ExtensionAPI) {
|
||||
pi.registerTool({
|
||||
name: "search",
|
||||
label: "Web Search",
|
||||
description:
|
||||
"Search the web. Returns a markdown list of titles, URLs, and snippets.",
|
||||
promptSnippet: "Search the web for the given query",
|
||||
parameters: Type.Object({
|
||||
query: Type.String({ description: "Search query text" }),
|
||||
}),
|
||||
async execute(_toolCallId, params) {
|
||||
try {
|
||||
const results = await runSearch(params.query);
|
||||
return {
|
||||
content: [{ type: "text", text: formatResults(results) }],
|
||||
details: { raw: results },
|
||||
};
|
||||
} catch (err) {
|
||||
if (err instanceof ConfigError || err instanceof SearchError) {
|
||||
return {
|
||||
content: [{ type: "text", text: `Search error: ${err.message}` }],
|
||||
isError: true,
|
||||
details: { raw: [] as SearchResult[] },
|
||||
};
|
||||
}
|
||||
throw err;
|
||||
}
|
||||
},
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user