@fradser/pi-context 0.5.0
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 +187 -0
- package/agents/context-researcher.md +36 -0
- package/extensions/context-tools.ts +275 -0
- package/package.json +40 -0
- package/skills/code-context/SKILL.md +127 -0
- package/skills/get-context/SKILL.md +38 -0
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# Code Context Pi Package
|
|
2
|
+
|
|
3
|
+
**Version:** 0.4.1
|
|
4
|
+
|
|
5
|
+
Retrieve code context for any repo, library, or natural-language query via 5 methods: DeepWiki, Context7, Exa, git clone, and web search+fetch.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
pi install /path/to/pi-packages/packages/code-context
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Run `/skill:get-context <query…>` for the executable Pi workflow.
|
|
14
|
+
|
|
15
|
+
## Runtime requirements
|
|
16
|
+
|
|
17
|
+
| Method | Availability | Notes |
|
|
18
|
+
|--------|--------------|-------|
|
|
19
|
+
| DeepWiki | Always (native tool `context_deepwiki`) | No API key required |
|
|
20
|
+
| Context7 | Always (native tool `context_context7`) | Optional `CONTEXT7_API_KEY` (Bearer) for per-key quota |
|
|
21
|
+
| Exa | Native tool `context_exa` | Requires `EXA_API_KEY` env var |
|
|
22
|
+
| Git clone | Always | Pi `bash` + `read` |
|
|
23
|
+
| Web fetch | Always | `curl`/HTTP via `bash` |
|
|
24
|
+
|
|
25
|
+
All three retrieval tools are registered by this package's extension (`extensions/context-tools.ts`) and call the public REST APIs directly — no MCP servers needed.
|
|
26
|
+
|
|
27
|
+
## Overview
|
|
28
|
+
|
|
29
|
+
Code Context accepts **arbitrary input** — a natural-language question, a repo slug, a library name, or several of these at once — and routes each target to the right method(s). It prefers intermediate summaries so the main conversation stays clean, returning only synthesized summaries.
|
|
30
|
+
|
|
31
|
+
## Methods
|
|
32
|
+
|
|
33
|
+
### 1. DeepWiki (AI-powered repo documentation)
|
|
34
|
+
|
|
35
|
+
Best for: Public GitHub repositories requiring architecture overview, component explanations, or high-level understanding.
|
|
36
|
+
|
|
37
|
+
**Tool:** `context_deepwiki` — `owner`/`repo` + `mode: structure | contents | ask`
|
|
38
|
+
|
|
39
|
+
**Process:**
|
|
40
|
+
1. `context_deepwiki` with `mode: "structure"` (e.g. owner=`facebook`, repo=`react`)
|
|
41
|
+
2. `context_deepwiki` with `mode: "contents"` for full wiki text, or `mode: "ask"` + `question` for targeted Q&A
|
|
42
|
+
|
|
43
|
+
**Strengths:** Zero setup, instant AI-summarized documentation.
|
|
44
|
+
|
|
45
|
+
**Limitations:** Only works for public GitHub repos; coverage varies by popularity.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
### 2. Context7 (library documentation)
|
|
50
|
+
|
|
51
|
+
Best for: Getting up-to-date API docs, usage examples, and version-specific documentation for npm/pip packages.
|
|
52
|
+
|
|
53
|
+
**Tool:** `context_context7` — `query` + optional `topic`
|
|
54
|
+
|
|
55
|
+
**Process:**
|
|
56
|
+
1. `context_context7` with the library name (e.g. `"react"`, `"fastapi"`)
|
|
57
|
+
2. Pass a `topic` (e.g. `"auth"`, `"ssr"`) for focused snippets; include the version in the query when given (`react@18`)
|
|
58
|
+
|
|
59
|
+
**Strengths:** Always current docs, supports version pinning, covers thousands of libraries.
|
|
60
|
+
|
|
61
|
+
**Limitations:** Requires the library to be indexed; less useful for internal packages.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
### 3. Exa Code Search (web-wide code examples)
|
|
66
|
+
|
|
67
|
+
Best for: Finding real-world usage patterns, StackOverflow answers, GitHub Gist examples.
|
|
68
|
+
|
|
69
|
+
**Tool:** `context_exa` — `query` + optional `numResults` (requires `EXA_API_KEY`)
|
|
70
|
+
|
|
71
|
+
**Query tips:**
|
|
72
|
+
- Include language: `"TypeScript React"`
|
|
73
|
+
- Include version: `"Next.js 14 app router"`
|
|
74
|
+
- Use exact identifiers: `"useServerAction"`
|
|
75
|
+
- Add pattern type: `"example"`, `"error handling"`
|
|
76
|
+
|
|
77
|
+
**Strengths:** Finds diverse real-world examples beyond official docs.
|
|
78
|
+
|
|
79
|
+
**Limitations:** Results may be outdated; verify publication dates.
|
|
80
|
+
|
|
81
|
+
---
|
|
82
|
+
|
|
83
|
+
### 4. Git Clone (direct code inspection)
|
|
84
|
+
|
|
85
|
+
Best for: Private repositories, detailed implementation review, or when other methods lack depth.
|
|
86
|
+
|
|
87
|
+
**Process:**
|
|
88
|
+
1. `git clone <repo-url> /tmp/<repo-name> --depth=1`
|
|
89
|
+
2. Read key files: entry points, configuration, core modules
|
|
90
|
+
3. Use bash find/grep and Pi `read` to analyze structure and patterns
|
|
91
|
+
4. Clean up: `rm -rf /tmp/<repo-name>`
|
|
92
|
+
|
|
93
|
+
**Strengths:** Full code access, works with private repos.
|
|
94
|
+
|
|
95
|
+
**Limitations:** Requires network/disk space; slow for large repos.
|
|
96
|
+
|
|
97
|
+
---
|
|
98
|
+
|
|
99
|
+
### 5. Web Search + Fetch (post-clone enrichment)
|
|
100
|
+
|
|
101
|
+
Best for: Enriching clone findings with changelogs, issue discussions, migration guides.
|
|
102
|
+
|
|
103
|
+
**Tools:** web search via `curl`/HTTP, HTTP fetch via `curl`
|
|
104
|
+
|
|
105
|
+
**When to use:** After completing Method 4. Gives you the *why* and *what changed*.
|
|
106
|
+
|
|
107
|
+
**Query patterns:**
|
|
108
|
+
- Changelogs: `"<repo> CHANGELOG v<version>"`
|
|
109
|
+
- Design rationale: `"<repo> <concept> why OR rationale site:github.com"`
|
|
110
|
+
- Migration: `"<repo> migrate from <old> to <new>"`
|
|
111
|
+
|
|
112
|
+
**Strengths:** Surfaces context never in source code.
|
|
113
|
+
|
|
114
|
+
**Limitations:** Results may be stale; always validate against actual code.
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## Usage
|
|
119
|
+
|
|
120
|
+
### Pi skill: get-context
|
|
121
|
+
|
|
122
|
+
Run `/skill:get-context` followed by a natural-language question, repository slug, library name, or several targets. For example:
|
|
123
|
+
|
|
124
|
+
```text
|
|
125
|
+
/skill:get-context React 19 server actions error handling
|
|
126
|
+
/skill:get-context facebook/react
|
|
127
|
+
/skill:get-context facebook/react zustand --method=deepwiki,context7
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
`--method=` accepts `deepwiki,context7,exa,clone,web,all` (comma-separated). With no target, the skill reads dependency manifests in the current directory. It classifies each target and routes it through the allowed methods, noting gaps when a permitted method cannot cover it.
|
|
131
|
+
|
|
132
|
+
### Optional manual brief
|
|
133
|
+
|
|
134
|
+
`agents/context-researcher.md` is an optional manual prompt brief for an isolated research pass. Pi does not load it as an invocable agent; use the `get-context` skill for the executable workflow.
|
|
135
|
+
|
|
136
|
+
The package's internal method-selection guidance is loaded by the `get-context` workflow.
|
|
137
|
+
|
|
138
|
+
## Method Selection Guide
|
|
139
|
+
|
|
140
|
+
| Scenario | Primary | Fallback |
|
|
141
|
+
|----------|---------|----------|
|
|
142
|
+
| "How does X library work?" | Context7 | DeepWiki |
|
|
143
|
+
| "Understand Y repo architecture" | DeepWiki | Git Clone |
|
|
144
|
+
| "Find examples of Z pattern" | Exa | Context7 |
|
|
145
|
+
| "Inspect private/internal repo" | Git Clone | - |
|
|
146
|
+
| "What changed in v3?" | Context7 | Exa |
|
|
147
|
+
| "How are modules connected?" | DeepWiki | Git Clone |
|
|
148
|
+
| "Why was this design decision?" | Git Clone → Web Search | DeepWiki |
|
|
149
|
+
| "Compare X vs Y" (natural-language) | Exa + Context7 | Web Search |
|
|
150
|
+
| "Best practice for Z" (natural-language) | Web Search | Exa |
|
|
151
|
+
|
|
152
|
+
## Structure
|
|
153
|
+
|
|
154
|
+
```
|
|
155
|
+
code-context/
|
|
156
|
+
├── extensions/
|
|
157
|
+
│ └── context-tools.ts # Native pi tools: context_deepwiki / context_context7 / context_exa
|
|
158
|
+
├── agents/
|
|
159
|
+
│ └── context-researcher.md # Isolated research agent brief
|
|
160
|
+
├── skills/
|
|
161
|
+
│ ├── code-context/ # Knowledge skill (internal)
|
|
162
|
+
│ │ └── SKILL.md # Method selection guide
|
|
163
|
+
│ └── get-context/ # User-invocable command
|
|
164
|
+
│ └── SKILL.md # Command workflow
|
|
165
|
+
└── README.md
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Prerequisites
|
|
169
|
+
|
|
170
|
+
- Pi 0.84+ (extensions + custom tools)
|
|
171
|
+
- Network access for external lookups
|
|
172
|
+
- `EXA_API_KEY` for the Exa method (optional for the other four)
|
|
173
|
+
|
|
174
|
+
## Best Practices
|
|
175
|
+
|
|
176
|
+
- **Local first**: Always check local context (package.json, imports) before external lookups
|
|
177
|
+
- **Isolated execution**: External lookups run in agent context to keep main conversation clean
|
|
178
|
+
- **Combine methods**: For comprehensive context, use DeepWiki → Context7 → Exa → Clone
|
|
179
|
+
- **Verify sources**: Cross-reference fetched content against actual code
|
|
180
|
+
|
|
181
|
+
## License
|
|
182
|
+
|
|
183
|
+
MIT
|
|
184
|
+
|
|
185
|
+
## Author
|
|
186
|
+
|
|
187
|
+
Frad LEE (fradser@gmail.com)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: context-researcher
|
|
3
|
+
description: Research a library, repository, or code pattern without polluting the main conversation. Uses the native context_deepwiki/context_context7/context_exa tools when applicable, otherwise git clone + HTTP fetch via Pi bash/read. Accepts natural-language queries, repo slugs, library names, or any combination.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Context researcher (optional agent brief)
|
|
7
|
+
|
|
8
|
+
Pi does not load Claude Code agent files automatically. Treat this file as a **prompt brief** when you want an isolated research pass. Prefer `/skill:get-context` or `/skill:code-context` for the executable workflow.
|
|
9
|
+
|
|
10
|
+
## Runtime requirements
|
|
11
|
+
|
|
12
|
+
| Method | Requirement |
|
|
13
|
+
|--------|-------------|
|
|
14
|
+
| DeepWiki | Native `context_deepwiki` tool |
|
|
15
|
+
| Context7 | Native `context_context7` tool |
|
|
16
|
+
| Exa | Native `context_exa` tool (+ optional `EXA_API_KEY`) |
|
|
17
|
+
| Git clone | `git` + Pi `bash` / `read` (always) |
|
|
18
|
+
| Web | `curl` or host HTTP via Pi `bash` (always) |
|
|
19
|
+
|
|
20
|
+
If a native context tool is unavailable or fails, skip that method, use clone + web, and report which methods ran and why the fallback was needed.
|
|
21
|
+
|
|
22
|
+
## Process
|
|
23
|
+
|
|
24
|
+
1. **Parse the request** — targets and optional `--method=` list (see `/skill:get-context`).
|
|
25
|
+
2. **Classify** each target (repo / library / natural-language).
|
|
26
|
+
3. **Select methods** per `/skill:code-context` selection guide.
|
|
27
|
+
4. **Execute with Pi tools** — use the applicable native context tools (`context_deepwiki`, `context_context7`, `context_exa`) plus `bash` and `read`. Summarize intermediates; do not dump raw payloads.
|
|
28
|
+
5. **Return** a concise architecture/API/examples summary only.
|
|
29
|
+
|
|
30
|
+
## Output format
|
|
31
|
+
|
|
32
|
+
- Target classification
|
|
33
|
+
- Methods used (and skipped, with reason)
|
|
34
|
+
- Key findings (bullets)
|
|
35
|
+
- Copyable snippets only when necessary
|
|
36
|
+
- Open questions / confidence notes
|
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Native pi tools for DeepWiki / Context7 / Exa retrieval.
|
|
3
|
+
*
|
|
4
|
+
* Replaces the package's `.mcp.json` (MCP servers). pi has no built-in MCP
|
|
5
|
+
* support (docs/usage.md), so these tools call the public REST APIs directly:
|
|
6
|
+
* - context_deepwiki → https://mcp.deepwiki.com/mcp (DeepWiki's public JSON-RPC/SSE API)
|
|
7
|
+
* - context_context7 → https://context7.com/api/v1/{search,docs}
|
|
8
|
+
* - context_exa → https://api.exa.ai/search (needs EXA_API_KEY)
|
|
9
|
+
*
|
|
10
|
+
* Pure HTTP tools — available in interactive and non-interactive runs alike.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
14
|
+
import { truncateHead, DEFAULT_MAX_LINES } from "@earendil-works/pi-coding-agent";
|
|
15
|
+
import { StringEnum } from "@earendil-works/pi-ai";
|
|
16
|
+
import { Type } from "typebox";
|
|
17
|
+
|
|
18
|
+
const MAX_CHARS = 60_000;
|
|
19
|
+
const TIMEOUT_MS = 30_000;
|
|
20
|
+
const DEEPWIKI_MCP = "https://mcp.deepwiki.com/mcp";
|
|
21
|
+
|
|
22
|
+
interface ToolTextResult {
|
|
23
|
+
content: [{ type: "text"; text: string }];
|
|
24
|
+
details: Record<string, unknown>;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function textResult(text: string, details: Record<string, unknown> = {}): ToolTextResult {
|
|
28
|
+
return { content: [{ type: "text", text }], details };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function truncate(text: string): string {
|
|
32
|
+
if (text.length <= MAX_CHARS) return text;
|
|
33
|
+
const result = truncateHead(text, { maxLines: DEFAULT_MAX_LINES, maxBytes: MAX_CHARS });
|
|
34
|
+
return `${result.content}\n\n…[truncated ${text.length - MAX_CHARS} chars]`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function requestSignal(signal: AbortSignal | undefined): AbortSignal {
|
|
38
|
+
const timeout = AbortSignal.timeout(TIMEOUT_MS);
|
|
39
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function httpJson(
|
|
43
|
+
url: string,
|
|
44
|
+
init: RequestInit = {},
|
|
45
|
+
signal: AbortSignal | undefined,
|
|
46
|
+
): Promise<{ ok: boolean; status: number; body: string }> {
|
|
47
|
+
const res = await fetch(url, { ...init, signal: requestSignal(signal) });
|
|
48
|
+
const body = await res.text();
|
|
49
|
+
return { ok: res.ok, status: res.status, body };
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* DeepWiki's public API is a JSON-RPC-over-HTTP/SSE endpoint (mcp.deepwiki.com).
|
|
54
|
+
* Call a tool directly — no MCP client or sidecar process needed.
|
|
55
|
+
*/
|
|
56
|
+
async function deepwikiCall(
|
|
57
|
+
tool: string,
|
|
58
|
+
args: Record<string, unknown>,
|
|
59
|
+
signal: AbortSignal | undefined,
|
|
60
|
+
): Promise<string> {
|
|
61
|
+
const res = await fetch(DEEPWIKI_MCP, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
headers: {
|
|
64
|
+
"Content-Type": "application/json",
|
|
65
|
+
Accept: "application/json, text/event-stream",
|
|
66
|
+
},
|
|
67
|
+
body: JSON.stringify({ jsonrpc: "2.0", id: 1, method: "tools/call", params: { name: tool, arguments: args } }),
|
|
68
|
+
signal: requestSignal(signal),
|
|
69
|
+
});
|
|
70
|
+
const body = await res.text();
|
|
71
|
+
if (!res.ok) throw new Error(`DeepWiki HTTP ${res.status}: ${body.slice(0, 200)}`);
|
|
72
|
+
|
|
73
|
+
// SSE frames: `event: message` + one or more `data:` lines, blank-line terminated.
|
|
74
|
+
// Join multiline payloads, keep the last frame with data.
|
|
75
|
+
let payload: string | null = null;
|
|
76
|
+
let currentData: string[] = [];
|
|
77
|
+
for (const raw of body.split("\n")) {
|
|
78
|
+
const line = raw.endsWith("\r") ? raw.slice(0, -1) : raw;
|
|
79
|
+
if (line === "") {
|
|
80
|
+
if (currentData.length > 0) payload = currentData.join("\n");
|
|
81
|
+
currentData = [];
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
if (line.startsWith(":")) continue; // SSE comment
|
|
85
|
+
if (line.startsWith("data:")) {
|
|
86
|
+
currentData.push(line.slice(5).replace(/^ /, ""));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (currentData.length > 0) payload = currentData.join("\n");
|
|
90
|
+
if (payload === null) throw new Error("DeepWiki: no data frame in response");
|
|
91
|
+
|
|
92
|
+
const data = JSON.parse(payload);
|
|
93
|
+
if (data.error) throw new Error(`DeepWiki: ${data.error.message ?? JSON.stringify(data.error)}`);
|
|
94
|
+
const content = data.result?.content;
|
|
95
|
+
if (Array.isArray(content)) {
|
|
96
|
+
return content.map((c) => (typeof c?.text === "string" ? c.text : JSON.stringify(c))).join("\n");
|
|
97
|
+
}
|
|
98
|
+
return JSON.stringify(data.result ?? data);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const DeepWikiParams = Type.Object({
|
|
102
|
+
owner: Type.String({ description: "GitHub owner, e.g. 'facebook'" }),
|
|
103
|
+
repo: Type.String({ description: "GitHub repository name, e.g. 'react'" }),
|
|
104
|
+
mode: StringEnum(["structure", "contents", "ask"], {
|
|
105
|
+
description: "structure = table of contents; contents = full wiki text; ask = targeted Q&A",
|
|
106
|
+
}),
|
|
107
|
+
question: Type.Optional(
|
|
108
|
+
Type.String({ description: "Required when mode is 'ask'; the question about the repository" }),
|
|
109
|
+
),
|
|
110
|
+
});
|
|
111
|
+
const Context7Params = Type.Object({
|
|
112
|
+
query: Type.String({ description: "Library name to find docs for, e.g. 'fastapi' or 'react'" }),
|
|
113
|
+
topic: Type.Optional(
|
|
114
|
+
Type.String({ description: "Optional topic focus, e.g. 'auth' or 'ssr' (best results with a topic)" }),
|
|
115
|
+
),
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
const ExaParams = Type.Object({
|
|
119
|
+
query: Type.String({ description: "Precise natural-language or code query" }),
|
|
120
|
+
numResults: Type.Optional(
|
|
121
|
+
Type.Number({ description: "Number of results (default 5)", default: 5 }),
|
|
122
|
+
),
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
export default function (pi: ExtensionAPI) {
|
|
126
|
+
pi.registerTool({
|
|
127
|
+
name: "context_deepwiki",
|
|
128
|
+
label: "DeepWiki repo docs",
|
|
129
|
+
description:
|
|
130
|
+
"Fetch AI-generated documentation for a public GitHub repository from DeepWiki. " +
|
|
131
|
+
"mode=structure returns the table of contents; mode=contents returns the full wiki text; " +
|
|
132
|
+
"mode=ask answers a specific question with sources. No API key required.",
|
|
133
|
+
promptSnippet: "Look up AI-generated documentation for a public GitHub repo from DeepWiki",
|
|
134
|
+
promptGuidelines: [
|
|
135
|
+
"Use context_deepwiki to fetch structured repo documentation via DeepWiki instead of cloning when you only need a high-level understanding.",
|
|
136
|
+
],
|
|
137
|
+
parameters: DeepWikiParams,
|
|
138
|
+
executionMode: "sequential",
|
|
139
|
+
|
|
140
|
+
async execute(_toolCallId, params, signal) {
|
|
141
|
+
const repo = `${params.owner}/${params.repo}`;
|
|
142
|
+
if (params.mode === "ask") {
|
|
143
|
+
if (!params.question) {
|
|
144
|
+
return textResult("context_deepwiki: mode 'ask' requires a question");
|
|
145
|
+
}
|
|
146
|
+
const answer = await deepwikiCall("ask_question", { repoName: repo, question: params.question }, signal);
|
|
147
|
+
return textResult(truncate(answer));
|
|
148
|
+
}
|
|
149
|
+
const tool = params.mode === "structure" ? "read_wiki_structure" : "read_wiki_contents";
|
|
150
|
+
const out = await deepwikiCall(tool, { repoName: repo }, signal);
|
|
151
|
+
return textResult(truncate(out));
|
|
152
|
+
},
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
pi.registerTool({
|
|
156
|
+
name: "context_context7",
|
|
157
|
+
label: "Context7 library docs",
|
|
158
|
+
description:
|
|
159
|
+
"Fetch up-to-date, version-aware documentation snippets for a library from Context7. " +
|
|
160
|
+
"Searches the library, then retrieves markdown docs (optionally focused on a topic). " +
|
|
161
|
+
"Anonymous usage is rate-limited; set CONTEXT7_API_KEY (Authorization: Bearer) for per-key quota.",
|
|
162
|
+
promptSnippet: "Look up version-aware library documentation from Context7",
|
|
163
|
+
promptGuidelines: [
|
|
164
|
+
"Use context_context7 to look up library docs instead of guessing APIs from memory. Anonymous usage is rate-limited; set CONTEXT7_API_KEY for higher quota.",
|
|
165
|
+
],
|
|
166
|
+
parameters: Context7Params,
|
|
167
|
+
executionMode: "sequential",
|
|
168
|
+
|
|
169
|
+
async execute(_toolCallId, params, signal) {
|
|
170
|
+
const headers: Record<string, string> = {};
|
|
171
|
+
if (process.env.CONTEXT7_API_KEY) {
|
|
172
|
+
headers.Authorization = `Bearer ${process.env.CONTEXT7_API_KEY}`;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const { ok, status, body } = await httpJson(
|
|
176
|
+
`https://context7.com/api/v1/search?query=${encodeURIComponent(params.query)}`,
|
|
177
|
+
{ headers },
|
|
178
|
+
signal,
|
|
179
|
+
);
|
|
180
|
+
if (!ok) {
|
|
181
|
+
if (status === 401 || status === 403) {
|
|
182
|
+
return textResult(
|
|
183
|
+
`Context7 search needs an API key (HTTP ${status}). Set CONTEXT7_API_KEY ` +
|
|
184
|
+
`(Authorization: Bearer) or fall back to web/clone methods.`,
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
throw new Error(`Context7 search failed (HTTP ${status}): ${body.slice(0, 400)}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
let data: { results?: Array<{ id?: unknown }> };
|
|
191
|
+
try {
|
|
192
|
+
data = JSON.parse(body);
|
|
193
|
+
} catch {
|
|
194
|
+
throw new Error(`Context7 search: unexpected response: ${body.slice(0, 400)}`);
|
|
195
|
+
}
|
|
196
|
+
const results = Array.isArray(data.results) ? data.results : [];
|
|
197
|
+
if (results.length === 0) {
|
|
198
|
+
return textResult(`Context7: no library found for "${params.query}"`);
|
|
199
|
+
}
|
|
200
|
+
const libraryId = typeof results[0]?.id === "string" ? results[0].id : "";
|
|
201
|
+
if (!libraryId) {
|
|
202
|
+
return textResult(`Context7: search returned no usable library id for "${params.query}"`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// v1 doc ids look like "/owner/repo" — strip the leading slash for the path.
|
|
206
|
+
const path = libraryId.startsWith("/") ? libraryId.slice(1) : libraryId;
|
|
207
|
+
const topic = params.topic ? `&topic=${encodeURIComponent(params.topic)}` : "";
|
|
208
|
+
const docUrl = `https://context7.com/api/v1/${path}?type=txt${topic}`;
|
|
209
|
+
const doc = await httpJson(docUrl, { headers }, signal);
|
|
210
|
+
if (!doc.ok) {
|
|
211
|
+
throw new Error(`Context7 docs failed (HTTP ${doc.status}): ${doc.body.slice(0, 400)}`);
|
|
212
|
+
}
|
|
213
|
+
return textResult(
|
|
214
|
+
`Context7 docs for "${params.query}" (${libraryId})${params.topic ? `, topic ${params.topic}` : ""}:\n\n${truncate(doc.body)}`,
|
|
215
|
+
);
|
|
216
|
+
},
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
pi.registerTool({
|
|
220
|
+
name: "context_exa",
|
|
221
|
+
label: "Exa web/code search",
|
|
222
|
+
description:
|
|
223
|
+
"Search the web for real-world code patterns, comparisons, and up-to-date information via Exa. " +
|
|
224
|
+
"Requires the EXA_API_KEY environment variable. Returns titles, URLs, and text snippets.",
|
|
225
|
+
promptSnippet: "Search the web for real-world code patterns, comparisons, and up-to-date information via Exa",
|
|
226
|
+
promptGuidelines: [
|
|
227
|
+
"Use context_exa to search the web when you need current information, code patterns, or comparisons. Requires EXA_API_KEY to be set.",
|
|
228
|
+
],
|
|
229
|
+
parameters: ExaParams,
|
|
230
|
+
executionMode: "sequential",
|
|
231
|
+
|
|
232
|
+
async execute(_toolCallId, params, signal) {
|
|
233
|
+
const apiKey = process.env.EXA_API_KEY;
|
|
234
|
+
if (!apiKey) {
|
|
235
|
+
return textResult(
|
|
236
|
+
"context_exa: EXA_API_KEY is not set. Set EXA_API_KEY to enable Exa search, " +
|
|
237
|
+
"or fall back to web search via curl / GitHub search.",
|
|
238
|
+
);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const numResults = Math.max(1, Math.min(10, Math.floor(params.numResults ?? 5)));
|
|
242
|
+
const { ok, status, body } = await httpJson(
|
|
243
|
+
"https://api.exa.ai/search",
|
|
244
|
+
{
|
|
245
|
+
method: "POST",
|
|
246
|
+
headers: {
|
|
247
|
+
"Content-Type": "application/json",
|
|
248
|
+
"x-api-key": apiKey,
|
|
249
|
+
},
|
|
250
|
+
body: JSON.stringify({ query: params.query, numResults, contents: { text: true } }),
|
|
251
|
+
},
|
|
252
|
+
signal,
|
|
253
|
+
);
|
|
254
|
+
if (!ok) throw new Error(`Exa search failed (HTTP ${status}): ${body.slice(0, 400)}`);
|
|
255
|
+
|
|
256
|
+
let data: { results?: Array<{ title?: string; url?: string; text?: string }> };
|
|
257
|
+
try {
|
|
258
|
+
data = JSON.parse(body);
|
|
259
|
+
} catch {
|
|
260
|
+
throw new Error(`Exa search: unexpected response: ${body.slice(0, 400)}`);
|
|
261
|
+
}
|
|
262
|
+
const results = Array.isArray(data.results) ? data.results : [];
|
|
263
|
+
if (results.length === 0) {
|
|
264
|
+
return textResult(`Exa: no results for "${params.query}"`);
|
|
265
|
+
}
|
|
266
|
+
const lines = results.map((r, i) => {
|
|
267
|
+
const title = r.title || r.url || "(untitled)";
|
|
268
|
+
const url = r.url || "";
|
|
269
|
+
const text = typeof r.text === "string" ? r.text.slice(0, 1200) : "";
|
|
270
|
+
return `${i + 1}. ${title}\n ${url}\n${text ? ` ${text}` : ""}`;
|
|
271
|
+
});
|
|
272
|
+
return textResult(truncate(lines.join("\n\n")));
|
|
273
|
+
},
|
|
274
|
+
});
|
|
275
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fradser/pi-context",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Retrieve code context for any repo, library, or natural-language query via DeepWiki, Context7, Exa, git clone, and web search+fetch. Native pi tools (context_deepwiki/context_context7/context_exa) call the public REST APIs directly — no MCP servers.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"pi-package",
|
|
7
|
+
"code-context",
|
|
8
|
+
"deepwiki",
|
|
9
|
+
"context7",
|
|
10
|
+
"exa"
|
|
11
|
+
],
|
|
12
|
+
"author": "Frad LEE <fradser@gmail.com>",
|
|
13
|
+
"license": "MIT",
|
|
14
|
+
"repository": {
|
|
15
|
+
"type": "git",
|
|
16
|
+
"url": "https://github.com/FradSer/pi-packages.git"
|
|
17
|
+
},
|
|
18
|
+
"publishConfig": {
|
|
19
|
+
"access": "public"
|
|
20
|
+
},
|
|
21
|
+
"files": [
|
|
22
|
+
"skills",
|
|
23
|
+
"extensions",
|
|
24
|
+
"agents",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"pi": {
|
|
28
|
+
"skills": [
|
|
29
|
+
"./skills"
|
|
30
|
+
],
|
|
31
|
+
"extensions": [
|
|
32
|
+
"./extensions"
|
|
33
|
+
]
|
|
34
|
+
},
|
|
35
|
+
"peerDependencies": {
|
|
36
|
+
"@earendil-works/pi-coding-agent": "*",
|
|
37
|
+
"@earendil-works/pi-ai": "*",
|
|
38
|
+
"typebox": "*"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: code-context
|
|
3
|
+
description: This skill should be used when the user asks to "understand a codebase", "get code context", "research a library", "explore a repository", "find code examples", "look up documentation", asks a natural-language code/technology question (e.g. "how does X work", "X vs Y", "best practice for Z"), or wants to understand how a specific project, library, or concept works before making changes.
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Code Context Retrieval
|
|
8
|
+
|
|
9
|
+
This skill provides 5 methods for retrieving code context. Select methods based on the target: public GitHub repos, library docs, code search, direct inspection, or post-clone web enrichment.
|
|
10
|
+
|
|
11
|
+
## Pi tools and runtime
|
|
12
|
+
|
|
13
|
+
Use Pi built-ins: `bash`, `read`, `edit` (rarely), `write` (rarely), plus this package's native tools from `extensions/context-tools.ts`.
|
|
14
|
+
|
|
15
|
+
| Method | Runtime |
|
|
16
|
+
|--------|---------|
|
|
17
|
+
| DeepWiki | `context_deepwiki` tool (always available; no key) |
|
|
18
|
+
| Context7 | `context_context7` tool (always available; optional `CONTEXT7_API_KEY` for higher quota) |
|
|
19
|
+
| Exa | `context_exa` tool (requires `EXA_API_KEY` env) |
|
|
20
|
+
| Git clone | `bash` + `read` (always available) |
|
|
21
|
+
| Web search+fetch | `bash` + `curl` (or host HTTP) |
|
|
22
|
+
|
|
23
|
+
If a tool reports an error (e.g. `EXA_API_KEY` unset), skip it and use clone/web fallbacks. Report which methods ran.
|
|
24
|
+
|
|
25
|
+
## Token Isolation (Critical)
|
|
26
|
+
|
|
27
|
+
Prefer small intermediate summaries over dumping entire API payloads into the long-lived conversation:
|
|
28
|
+
|
|
29
|
+
- **DeepWiki**: structure → relevant pages → short architecture summary
|
|
30
|
+
- **Context7**: resolve id → query → minimum viable API surface + examples
|
|
31
|
+
- **Exa**: query → dedupe near-identical snippets → short explanation
|
|
32
|
+
- **Git clone**: clone to `/tmp/`, read entry points, cleanup, return structure + patterns
|
|
33
|
+
- **Web**: targeted queries, fetch high-signal URLs only, validate against code when possible
|
|
34
|
+
|
|
35
|
+
## Method 1: DeepWiki (AI-powered repo documentation)
|
|
36
|
+
|
|
37
|
+
Best for: public GitHub repos needing architecture overview quickly.
|
|
38
|
+
|
|
39
|
+
**Tool**: `context_deepwiki` (native, always available)
|
|
40
|
+
|
|
41
|
+
**Process**:
|
|
42
|
+
1. `context_deepwiki` with `owner`/`repo` and `mode: "structure"` (e.g. `facebook` / `react`)
|
|
43
|
+
2. `context_deepwiki` `mode: "contents"` for the full wiki text, or `mode: "ask"` with a `question` for targeted Q&A
|
|
44
|
+
3. Extract architecture summary and key relationships
|
|
45
|
+
|
|
46
|
+
**Fallback on error**: Method 4 (clone) + Method 5 (web) on GitHub docs/README.
|
|
47
|
+
|
|
48
|
+
## Method 2: Context7 (library documentation)
|
|
49
|
+
|
|
50
|
+
Best for: up-to-date API docs and version-specific examples for packages/frameworks.
|
|
51
|
+
|
|
52
|
+
**Tool**: `context_context7` (native, always available)
|
|
53
|
+
|
|
54
|
+
**Process**:
|
|
55
|
+
1. `context_context7` with the library name as `query` (e.g. `fastapi`, `react`)
|
|
56
|
+
2. Pass a `topic` (e.g. `auth`, `ssr`) for focused snippets; include the version in the query when given (`react@18`)
|
|
57
|
+
3. Extract the minimum viable API surface + examples
|
|
58
|
+
|
|
59
|
+
**Fallback on error**: official docs URLs via `curl`, or clone of the library repo docs.
|
|
60
|
+
|
|
61
|
+
## Method 3: Exa Code Search
|
|
62
|
+
|
|
63
|
+
Best for: real-world usage patterns and cross-web code examples.
|
|
64
|
+
|
|
65
|
+
**Tool**: `context_exa` (native; requires `EXA_API_KEY`)
|
|
66
|
+
|
|
67
|
+
**Process**:
|
|
68
|
+
1. Call with a precise query (language/framework/version/identifiers)
|
|
69
|
+
2. Prefer recent sources; dedupe mirrors
|
|
70
|
+
|
|
71
|
+
**Fallback when key missing / error**: GitHub code search URLs / known docs via `curl`, or clone representative examples.
|
|
72
|
+
|
|
73
|
+
## Method 4: Git Clone (direct code inspection)
|
|
74
|
+
|
|
75
|
+
Best for: private repos, deep implementation review, when other methods lack depth.
|
|
76
|
+
|
|
77
|
+
**Process**:
|
|
78
|
+
1. `git clone <repo-url> /tmp/<repo-name> --depth=1`
|
|
79
|
+
2. `read` key files: entry points, config, core modules
|
|
80
|
+
3. Map structure with `bash` find/ls; search with `bash` grep or Pi search tools if available
|
|
81
|
+
4. Cleanup: `rm -rf /tmp/<repo-name>`
|
|
82
|
+
|
|
83
|
+
## Method 5: Web Search + Fetch
|
|
84
|
+
|
|
85
|
+
Best for: rationale, changelogs, issues, migration guides.
|
|
86
|
+
|
|
87
|
+
**Tools**: `bash` + `curl` (or host HTTP client). There are no Pi built-ins named web search via `curl`/HTTP / HTTP fetch via `curl`.
|
|
88
|
+
|
|
89
|
+
**Process**:
|
|
90
|
+
1. Derive version-anchored queries from the question or clone findings
|
|
91
|
+
2. Fetch high-signal URLs (official docs, GitHub issues/PRs, changelogs)
|
|
92
|
+
3. Extract only relevant sections; cross-check against cloned code when available
|
|
93
|
+
4. Discard results older than ~2 years unless foundational
|
|
94
|
+
|
|
95
|
+
**Query patterns**:
|
|
96
|
+
- Changelogs: `"<repo> CHANGELOG v<version>"`
|
|
97
|
+
- Rationale: `"<repo> <concept> why OR rationale site:github.com"`
|
|
98
|
+
- Known issues: `"<repo> <symbol> issue OR bug site:github.com"`
|
|
99
|
+
- Migration: `"<repo> migrate from <old> to <new>"`
|
|
100
|
+
|
|
101
|
+
## Target Classification
|
|
102
|
+
|
|
103
|
+
- **Repo target** — `owner/repo` or git URL → DeepWiki (`context_deepwiki`) or Git Clone
|
|
104
|
+
- **Library target** — package name / `name@version` → Context7 (`context_context7`) else web/docs clone
|
|
105
|
+
- **Natural-language target** — question/comparison → Exa (`context_exa`) and/or Web; Context7 if a library is named
|
|
106
|
+
|
|
107
|
+
When caller passes `--method=`, use the intersection of allowed and applicable methods. Empty intersection → skip and report.
|
|
108
|
+
|
|
109
|
+
## Method Selection Guide
|
|
110
|
+
|
|
111
|
+
| Scenario | Primary | Fallback |
|
|
112
|
+
|----------|---------|----------|
|
|
113
|
+
| "How does X library work?" | Context7 | Web / clone |
|
|
114
|
+
| "Architecture of Y repo" | DeepWiki | Git Clone |
|
|
115
|
+
| "Examples of Z pattern" | Exa | Context7 / web |
|
|
116
|
+
| "Private/internal repo" | Git Clone | — |
|
|
117
|
+
| "What changed in v3?" | Context7 | Web |
|
|
118
|
+
| "Why this design?" | Git Clone → Web | DeepWiki |
|
|
119
|
+
| "Compare X vs Y" | Exa + Context7 | Web |
|
|
120
|
+
| "Best practice for Z" | Web | Exa |
|
|
121
|
+
|
|
122
|
+
## Combining Methods
|
|
123
|
+
|
|
124
|
+
1. DeepWiki / clone for architecture
|
|
125
|
+
2. Context7 for API surface
|
|
126
|
+
3. Exa / web for community patterns
|
|
127
|
+
4. Always prefer read-only operations; clean up `/tmp` clones
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: get-context
|
|
3
|
+
description: Execute this when the user requests code context for a repository, library, or any natural-language code/technology question using DeepWiki, Context7, Exa, git clone, and/or web search+fetch.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# get-context
|
|
7
|
+
|
|
8
|
+
Run the full code-context workflow using Pi tools. Prefer keeping heavy lookups out of a single giant turn when possible — summarize intermediate results.
|
|
9
|
+
|
|
10
|
+
## Pi tools and runtime
|
|
11
|
+
|
|
12
|
+
| Capability | How |
|
|
13
|
+
|------------|-----|
|
|
14
|
+
| Clone / local files | `bash` (`git clone`), `read`, `bash` (find/grep) |
|
|
15
|
+
| Web fetch | `bash` with `curl -fsSL` (or available HTTP client) |
|
|
16
|
+
| DeepWiki / Context7 / Exa | Native tools from this package's `extensions/context-tools.ts`: `context_deepwiki`, `context_context7`, `context_exa` (Exa needs `EXA_API_KEY`) |
|
|
17
|
+
|
|
18
|
+
If a native tool is unavailable (e.g. `EXA_API_KEY` unset), fall back to clone + web fetch methods from `/skill:code-context`.
|
|
19
|
+
|
|
20
|
+
## Invocation args
|
|
21
|
+
|
|
22
|
+
When invoked as `/skill:get-context <args>`, the text after the skill block is the user-provided arguments. Pi does not expand `$ARGUMENTS` placeholders inside skill bodies; use the trailing invocation text.
|
|
23
|
+
|
|
24
|
+
1. Split into positional targets and optional `--method=` flag.
|
|
25
|
+
- `--method=` accepts a comma-separated list from `deepwiki,context7,exa,clone,web,all`. Default: `all`.
|
|
26
|
+
- Quoted strings are one target.
|
|
27
|
+
- Multiple positional tokens are multiple targets.
|
|
28
|
+
2. Classify each target:
|
|
29
|
+
- GitHub slug (`owner/repo`) or git URL → repo (DeepWiki via `context_deepwiki`, else clone).
|
|
30
|
+
- Bare package name or `name@version` → library (Context7 via `context_context7`, else web + clone of docs repo when known).
|
|
31
|
+
- Anything else → natural-language (Exa via `context_exa` when `EXA_API_KEY` is set, else web search via `curl` to searchable sources / known docs URLs).
|
|
32
|
+
3. Empty input: read dependency manifests in cwd (`package.json`, `go.mod`, `pyproject.toml`, `Cargo.toml`) and use detected dependencies as targets.
|
|
33
|
+
|
|
34
|
+
## Execute
|
|
35
|
+
|
|
36
|
+
1. Load and follow `/skill:code-context` (or `read` that skill file) for method selection and process detail.
|
|
37
|
+
2. Run methods with Pi tools as above; do not invent Claude-only tools (`Task`, `WebSearch`, `WebFetch`).
|
|
38
|
+
3. Return a synthesized summary only — drop raw bulk payloads.
|