pi-all-search 1.0.2 → 1.0.4

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
@@ -1,6 +1,6 @@
1
1
  # pi-all-search
2
2
 
3
- **All-in-one web search extension for Pi — exa, tavily, anysearch, firecrawl, brave.**
3
+ **All-in-one web search extension for Pi — exa, tavily, anysearch, firecrawl, context7.**
4
4
 
5
5
  ## Install
6
6
 
@@ -18,11 +18,11 @@ Set API keys in environment or `~/.pi/web-search.json`:
18
18
  "tavilyApiKey": "tvly-...",
19
19
  "anysearchApiKey": "as_sk_...",
20
20
  "firecrawlApiKey": "fc-...",
21
- "braveApiKey": "BSA_..."
21
+ "context7ApiKey": "ctx7sk_..."
22
22
  }
23
23
  ```
24
24
 
25
- Or set environment variables: `EXA_API_KEY`, `TAVILY_API_KEY`, `ANYSEARCH_API_KEY`, `FIRECRAWL_API_KEY`, `BRAVE_API_KEY`.
25
+ Or set environment variables: `EXA_API_KEY`, `TAVILY_API_KEY`, `ANYSEARCH_API_KEY`, `FIRECRAWL_API_KEY`, `CONTEXT7_API_KEY`.
26
26
 
27
27
  ## Usage
28
28
 
@@ -31,6 +31,7 @@ web_search({ query: "TypeScript best practices" })
31
31
  web_search({ queries: ["React vs Vue", "Angular vs Svelte"] })
32
32
  web_search({ query: "AAPL stock price", provider: "anysearch" })
33
33
  web_search({ query: "rust async programming", provider: "tavily" })
34
+ web_search({ query: "Next.js caching", provider: "context7" })
34
35
  ```
35
36
 
36
37
  ## Providers
@@ -41,14 +42,15 @@ web_search({ query: "rust async programming", provider: "tavily" })
41
42
  | **tavily** | General web, programming, fast results | `TAVILY_API_KEY` |
42
43
  | **anysearch** | Finance, stocks, structured data | `ANYSEARCH_API_KEY` |
43
44
  | **firecrawl** | Scraping-heavy sites, fallback | `FIRECRAWL_API_KEY` |
44
- | **brave** | General web, good coverage | `BRAVE_API_KEY` |
45
+ | **context7** | Library/framework/API documentation | `CONTEXT7_API_KEY` |
45
46
 
46
47
  ## Routing
47
48
 
48
49
  Automatic intent-based routing:
49
- - **Finance queries** → anysearch → braveexa
50
- - **Academic queries** → exa → anysearch → brave
51
- - **General queries** → tavily → braveexaanysearch
50
+ - **Finance queries** → anysearch → exatavily
51
+ - **Academic queries** → exa → anysearch → tavily
52
+ - **General queries** → tavily → exaanysearchfirecrawl
53
+ - **Docs queries** → context7 → exa → tavily
52
54
 
53
55
  Override with `provider="exa"` etc.
54
56
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-all-search",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
4
4
  "description": "All-in-one search extension for Pi — exa, tavily, anysearch, firecrawl, brave",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -0,0 +1,27 @@
1
+ import type { SearchProvider, SearchResult } from "./types.js";
2
+
3
+ export const CONTEXT7_META = { name: "context7", label: "Context7", envVar: "CONTEXT7_API_KEY" };
4
+
5
+ export class Context7Provider implements SearchProvider {
6
+ name = "context7";
7
+ constructor(private apiKey: string) {}
8
+
9
+ async search(query: string, maxResults: number): Promise<{ results: SearchResult[] }> {
10
+ const resp = await fetch("https://api.context7.com/v1/search", {
11
+ method: "POST",
12
+ headers: {
13
+ "Content-Type": "application/json",
14
+ Authorization: `Bearer ${this.apiKey}`,
15
+ },
16
+ body: JSON.stringify({ query, limit: maxResults }),
17
+ });
18
+ const data = await resp.json();
19
+ const results: SearchResult[] = (data.results ?? []).map((r: any) => ({
20
+ title: r.title ?? r.name ?? "",
21
+ url: r.url ?? r.documentation_url ?? "",
22
+ snippet: r.description ?? r.snippet ?? "",
23
+ score: r.score,
24
+ }));
25
+ return { results };
26
+ }
27
+ }
@@ -2,7 +2,7 @@ import { EXA_META, ExaProvider } from "./exa.js";
2
2
  import { TAVILY_META, TavilyProvider } from "./tavily.js";
3
3
  import { ANYSEARCH_META, AnysearchProvider } from "./anysearch.js";
4
4
  import { FIRECRAWL_META, FirecrawlProvider } from "./firecrawl.js";
5
- import { BRAVE_META, BraveProvider } from "./brave.js";
5
+ import { CONTEXT7_META, Context7Provider } from "./context7.js";
6
6
  import type { SearchProvider } from "./types.js";
7
7
 
8
8
  export interface ProviderMeta {
@@ -16,7 +16,7 @@ export const PROVIDERS: readonly ProviderMeta[] = [
16
16
  TAVILY_META,
17
17
  ANYSEARCH_META,
18
18
  FIRECRAWL_META,
19
- BRAVE_META,
19
+ CONTEXT7_META,
20
20
  ];
21
21
 
22
22
  export function createProvider(name: string, apiKey: string): SearchProvider {
@@ -29,8 +29,8 @@ export function createProvider(name: string, apiKey: string): SearchProvider {
29
29
  return new AnysearchProvider(apiKey);
30
30
  case "firecrawl":
31
31
  return new FirecrawlProvider(apiKey);
32
- case "brave":
33
- return new BraveProvider(apiKey);
32
+ case "context7":
33
+ return new Context7Provider(apiKey);
34
34
  default:
35
35
  throw new Error(`Unknown provider: "${name}". Available: ${PROVIDERS.map((p) => p.name).join(", ")}`);
36
36
  }
package/src/router.ts CHANGED
@@ -1,24 +1,26 @@
1
1
  import type { SearchProvider } from "./providers/types.js";
2
2
 
3
- export type SearchIntent = "finance" | "academic" | "general";
3
+ export type SearchIntent = "finance" | "academic" | "general" | "docs";
4
4
 
5
5
  export function classifyIntent(query: string): SearchIntent {
6
6
  const q = query.toLowerCase();
7
7
  if (/\b(stock|price|ticker|forex|crypto|market|trade|earnings)\b/.test(q)) return "finance";
8
8
  if (/\b(paper|research|journal|doi|arxiv|scholar|academic|study)\b/.test(q)) return "academic";
9
+ if (/\b(doc|docs|documentation|library|framework|api|sdk|how to|example|syntax)\b/.test(q)) return "docs";
9
10
  return "general";
10
11
  }
11
12
 
12
- const INTENT PROVIDERS: Record<SearchIntent, { primary: string; secondary: string[] }> = {
13
- finance: { primary: "anysearch", secondary: ["brave", "exa"] },
14
- academic: { primary: "exa", secondary: ["anysearch", "brave"] },
15
- general: { primary: "tavily", secondary: ["brave", "exa", "anysearch"] },
13
+ const INTENT_PROVIDERS: Record<SearchIntent, { primary: string; secondary: string[] }> = {
14
+ finance: { primary: "anysearch", secondary: ["exa", "tavily"] },
15
+ academic: { primary: "exa", secondary: ["anysearch", "tavily"] },
16
+ general: { primary: "tavily", secondary: ["exa", "anysearch", "firecrawl"] },
17
+ docs: { primary: "context7", secondary: ["exa", "tavily"] },
16
18
  };
17
19
 
18
20
  export function routeIntent(
19
21
  intent: SearchIntent,
20
22
  providers: Map<string, SearchProvider>,
21
- requestedProvider?: string
23
+ requestedProvider?: string,
22
24
  ): { primary: string; secondary: string[] } {
23
25
  if (requestedProvider && providers.has(requestedProvider)) {
24
26
  return { primary: requestedProvider, secondary: [...providers.keys()].filter((k) => k !== requestedProvider) };
package/src/web-search.ts CHANGED
@@ -128,9 +128,9 @@ export function registerWebSearchTool(pi: ExtensionAPI): void {
128
128
  "Use web_search for information beyond your training data — current events, recent docs, live data.",
129
129
  "Set provider='anysearch' when searching for stock prices, tickers, forex, or CVE vulnerability hashes.",
130
130
  "Set provider='exa' when searching for academic research papers, journals, or DOIs.",
131
- "Set provider='tavily' for web pages, coding guides, library docs, and fast programming research.",
132
- "Set provider='brave' for general web search with good coverage.",
131
+ "Set provider='tavily' for web pages, coding guides, and fast programming research.",
133
132
  "Set provider='firecrawl' for scraping-heavy sites or when others fail.",
133
+ "Set provider='context7' for library/framework/API documentation, code examples, and how-to guides.",
134
134
  "Set provider='auto' to let the fast local intent router decide automatically (default).",
135
135
  "After answering, include a \"Sources:\" section with markdown hyperlinks: [Title](URL).",
136
136
  "Use web_fetch after web_search to read full page content — web_search returns snippets only.",
@@ -148,7 +148,7 @@ export function registerWebSearchTool(pi: ExtensionAPI): void {
148
148
  }),
149
149
  ),
150
150
  provider: Type.Optional(
151
- StringEnum(["auto", "exa", "tavily", "anysearch", "firecrawl", "brave"], {
151
+ StringEnum(["auto", "exa", "tavily", "anysearch", "firecrawl", "context7"], {
152
152
  description:
153
153
  "Directly override the search provider. 'auto' uses local intent routing (default).",
154
154
  default: "auto",
@@ -1,25 +0,0 @@
1
- import type { SearchProvider, SearchResult } from "./types.js";
2
-
3
- export const BRAVE_META = { name: "brave", label: "Brave", envVar: "BRAVE_API_KEY" };
4
-
5
- export class BraveProvider implements SearchProvider {
6
- name = "brave";
7
- constructor(private apiKey: string) {}
8
-
9
- async search(query: string, maxResults: number, signal?: AbortSignal): Promise<{ results: SearchResult[] }> {
10
- const resp = await fetch(
11
- `https://api.search.brave.com/res/v1/web/search?q=${encodeURIComponent(query)}&count=${maxResults}`,
12
- {
13
- headers: { "X-Subscription-Token": this.apiKey, Accept: "application/json" },
14
- signal,
15
- }
16
- );
17
- const data = await resp.json();
18
- const results: SearchResult[] = ((data.web?.results ?? []) as any[]).slice(0, maxResults).map((r) => ({
19
- title: r.title ?? "",
20
- url: r.url ?? "",
21
- snippet: r.description ?? "",
22
- }));
23
- return { results };
24
- }
25
- }