opencodekit 0.15.12 → 0.15.13
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 +4 -4
- package/dist/index.js +1 -1
- package/dist/template/.opencode/README.md +7 -4
- package/dist/template/.opencode/agent/scout.md +97 -26
- package/dist/template/.opencode/command/research-ui.md +1 -1
- package/dist/template/.opencode/command/research.md +1 -1
- package/dist/template/.opencode/memory/observations/2026-01-28-decision-gh-grep-mcp-wrapper-vs-native-grep-searc.md +21 -0
- package/dist/template/.opencode/memory/research/opencode-mcp-bug-report.md +5 -2
- package/dist/template/.opencode/opencode.json +39 -155
- package/dist/template/.opencode/plans/1768385996691-silent-wizard.md +12 -2
- package/dist/template/.opencode/skill/source-code-research/SKILL.md +8 -8
- package/dist/template/.opencode/skill/tool-priority/SKILL.md +8 -6
- package/dist/template/.opencode/tool/context7-query-docs.ts +89 -0
- package/dist/template/.opencode/tool/context7-resolve-library-id.ts +113 -0
- package/dist/template/.opencode/tool/grep-search.ts +135 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,8 +80,8 @@ Background Plugins:
|
|
|
80
80
|
Supporting:
|
|
81
81
|
• 30+ Skills (domain expertise loaded on-demand)
|
|
82
82
|
• 26+ Commands (workflow shortcuts)
|
|
83
|
-
• 3 MCP Services (context7, exa
|
|
84
|
-
• Custom Tools (memory-*, observation,
|
|
83
|
+
• 3 MCP Services (context7, exa) + grep-search tool + skill-embedded MCPs
|
|
84
|
+
• Custom Tools (memory-*, observation, grep-search, lsp-*)
|
|
85
85
|
• Beads task tracking (`bd` CLI for multi-session workflows)
|
|
86
86
|
• Manual Handoffs (clean phase transitions)
|
|
87
87
|
```
|
|
@@ -223,9 +223,9 @@ You've successfully set up OpenCodeKit when:
|
|
|
223
223
|
- ✅ Skills load on-demand (30+ skills)
|
|
224
224
|
- ✅ Delegation is clear (build → specialized subagents)
|
|
225
225
|
- ✅ `/handoff` creates portable bundles (.opencode/memory/handoffs/)
|
|
226
|
-
- ✅
|
|
226
|
+
- ✅ Tools configured (context7, grep-search + skill-embedded MCP)
|
|
227
227
|
- ✅ Background plugins active (enforcer, compactor, truncator)
|
|
228
|
-
- ✅ Custom tools available (memory-_, observation,
|
|
228
|
+
- ✅ Custom tools available (memory-_, observation, grep-search, lsp-_)
|
|
229
229
|
- ✅ Environment variables set (.opencode/.env with API keys)
|
|
230
230
|
|
|
231
231
|
---
|
package/dist/index.js
CHANGED
|
@@ -750,7 +750,7 @@ var cac = (name = "") => new CAC(name);
|
|
|
750
750
|
// package.json
|
|
751
751
|
var package_default = {
|
|
752
752
|
name: "opencodekit",
|
|
753
|
-
version: "0.15.
|
|
753
|
+
version: "0.15.13",
|
|
754
754
|
description: "CLI tool for bootstrapping and managing OpenCodeKit projects",
|
|
755
755
|
keywords: ["agents", "cli", "mcp", "opencode", "opencodekit", "template"],
|
|
756
756
|
license: "MIT",
|
|
@@ -180,16 +180,19 @@ Plugins run automatically in every session:
|
|
|
180
180
|
**Enabled by default (3 total):**
|
|
181
181
|
|
|
182
182
|
1. **context7** - Up-to-date library documentation (37.6k+ libraries)
|
|
183
|
-
-
|
|
184
|
-
-
|
|
183
|
+
- Native tools: `context7_resolve_library_id()` and `context7_query_docs()`
|
|
184
|
+
- Requires: CONTEXT7_API_KEY (optional - public libraries work without it)
|
|
185
|
+
- Step 1: Resolve library name → `/libraryId`
|
|
186
|
+
- Step 2: Query docs with the libraryId
|
|
185
187
|
|
|
186
188
|
2. **exa** - Web search + code context (3.3k+ repos)
|
|
187
189
|
- Requires: EXA_API_KEY
|
|
188
190
|
- GitHub: https://github.com/exa-labs/exa-mcp-server
|
|
189
191
|
|
|
190
|
-
3. **
|
|
192
|
+
3. **grep-search** - Search 1M+ public GitHub repositories via grep.app
|
|
193
|
+
- Native tool: `grep_search({ query: "...", language: [...] })`
|
|
191
194
|
- No API key needed (public service)
|
|
192
|
-
-
|
|
195
|
+
- Direct HTTP wrapper (no MCP overhead)
|
|
193
196
|
|
|
194
197
|
**Skill-Embedded MCP (load on-demand):**
|
|
195
198
|
|
|
@@ -93,7 +93,7 @@ Run 2-3 tool calls in parallel.
|
|
|
93
93
|
**Triggers:** "how do others", "compare", "best practices", "production patterns"
|
|
94
94
|
**Target:** Summary, 3-5 code examples, tradeoffs, recommendation
|
|
95
95
|
|
|
96
|
-
1. Search GitHub with
|
|
96
|
+
1. Search GitHub with grep_search (vary queries for different angles)
|
|
97
97
|
2. Compare 3-5 implementations from different repositories
|
|
98
98
|
3. Synthesize common patterns
|
|
99
99
|
4. Note tradeoffs and edge cases
|
|
@@ -104,30 +104,101 @@ Run 4-6 tool calls in parallel.
|
|
|
104
104
|
|
|
105
105
|
Every code reference must include a GitHub permalink. Never link to a branch or tag that can change.
|
|
106
106
|
|
|
107
|
-
To construct a permalink: use `
|
|
107
|
+
To construct a permalink: use `grep_search` to find code, then build the URL from the repository and file path returned. Format: `https://github.com/owner/repo/blob/<sha>/path/to/file#L10-L20`.
|
|
108
108
|
|
|
109
109
|
## Tool Priority (External Sources Only)
|
|
110
110
|
|
|
111
|
-
| Priority | Tool
|
|
112
|
-
| -------- |
|
|
113
|
-
| 1 | memory-search
|
|
114
|
-
| 2 | context7
|
|
115
|
-
| 3 | codesearch
|
|
116
|
-
| 4 |
|
|
117
|
-
| 5 | webfetch
|
|
118
|
-
| 6 | opensrc + LSP
|
|
119
|
-
| 7 | websearch
|
|
111
|
+
| Priority | Tool | Use Case | Speed |
|
|
112
|
+
| -------- | ------------- | ----------------------------------------- | ------- |
|
|
113
|
+
| 1 | memory-search | Past research findings | Instant |
|
|
114
|
+
| 2 | context7 | Official library docs | Fast |
|
|
115
|
+
| 3 | codesearch | Exa Code API for SDK/library patterns | Fast |
|
|
116
|
+
| 4 | grep_search | Cross-repo GitHub code search (1M+ repos) | Medium |
|
|
117
|
+
| 5 | webfetch | Specific doc URLs, READMEs, changelogs | Medium |
|
|
118
|
+
| 6 | opensrc + LSP | Clone & analyze source code | Slow |
|
|
119
|
+
| 7 | websearch | Tutorials, blog posts, recent news | Slow |
|
|
120
120
|
|
|
121
121
|
**Rule:** Exhaust faster tools before slower ones. Run tools in parallel when independent.
|
|
122
122
|
|
|
123
|
-
##
|
|
123
|
+
## context7 Tools
|
|
124
|
+
|
|
125
|
+
Use to access up-to-date library documentation (37.6k+ libraries).
|
|
126
|
+
|
|
127
|
+
### Two-Step Process
|
|
128
|
+
|
|
129
|
+
**Step 1: Resolve Library ID**
|
|
130
|
+
|
|
131
|
+
```typescript
|
|
132
|
+
context7_resolve_library_id({
|
|
133
|
+
libraryName: "react", // Required - package/library name
|
|
134
|
+
query?: "hooks composition", // Optional - search context
|
|
135
|
+
limit?: 5 // Optional - max results (default: 5)
|
|
136
|
+
})
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
Returns library IDs like `/facebook/react`, `/nodejs/node`, etc.
|
|
140
|
+
|
|
141
|
+
**Step 2: Query Documentation**
|
|
142
|
+
|
|
143
|
+
```typescript
|
|
144
|
+
context7_query_docs({
|
|
145
|
+
libraryId: "/facebook/react", // From resolve step
|
|
146
|
+
topic: "useState hook", // What you want to know
|
|
147
|
+
maxTokens?: 8000 // Optional - response size
|
|
148
|
+
})
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Query Patterns
|
|
152
|
+
|
|
153
|
+
**Good queries (specific features):**
|
|
154
|
+
|
|
155
|
+
- `useState hook` - Find React useState documentation
|
|
156
|
+
- `async/await` - Node.js async patterns
|
|
157
|
+
- `type decorators` - TypeScript types and decorators
|
|
158
|
+
- `setup instructions` - Getting started guides
|
|
159
|
+
|
|
160
|
+
**Bad queries (too vague):**
|
|
161
|
+
|
|
162
|
+
- `react` - Too broad
|
|
163
|
+
- `best practices` - Not a feature
|
|
164
|
+
- `how to use` - Natural language
|
|
165
|
+
|
|
166
|
+
### Examples
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
// Find React docs
|
|
170
|
+
const reactId = context7_resolve_library_id({ libraryName: "react" });
|
|
171
|
+
context7_query_docs({ libraryId: reactId, topic: "useEffect cleanup" });
|
|
172
|
+
|
|
173
|
+
// Find TypeScript docs
|
|
174
|
+
const tsId = context7_resolve_library_id({
|
|
175
|
+
libraryName: "typescript",
|
|
176
|
+
query: "types",
|
|
177
|
+
});
|
|
178
|
+
context7_query_docs({ libraryId: tsId, topic: "generic types" });
|
|
179
|
+
|
|
180
|
+
// Find Node.js docs
|
|
181
|
+
const nodeId = context7_resolve_library_id({ libraryName: "nodejs" });
|
|
182
|
+
context7_query_docs({ libraryId: nodeId, topic: "streams API" });
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
### Failure Handling
|
|
186
|
+
|
|
187
|
+
| Problem | Solution |
|
|
188
|
+
| ----------------- | ---------------------------------------------- |
|
|
189
|
+
| Library not found | Check exact name; try shorter/different names |
|
|
190
|
+
| No docs for topic | Broaden query - search parent concepts instead |
|
|
191
|
+
| Empty results | Try related terms or "API reference" |
|
|
192
|
+
| API error | Verify CONTEXT7_API_KEY is set (optional) |
|
|
193
|
+
|
|
194
|
+
## grep_search Tool
|
|
124
195
|
|
|
125
196
|
Use for cross-repository code search across 1M+ public GitHub repositories.
|
|
126
197
|
|
|
127
198
|
### Schema
|
|
128
199
|
|
|
129
200
|
```typescript
|
|
130
|
-
|
|
201
|
+
grep_search({
|
|
131
202
|
query: string, // Required - search pattern (literal code, not keywords)
|
|
132
203
|
repo?: string, // Optional - filter to specific repo (e.g., "vercel/ai")
|
|
133
204
|
language?: string[], // Optional - e.g., ["TypeScript", "TSX"]
|
|
@@ -157,22 +228,22 @@ gh_grep_searchGitHub({
|
|
|
157
228
|
|
|
158
229
|
```typescript
|
|
159
230
|
// Basic repo search
|
|
160
|
-
|
|
231
|
+
grep_search({ query: "batch_tool", repo: "anomalyco/opencode" });
|
|
161
232
|
|
|
162
233
|
// With language filter
|
|
163
|
-
|
|
234
|
+
grep_search({
|
|
164
235
|
query: "getServerSession",
|
|
165
236
|
language: ["TypeScript", "TSX"],
|
|
166
237
|
});
|
|
167
238
|
|
|
168
239
|
// Regex pattern (multi-line)
|
|
169
|
-
|
|
240
|
+
grep_search({
|
|
170
241
|
query: "(?s)useEffect\\(\\(\\) => {.*removeEventListener",
|
|
171
242
|
useRegexp: true,
|
|
172
243
|
});
|
|
173
244
|
|
|
174
245
|
// Path filter for specific files
|
|
175
|
-
|
|
246
|
+
grep_search({
|
|
176
247
|
query: "CORS(",
|
|
177
248
|
language: ["Python"],
|
|
178
249
|
matchCase: true,
|
|
@@ -184,7 +255,7 @@ gh_grep_searchGitHub({
|
|
|
184
255
|
| Problem | Solution |
|
|
185
256
|
| ---------------- | ------------------------------------------------ |
|
|
186
257
|
| Empty results | Broaden query - search concepts, not exact names |
|
|
187
|
-
|
|
|
258
|
+
| API error | Fall back to `codesearch` or `websearch` |
|
|
188
259
|
| Rate limited | Reduce parallel calls, go sequential |
|
|
189
260
|
| Too many results | Add `language` or `path` filters |
|
|
190
261
|
|
|
@@ -316,8 +387,8 @@ Structure your response as:
|
|
|
316
387
|
|
|
317
388
|
```
|
|
318
389
|
context7 fails → try codesearch for patterns
|
|
319
|
-
codesearch empty → try
|
|
320
|
-
|
|
390
|
+
codesearch empty → try grep_search with broader query
|
|
391
|
+
grep_search empty → webfetch specific doc URLs if known
|
|
321
392
|
still stuck → opensrc clone + LSP analysis
|
|
322
393
|
last resort → websearch for tutorials/blogs
|
|
323
394
|
```
|
|
@@ -327,10 +398,10 @@ last resort → websearch for tutorials/blogs
|
|
|
327
398
|
**context7 doesn't find library:**
|
|
328
399
|
|
|
329
400
|
1. Try `codesearch({ query: "<library> <function> example" })`
|
|
330
|
-
2. Try `
|
|
401
|
+
2. Try `grep_search({ query: "import.*from.*<library>", language: ["TypeScript"] })`
|
|
331
402
|
3. Clone with `npx opensrc <library>` and read source
|
|
332
403
|
|
|
333
|
-
**
|
|
404
|
+
**grep_search returns nothing:**
|
|
334
405
|
|
|
335
406
|
- Broaden query: search concepts, not exact function names
|
|
336
407
|
- Remove specific repo filter to search across all repos
|
|
@@ -339,9 +410,9 @@ last resort → websearch for tutorials/blogs
|
|
|
339
410
|
- Search for error messages, config patterns, or import statements
|
|
340
411
|
- Fall back to `codesearch` for conceptual queries
|
|
341
412
|
|
|
342
|
-
**
|
|
413
|
+
**grep_search API error:**
|
|
343
414
|
|
|
344
|
-
-
|
|
415
|
+
- The grep.app API may be temporarily unavailable
|
|
345
416
|
- Fall back to `codesearch` for similar functionality
|
|
346
417
|
- Use `opensrc` to clone specific repos and search locally
|
|
347
418
|
- Try `websearch` as last resort
|
|
@@ -368,6 +439,6 @@ NO URL GUESSING: Only use URLs from tools or user input.
|
|
|
368
439
|
CITE EVERYTHING: No claims without source links.
|
|
369
440
|
|
|
370
441
|
Quick: context7 → codesearch → websearch (2-3 parallel calls)
|
|
371
|
-
Deep:
|
|
372
|
-
Fallback: context7 → codesearch →
|
|
442
|
+
Deep: grep_search (4-6 parallel calls) → compare 3-5 repos → synthesize
|
|
443
|
+
Fallback: context7 → codesearch → grep_search → webfetch → opensrc → websearch
|
|
373
444
|
```
|
|
@@ -95,7 +95,7 @@ If memory search fails (Ollama not running), continue to external sources.
|
|
|
95
95
|
2. **Official docs** (high trust) - What does the library documentation say?
|
|
96
96
|
3. **Context7** (high trust) - API usage and examples
|
|
97
97
|
4. **Source code** (high trust) - Library implementation (use `source-code-research` skill)
|
|
98
|
-
5. **GitHub examples** (medium trust) - Real-world patterns via codesearch/
|
|
98
|
+
5. **GitHub examples** (medium trust) - Real-world patterns via codesearch/grep_search
|
|
99
99
|
6. **Web search** (lower trust) - Only if tiers 1-5 don't answer
|
|
100
100
|
|
|
101
101
|
## Research
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: decision
|
|
3
|
+
created: 2026-01-28T16:59:35.621Z
|
|
4
|
+
confidence: high
|
|
5
|
+
valid_until: null
|
|
6
|
+
superseded_by: null
|
|
7
|
+
concepts: ["grep-search", "MCP", "native tool", "architecture", "best practices"]
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# 🎯 gh-grep MCP wrapper vs native grep-search tool
|
|
11
|
+
|
|
12
|
+
🟢 **Confidence:** high
|
|
13
|
+
|
|
14
|
+
Created two implementations for GitHub code search:
|
|
15
|
+
|
|
16
|
+
1. **Native tool** (.opencode/tool/grep-search.ts): TypeScript wrapper around grep.app API - WORKING ✅
|
|
17
|
+
2. **MCP skill** (.opencode/skill/gh-grep/): Skill wrapper that calls uvx grep-mcp server - BROKEN ❌
|
|
18
|
+
|
|
19
|
+
The MCP server has an argument parsing bug ('str' object has no attribute 'get') that prevents tool execution. Even the SKILL.md documentation admits: "Note: The MCP server (grep-mcp) may have bugs. The native grep_search tool is recommended."
|
|
20
|
+
|
|
21
|
+
**Key insight**: We already documented the problem and recommended against the MCP approach. The native tool works perfectly and doesn't require external process dependencies.
|
|
@@ -94,10 +94,13 @@ The bug is in **OpenCode's tool response handler**, not the MCP connection layer
|
|
|
94
94
|
2. `opencode mcp debug` shows HTTP 200 OK responses
|
|
95
95
|
3. The error happens only when the AI agent calls the tool
|
|
96
96
|
|
|
97
|
-
##
|
|
97
|
+
## Resolution
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
✅ **FIXED** - Replaced MCP tools with native HTTP wrappers:
|
|
100
100
|
|
|
101
|
+
- `context7_resolve-library-id` - Native HTTP wrapper (works)
|
|
102
|
+
- `context7_query-docs` - Native HTTP wrapper (works)
|
|
103
|
+
- `grep-search` - Native HTTP wrapper (works)
|
|
101
104
|
- `codesearch` (Exa Code API) - works
|
|
102
105
|
- `websearch` (Exa Web Search) - works
|
|
103
106
|
|
|
@@ -49,53 +49,22 @@
|
|
|
49
49
|
"experimental": {},
|
|
50
50
|
"formatter": {
|
|
51
51
|
"biome": {
|
|
52
|
-
"command": [
|
|
53
|
-
|
|
54
|
-
"@biomejs/biome",
|
|
55
|
-
"check",
|
|
56
|
-
"--write",
|
|
57
|
-
"$FILE"
|
|
58
|
-
],
|
|
59
|
-
"extensions": [
|
|
60
|
-
".js",
|
|
61
|
-
".jsx",
|
|
62
|
-
".ts",
|
|
63
|
-
".tsx",
|
|
64
|
-
".json",
|
|
65
|
-
".jsonc"
|
|
66
|
-
]
|
|
52
|
+
"command": ["npx", "@biomejs/biome", "check", "--write", "$FILE"],
|
|
53
|
+
"extensions": [".js", ".jsx", ".ts", ".tsx", ".json", ".jsonc"]
|
|
67
54
|
},
|
|
68
55
|
"cargo-fmt": {
|
|
69
|
-
"command": [
|
|
70
|
-
|
|
71
|
-
"fmt",
|
|
72
|
-
"--",
|
|
73
|
-
"$FILE"
|
|
74
|
-
],
|
|
75
|
-
"extensions": [
|
|
76
|
-
".rs"
|
|
77
|
-
]
|
|
56
|
+
"command": ["cargo", "fmt", "--", "$FILE"],
|
|
57
|
+
"extensions": [".rs"]
|
|
78
58
|
},
|
|
79
59
|
"java-formatter": {
|
|
80
|
-
"command": [
|
|
81
|
-
"google-java-format",
|
|
82
|
-
"--replace",
|
|
83
|
-
"$FILE"
|
|
84
|
-
],
|
|
60
|
+
"command": ["google-java-format", "--replace", "$FILE"],
|
|
85
61
|
"environment": {
|
|
86
62
|
"JAVA_HOME": "{env:JAVA_HOME}"
|
|
87
63
|
},
|
|
88
|
-
"extensions": [
|
|
89
|
-
".java"
|
|
90
|
-
]
|
|
64
|
+
"extensions": [".java"]
|
|
91
65
|
},
|
|
92
66
|
"oxfmt": {
|
|
93
|
-
"command": [
|
|
94
|
-
"npx",
|
|
95
|
-
"oxfmt",
|
|
96
|
-
"--write",
|
|
97
|
-
"$FILE"
|
|
98
|
-
],
|
|
67
|
+
"command": ["npx", "oxfmt", "--write", "$FILE"],
|
|
99
68
|
"extensions": [
|
|
100
69
|
".js",
|
|
101
70
|
".jsx",
|
|
@@ -114,22 +83,11 @@
|
|
|
114
83
|
]
|
|
115
84
|
},
|
|
116
85
|
"laravel-pint": {
|
|
117
|
-
"command": [
|
|
118
|
-
|
|
119
|
-
"laravel-pint",
|
|
120
|
-
"--preset",
|
|
121
|
-
"psr12",
|
|
122
|
-
"$FILE"
|
|
123
|
-
],
|
|
124
|
-
"extensions": [
|
|
125
|
-
".php"
|
|
126
|
-
]
|
|
86
|
+
"command": ["npx", "laravel-pint", "--preset", "psr12", "$FILE"],
|
|
87
|
+
"extensions": [".php"]
|
|
127
88
|
}
|
|
128
89
|
},
|
|
129
|
-
"instructions": [
|
|
130
|
-
".opencode/memory/user.md",
|
|
131
|
-
".opencode/memory/project/*.md"
|
|
132
|
-
],
|
|
90
|
+
"instructions": [".opencode/memory/user.md", ".opencode/memory/project/*.md"],
|
|
133
91
|
"keybinds": {
|
|
134
92
|
"command_list": ";",
|
|
135
93
|
"leader": "`",
|
|
@@ -138,17 +96,6 @@
|
|
|
138
96
|
"session_compact": "ctrl+k"
|
|
139
97
|
},
|
|
140
98
|
"mcp": {
|
|
141
|
-
"context7": {
|
|
142
|
-
"type": "remote",
|
|
143
|
-
"url": "https://mcp.context7.com/mcp",
|
|
144
|
-
"headers": {
|
|
145
|
-
"CONTEXT7_API_KEY": "{env:CONTEXT7_API_KEY}"
|
|
146
|
-
}
|
|
147
|
-
},
|
|
148
|
-
"gh_grep": {
|
|
149
|
-
"type": "remote",
|
|
150
|
-
"url": "https://mcp.grep.app"
|
|
151
|
-
},
|
|
152
99
|
"stitch": {
|
|
153
100
|
"enabled": false,
|
|
154
101
|
"type": "remote",
|
|
@@ -195,6 +142,11 @@
|
|
|
195
142
|
"@tarquinen/opencode-dcp@latest",
|
|
196
143
|
"@franlol/opencode-md-table-formatter@0.0.3"
|
|
197
144
|
],
|
|
145
|
+
"tools": {
|
|
146
|
+
"grep-search": true,
|
|
147
|
+
"context7-resolve-library-id": true,
|
|
148
|
+
"context7-query-docs": true
|
|
149
|
+
},
|
|
198
150
|
"provider": {
|
|
199
151
|
"github-copilot": {
|
|
200
152
|
"models": {
|
|
@@ -338,17 +290,13 @@
|
|
|
338
290
|
"disabled": true
|
|
339
291
|
},
|
|
340
292
|
"high": {
|
|
341
|
-
"include": [
|
|
342
|
-
"reasoning.encrypted_content"
|
|
343
|
-
],
|
|
293
|
+
"include": ["reasoning.encrypted_content"],
|
|
344
294
|
"reasoningEffort": "high",
|
|
345
295
|
"reasoningSummary": "auto",
|
|
346
296
|
"textVerbosity": "low"
|
|
347
297
|
},
|
|
348
298
|
"medium": {
|
|
349
|
-
"include": [
|
|
350
|
-
"reasoning.encrypted_content"
|
|
351
|
-
],
|
|
299
|
+
"include": ["reasoning.encrypted_content"],
|
|
352
300
|
"reasoningEffort": "medium",
|
|
353
301
|
"reasoningSummary": "auto",
|
|
354
302
|
"textVerbosity": "low"
|
|
@@ -361,17 +309,13 @@
|
|
|
361
309
|
"disabled": true
|
|
362
310
|
},
|
|
363
311
|
"high": {
|
|
364
|
-
"include": [
|
|
365
|
-
"reasoning.encrypted_content"
|
|
366
|
-
],
|
|
312
|
+
"include": ["reasoning.encrypted_content"],
|
|
367
313
|
"reasoningEffort": "high",
|
|
368
314
|
"reasoningSummary": "auto",
|
|
369
315
|
"textVerbosity": "low"
|
|
370
316
|
},
|
|
371
317
|
"medium": {
|
|
372
|
-
"include": [
|
|
373
|
-
"reasoning.encrypted_content"
|
|
374
|
-
],
|
|
318
|
+
"include": ["reasoning.encrypted_content"],
|
|
375
319
|
"reasoningEffort": "medium",
|
|
376
320
|
"reasoningSummary": "auto",
|
|
377
321
|
"textVerbosity": "low"
|
|
@@ -402,14 +346,8 @@
|
|
|
402
346
|
},
|
|
403
347
|
"name": "Gemini 2 5 Computer Use Preview 10 2025",
|
|
404
348
|
"modalities": {
|
|
405
|
-
"input": [
|
|
406
|
-
|
|
407
|
-
"image",
|
|
408
|
-
"pdf"
|
|
409
|
-
],
|
|
410
|
-
"output": [
|
|
411
|
-
"text"
|
|
412
|
-
]
|
|
349
|
+
"input": ["text", "image", "pdf"],
|
|
350
|
+
"output": ["text"]
|
|
413
351
|
}
|
|
414
352
|
},
|
|
415
353
|
"gemini-2.5-flash": {
|
|
@@ -419,14 +357,8 @@
|
|
|
419
357
|
},
|
|
420
358
|
"name": "Gemini 2 5 Flash",
|
|
421
359
|
"modalities": {
|
|
422
|
-
"input": [
|
|
423
|
-
|
|
424
|
-
"image",
|
|
425
|
-
"pdf"
|
|
426
|
-
],
|
|
427
|
-
"output": [
|
|
428
|
-
"text"
|
|
429
|
-
]
|
|
360
|
+
"input": ["text", "image", "pdf"],
|
|
361
|
+
"output": ["text"]
|
|
430
362
|
}
|
|
431
363
|
},
|
|
432
364
|
"gemini-2.5-flash-lite": {
|
|
@@ -436,14 +368,8 @@
|
|
|
436
368
|
},
|
|
437
369
|
"name": "Gemini 2 5 Flash Lite",
|
|
438
370
|
"modalities": {
|
|
439
|
-
"input": [
|
|
440
|
-
|
|
441
|
-
"image",
|
|
442
|
-
"pdf"
|
|
443
|
-
],
|
|
444
|
-
"output": [
|
|
445
|
-
"text"
|
|
446
|
-
]
|
|
371
|
+
"input": ["text", "image", "pdf"],
|
|
372
|
+
"output": ["text"]
|
|
447
373
|
}
|
|
448
374
|
},
|
|
449
375
|
"gemini-2.5-pro": {
|
|
@@ -453,14 +379,8 @@
|
|
|
453
379
|
},
|
|
454
380
|
"name": "Gemini 2 5 Pro",
|
|
455
381
|
"modalities": {
|
|
456
|
-
"input": [
|
|
457
|
-
|
|
458
|
-
"image",
|
|
459
|
-
"pdf"
|
|
460
|
-
],
|
|
461
|
-
"output": [
|
|
462
|
-
"text"
|
|
463
|
-
]
|
|
382
|
+
"input": ["text", "image", "pdf"],
|
|
383
|
+
"output": ["text"]
|
|
464
384
|
}
|
|
465
385
|
},
|
|
466
386
|
"gemini-3-flash-preview": {
|
|
@@ -470,14 +390,8 @@
|
|
|
470
390
|
},
|
|
471
391
|
"name": "Gemini 3 Flash Preview",
|
|
472
392
|
"modalities": {
|
|
473
|
-
"input": [
|
|
474
|
-
|
|
475
|
-
"image",
|
|
476
|
-
"pdf"
|
|
477
|
-
],
|
|
478
|
-
"output": [
|
|
479
|
-
"text"
|
|
480
|
-
]
|
|
393
|
+
"input": ["text", "image", "pdf"],
|
|
394
|
+
"output": ["text"]
|
|
481
395
|
},
|
|
482
396
|
"variants": {
|
|
483
397
|
"low": {
|
|
@@ -499,14 +413,8 @@
|
|
|
499
413
|
},
|
|
500
414
|
"name": "Gemini 3 Pro Image Preview",
|
|
501
415
|
"modalities": {
|
|
502
|
-
"input": [
|
|
503
|
-
|
|
504
|
-
"image",
|
|
505
|
-
"pdf"
|
|
506
|
-
],
|
|
507
|
-
"output": [
|
|
508
|
-
"text"
|
|
509
|
-
]
|
|
416
|
+
"input": ["text", "image", "pdf"],
|
|
417
|
+
"output": ["text"]
|
|
510
418
|
}
|
|
511
419
|
},
|
|
512
420
|
"gemini-3-pro-preview": {
|
|
@@ -516,14 +424,8 @@
|
|
|
516
424
|
},
|
|
517
425
|
"name": "Gemini 3 Pro Preview",
|
|
518
426
|
"modalities": {
|
|
519
|
-
"input": [
|
|
520
|
-
|
|
521
|
-
"image",
|
|
522
|
-
"pdf"
|
|
523
|
-
],
|
|
524
|
-
"output": [
|
|
525
|
-
"text"
|
|
526
|
-
]
|
|
427
|
+
"input": ["text", "image", "pdf"],
|
|
428
|
+
"output": ["text"]
|
|
527
429
|
},
|
|
528
430
|
"variants": {
|
|
529
431
|
"low": {
|
|
@@ -541,14 +443,8 @@
|
|
|
541
443
|
"output": 64000
|
|
542
444
|
},
|
|
543
445
|
"modalities": {
|
|
544
|
-
"input": [
|
|
545
|
-
|
|
546
|
-
"image",
|
|
547
|
-
"pdf"
|
|
548
|
-
],
|
|
549
|
-
"output": [
|
|
550
|
-
"text"
|
|
551
|
-
]
|
|
446
|
+
"input": ["text", "image", "pdf"],
|
|
447
|
+
"output": ["text"]
|
|
552
448
|
},
|
|
553
449
|
"name": "Gemini Claude Opus 4 5 Thinking",
|
|
554
450
|
"variants": {
|
|
@@ -571,14 +467,8 @@
|
|
|
571
467
|
"output": 64000
|
|
572
468
|
},
|
|
573
469
|
"modalities": {
|
|
574
|
-
"input": [
|
|
575
|
-
|
|
576
|
-
"image",
|
|
577
|
-
"pdf"
|
|
578
|
-
],
|
|
579
|
-
"output": [
|
|
580
|
-
"text"
|
|
581
|
-
]
|
|
470
|
+
"input": ["text", "image", "pdf"],
|
|
471
|
+
"output": ["text"]
|
|
582
472
|
},
|
|
583
473
|
"name": "Gemini Claude Sonnet 4 5"
|
|
584
474
|
},
|
|
@@ -588,14 +478,8 @@
|
|
|
588
478
|
"output": 64000
|
|
589
479
|
},
|
|
590
480
|
"modalities": {
|
|
591
|
-
"input": [
|
|
592
|
-
|
|
593
|
-
"image",
|
|
594
|
-
"pdf"
|
|
595
|
-
],
|
|
596
|
-
"output": [
|
|
597
|
-
"text"
|
|
598
|
-
]
|
|
481
|
+
"input": ["text", "image", "pdf"],
|
|
482
|
+
"output": ["text"]
|
|
599
483
|
},
|
|
600
484
|
"name": "Gemini Claude Sonnet 4 5 Thinking",
|
|
601
485
|
"variants": {
|
|
@@ -38,7 +38,16 @@ If memory returns high-confidence findings on this exact topic, synthesize and r
|
|
|
38
38
|
| Priority | Tool | Use Case | Speed |
|
|
39
39
|
|----------|------|----------|-------|
|
|
40
40
|
| 1 | memory-search | Past research findings | Instant |
|
|
41
|
-
|
|
|
41
|
+
| Priority | Tool | Use Case | Speed |
|
|
42
|
+
|----------|------|----------|-------|
|
|
43
|
+
| 1 | memory-search | Past research findings | Instant |
|
|
44
|
+
| 2 | context7_resolve-library-id | Resolve library names to IDs | Fast |
|
|
45
|
+
| 3 | context7_query-docs | Official library docs | Fast |
|
|
46
|
+
| 4 | codesearch | Exa Code API for SDK/library patterns | Fast |
|
|
47
|
+
| 5 | grep-search | Cross-repo GitHub code search | Medium |
|
|
48
|
+
| 6 | webfetch | Specific doc URLs, READMEs, changelogs | Medium |
|
|
49
|
+
| 7 | opensrc + LSP | Clone & analyze source code | Slow |
|
|
50
|
+
| 8 | websearch | Tutorials, blog posts, recent news | Slow |
|
|
42
51
|
| 3 | codesearch | Usage patterns in real code | Fast |
|
|
43
52
|
| 4 | gh_grep | Cross-repo deep code search | Medium |
|
|
44
53
|
| 5 | webfetch | Specific doc URLs, READMEs, changelogs | Medium |
|
|
@@ -76,7 +85,8 @@ webfetch({ url: "https://docs.example.com/api/authentication", format: "markdown
|
|
|
76
85
|
**When to use:**
|
|
77
86
|
|
|
78
87
|
- User provides a specific URL
|
|
79
|
-
-
|
|
88
|
+
- context7_resolve-library-id returns a library ID
|
|
89
|
+
- context7_query-docs returns a doc link worth fetching
|
|
80
90
|
- Need CHANGELOG or release notes
|
|
81
91
|
- GitHub README has details not in context7
|
|
82
92
|
|
|
@@ -362,20 +362,20 @@ If opensrc doesn't work:
|
|
|
362
362
|
|
|
363
363
|
Source code research complements other tools:
|
|
364
364
|
|
|
365
|
-
| Method
|
|
366
|
-
|
|
|
367
|
-
| **Context7**
|
|
368
|
-
| **codesearch**
|
|
369
|
-
| **
|
|
370
|
-
| **Web search**
|
|
371
|
-
| **Codebase**
|
|
365
|
+
| Method | Best For | Source Code Adds |
|
|
366
|
+
| --------------- | -------------------------- | ------------------------------ |
|
|
367
|
+
| **Context7** | API docs, official guides | Implementation details |
|
|
368
|
+
| **codesearch** | Usage patterns in the wild | Canonical implementation |
|
|
369
|
+
| **grep_search** | Real-world examples | How library itself works |
|
|
370
|
+
| **Web search** | Tutorials, blog posts | Ground truth from source |
|
|
371
|
+
| **Codebase** | Project-specific patterns | How dependencies actually work |
|
|
372
372
|
|
|
373
373
|
**Recommended flow:**
|
|
374
374
|
|
|
375
375
|
1. Context7 - Check official docs
|
|
376
376
|
2. Codebase - Check existing usage
|
|
377
377
|
3. **Source code** - If still unclear, fetch source
|
|
378
|
-
4. codesearch/
|
|
378
|
+
4. codesearch/grep_search - See how others use it
|
|
379
379
|
5. Web search - Last resort for context
|
|
380
380
|
|
|
381
381
|
## Cleanup
|
|
@@ -180,9 +180,11 @@ glob ["src/**/*.ts", "tests/**/*.ts"]
|
|
|
180
180
|
|
|
181
181
|
## Research Tools
|
|
182
182
|
|
|
183
|
-
| Tool
|
|
184
|
-
|
|
|
185
|
-
| **
|
|
186
|
-
| **
|
|
187
|
-
| **
|
|
188
|
-
| **
|
|
183
|
+
| Tool | Use When |
|
|
184
|
+
| ------------------------------- | ------------------------------------------------------- |
|
|
185
|
+
| **context7_resolve-library-id** | Resolve library names to IDs (try first). |
|
|
186
|
+
| **context7_query-docs** | Query official library documentation. Fast. |
|
|
187
|
+
| **websearch** | Docs not in Context7, recent releases, troubleshooting. |
|
|
188
|
+
| **codesearch** | Real implementation patterns from GitHub. |
|
|
189
|
+
| **grep-search** | Cross-repo code patterns via grep.app. |
|
|
190
|
+
| **webfetch** | Specific URL user provided. |
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
|
|
3
|
+
// Context7 API v2 - https://context7.com/docs/api-guide
|
|
4
|
+
const CONTEXT7_API = "https://context7.com/api/v2";
|
|
5
|
+
|
|
6
|
+
export default tool({
|
|
7
|
+
description: `Query library documentation from Context7 using a library ID.
|
|
8
|
+
|
|
9
|
+
Use when:
|
|
10
|
+
- You have a library ID (from context7_resolve_library_id)
|
|
11
|
+
- Need specific documentation about a library feature
|
|
12
|
+
- Looking for API reference, examples, or setup instructions
|
|
13
|
+
|
|
14
|
+
Always resolve library name to ID first with context7_resolve_library_id!
|
|
15
|
+
|
|
16
|
+
Examples:
|
|
17
|
+
context7_query_docs({ libraryId: "/reactjs/react.dev", topic: "hooks" })
|
|
18
|
+
context7_query_docs({ libraryId: "/vercel/next.js", topic: "middleware" })
|
|
19
|
+
context7_query_docs({ libraryId: "/microsoft/TypeScript", topic: "generics" })
|
|
20
|
+
`,
|
|
21
|
+
args: {
|
|
22
|
+
libraryId: tool.schema
|
|
23
|
+
.string()
|
|
24
|
+
.describe(
|
|
25
|
+
"Library ID from context7_resolve_library_id (e.g., '/reactjs/react.dev')",
|
|
26
|
+
),
|
|
27
|
+
topic: tool.schema
|
|
28
|
+
.string()
|
|
29
|
+
.describe("Documentation topic or feature to search for"),
|
|
30
|
+
},
|
|
31
|
+
execute: async (args) => {
|
|
32
|
+
const { libraryId, topic } = args;
|
|
33
|
+
|
|
34
|
+
if (!libraryId || libraryId.trim() === "") {
|
|
35
|
+
return "Error: libraryId is required (use context7-resolve-library-id first)";
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
if (!topic || topic.trim() === "") {
|
|
39
|
+
return "Error: topic is required (e.g., 'hooks', 'setup', 'API reference')";
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
// Query Context7 documentation - GET /api/v2/context
|
|
44
|
+
// Returns text format by default which is better for LLM consumption
|
|
45
|
+
const url = new URL(`${CONTEXT7_API}/context`);
|
|
46
|
+
url.searchParams.set("libraryId", libraryId);
|
|
47
|
+
url.searchParams.set("query", topic);
|
|
48
|
+
|
|
49
|
+
// Add API key if available (recommended for higher rate limits)
|
|
50
|
+
const apiKey = process.env.CONTEXT7_API_KEY;
|
|
51
|
+
const headers: HeadersInit = {
|
|
52
|
+
Accept: "text/plain",
|
|
53
|
+
"User-Agent": "OpenCode/1.0",
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
if (apiKey) {
|
|
57
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const response = await fetch(url.toString(), { headers });
|
|
61
|
+
|
|
62
|
+
if (!response.ok) {
|
|
63
|
+
if (response.status === 401) {
|
|
64
|
+
return `Error: Invalid CONTEXT7_API_KEY. Get a free key at https://context7.com/dashboard`;
|
|
65
|
+
}
|
|
66
|
+
if (response.status === 404) {
|
|
67
|
+
return `Error: Library not found: ${libraryId}\n\nUse context7-resolve-library-id first to find the correct ID.`;
|
|
68
|
+
}
|
|
69
|
+
if (response.status === 429) {
|
|
70
|
+
return `Error: Rate limit exceeded. Get a free API key at https://context7.com/dashboard for higher limits.`;
|
|
71
|
+
}
|
|
72
|
+
return `Error: Context7 API returned ${response.status}`;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const content = await response.text();
|
|
76
|
+
|
|
77
|
+
if (!content || content.trim() === "") {
|
|
78
|
+
return `No documentation found for "${topic}" in ${libraryId}.\n\nTry:\n- Simpler terms (e.g., "useState" instead of "state management")\n- Different topic spelling\n- Broader topics like "API reference" or "getting started"`;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return `# Documentation: ${topic} (${libraryId})
|
|
82
|
+
|
|
83
|
+
${content}`;
|
|
84
|
+
} catch (error: unknown) {
|
|
85
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
86
|
+
return `Error querying documentation: ${message}`;
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
});
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
|
|
3
|
+
// Context7 API v2 - https://context7.com/docs/api-guide
|
|
4
|
+
const CONTEXT7_API = "https://context7.com/api/v2";
|
|
5
|
+
|
|
6
|
+
interface LibraryInfo {
|
|
7
|
+
id: string;
|
|
8
|
+
title: string;
|
|
9
|
+
description?: string;
|
|
10
|
+
totalSnippets?: number;
|
|
11
|
+
trustScore?: number;
|
|
12
|
+
benchmarkScore?: number;
|
|
13
|
+
versions?: string[];
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface SearchResponse {
|
|
17
|
+
results: LibraryInfo[];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export default tool({
|
|
21
|
+
description: `Resolve a library name to its Context7 ID for documentation lookup.
|
|
22
|
+
|
|
23
|
+
Use when:
|
|
24
|
+
- You need to find the exact library ID format for a package
|
|
25
|
+
- Starting a documentation search (get the ID first, then query docs)
|
|
26
|
+
- Normalizing library names (e.g., "react" → "/reactjs/react.dev")
|
|
27
|
+
|
|
28
|
+
Examples:
|
|
29
|
+
context7_resolve_library_id({ libraryName: "react" })
|
|
30
|
+
context7_resolve_library_id({ libraryName: "vue", query: "composition API" })
|
|
31
|
+
context7_resolve_library_id({ libraryName: "nextjs" })
|
|
32
|
+
`,
|
|
33
|
+
args: {
|
|
34
|
+
libraryName: tool.schema
|
|
35
|
+
.string()
|
|
36
|
+
.describe(
|
|
37
|
+
"Library name to resolve (e.g., 'react', 'lodash', 'typescript')",
|
|
38
|
+
),
|
|
39
|
+
query: tool.schema
|
|
40
|
+
.string()
|
|
41
|
+
.optional()
|
|
42
|
+
.describe("Optional context for the search (improves relevance ranking)"),
|
|
43
|
+
},
|
|
44
|
+
execute: async (args) => {
|
|
45
|
+
const { libraryName, query = "documentation" } = args;
|
|
46
|
+
|
|
47
|
+
if (!libraryName || libraryName.trim() === "") {
|
|
48
|
+
return "Error: libraryName is required";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// Query Context7 library search - GET /api/v2/libs/search
|
|
53
|
+
const url = new URL(`${CONTEXT7_API}/libs/search`);
|
|
54
|
+
url.searchParams.set("libraryName", libraryName);
|
|
55
|
+
url.searchParams.set("query", query);
|
|
56
|
+
|
|
57
|
+
// Add API key if available (recommended for higher rate limits)
|
|
58
|
+
const apiKey = process.env.CONTEXT7_API_KEY;
|
|
59
|
+
const headers: HeadersInit = {
|
|
60
|
+
Accept: "application/json",
|
|
61
|
+
"User-Agent": "OpenCode/1.0",
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
if (apiKey) {
|
|
65
|
+
headers.Authorization = `Bearer ${apiKey}`;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const response = await fetch(url.toString(), { headers });
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
if (response.status === 401) {
|
|
72
|
+
return `Error: Invalid CONTEXT7_API_KEY. Get a free key at https://context7.com/dashboard`;
|
|
73
|
+
}
|
|
74
|
+
if (response.status === 429) {
|
|
75
|
+
return `Error: Rate limit exceeded. Get a free API key at https://context7.com/dashboard for higher limits.`;
|
|
76
|
+
}
|
|
77
|
+
return `Error: Context7 API returned ${response.status}`;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const data = (await response.json()) as SearchResponse;
|
|
81
|
+
const libraries = data.results || [];
|
|
82
|
+
|
|
83
|
+
if (!libraries || libraries.length === 0) {
|
|
84
|
+
return `No libraries found matching: ${libraryName}\n\nTry:\n- Different library name\n- Check spelling\n- Use official package name`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const formatted = libraries
|
|
88
|
+
.slice(0, 5)
|
|
89
|
+
.map((lib, i) => {
|
|
90
|
+
const desc = lib.description
|
|
91
|
+
? `\n ${lib.description.slice(0, 100)}...`
|
|
92
|
+
: "";
|
|
93
|
+
const snippets = lib.totalSnippets
|
|
94
|
+
? ` (${lib.totalSnippets} snippets)`
|
|
95
|
+
: "";
|
|
96
|
+
const score = lib.benchmarkScore
|
|
97
|
+
? ` [score: ${lib.benchmarkScore}]`
|
|
98
|
+
: "";
|
|
99
|
+
return `${i + 1}. **${lib.title}** → \`${lib.id}\`${snippets}${score}${desc}`;
|
|
100
|
+
})
|
|
101
|
+
.join("\n\n");
|
|
102
|
+
|
|
103
|
+
return `Found ${libraries.length} libraries matching "${libraryName}":
|
|
104
|
+
|
|
105
|
+
${formatted}
|
|
106
|
+
|
|
107
|
+
**Next step**: Use \`context7-query-docs({ libraryId: "${libraries[0].id}", topic: "your topic" })\` to fetch documentation.`;
|
|
108
|
+
} catch (error: unknown) {
|
|
109
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
110
|
+
return `Error resolving library: ${message}`;
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
});
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin";
|
|
2
|
+
|
|
3
|
+
const GREP_APP_API = "https://grep.app/api/search";
|
|
4
|
+
|
|
5
|
+
interface SearchResult {
|
|
6
|
+
repo: string;
|
|
7
|
+
path: string;
|
|
8
|
+
content: { snippet: string };
|
|
9
|
+
total_matches: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
interface GrepResponse {
|
|
13
|
+
hits: { hits: SearchResult[] };
|
|
14
|
+
time: number;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export default tool({
|
|
18
|
+
description: `Search real-world code examples from GitHub repositories via grep.app.
|
|
19
|
+
|
|
20
|
+
Use when:
|
|
21
|
+
- Implementing unfamiliar APIs - see how others use a library
|
|
22
|
+
- Looking for production patterns - find real-world examples
|
|
23
|
+
- Understanding library integrations - see how things work together
|
|
24
|
+
|
|
25
|
+
IMPORTANT: Search for **literal code patterns**, not keywords:
|
|
26
|
+
✅ Good: "useState(", "import React from", "async function"
|
|
27
|
+
❌ Bad: "react tutorial", "best practices", "how to use"
|
|
28
|
+
|
|
29
|
+
Examples:
|
|
30
|
+
grep_search({ query: "getServerSession", language: "TypeScript" })
|
|
31
|
+
grep_search({ query: "CORS(", language: "Python", repo: "flask" })
|
|
32
|
+
grep_search({ query: "export async function POST", path: "route.ts" })
|
|
33
|
+
`,
|
|
34
|
+
args: {
|
|
35
|
+
query: tool.schema
|
|
36
|
+
.string()
|
|
37
|
+
.describe("Code pattern to search for (literal text)"),
|
|
38
|
+
language: tool.schema
|
|
39
|
+
.string()
|
|
40
|
+
.optional()
|
|
41
|
+
.describe("Filter by language: TypeScript, TSX, Python, Go, Rust, etc."),
|
|
42
|
+
repo: tool.schema
|
|
43
|
+
.string()
|
|
44
|
+
.optional()
|
|
45
|
+
.describe("Filter by repo: 'owner/repo' or partial match"),
|
|
46
|
+
path: tool.schema
|
|
47
|
+
.string()
|
|
48
|
+
.optional()
|
|
49
|
+
.describe("Filter by file path: 'src/', '.test.ts', etc."),
|
|
50
|
+
limit: tool.schema
|
|
51
|
+
.number()
|
|
52
|
+
.optional()
|
|
53
|
+
.describe("Max results to return (default: 10, max: 20)"),
|
|
54
|
+
},
|
|
55
|
+
execute: async (args) => {
|
|
56
|
+
const { query, language, repo, path, limit = 10 } = args;
|
|
57
|
+
|
|
58
|
+
if (!query || query.trim() === "") {
|
|
59
|
+
return "Error: query is required";
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Build URL with proper filter parameters
|
|
63
|
+
// grep.app uses filter[lang][0]=TypeScript format, NOT inline lang:TypeScript
|
|
64
|
+
const url = new URL(GREP_APP_API);
|
|
65
|
+
url.searchParams.set("q", query);
|
|
66
|
+
|
|
67
|
+
// Add language filter (grep.app uses filter[lang][0] format)
|
|
68
|
+
if (language) {
|
|
69
|
+
url.searchParams.set("filter[lang][0]", language);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Add repo filter
|
|
73
|
+
if (repo) {
|
|
74
|
+
url.searchParams.set("filter[repo][0]", repo);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Add path filter
|
|
78
|
+
if (path) {
|
|
79
|
+
url.searchParams.set("filter[path][0]", path);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
try {
|
|
83
|
+
const response = await fetch(url.toString(), {
|
|
84
|
+
headers: {
|
|
85
|
+
Accept: "application/json",
|
|
86
|
+
"User-Agent": "OpenCode/1.0",
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
if (!response.ok) {
|
|
91
|
+
return `Error: grep.app API returned ${response.status}`;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const data = (await response.json()) as GrepResponse;
|
|
95
|
+
|
|
96
|
+
if (!data.hits?.hits?.length) {
|
|
97
|
+
return `No results found for: ${query}${language ? ` (${language})` : ""}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const maxResults = Math.min(limit, 20);
|
|
101
|
+
const results = data.hits.hits.slice(0, maxResults);
|
|
102
|
+
|
|
103
|
+
const formatted = results.map((hit, i) => {
|
|
104
|
+
const repoName = hit.repo || "unknown";
|
|
105
|
+
const filePath = hit.path || "unknown";
|
|
106
|
+
const snippet = hit.content?.snippet || "";
|
|
107
|
+
|
|
108
|
+
// Clean up HTML from snippet and extract text
|
|
109
|
+
const cleanCode = snippet
|
|
110
|
+
.replace(/<[^>]*>/g, "") // Remove HTML tags
|
|
111
|
+
.replace(/</g, "<")
|
|
112
|
+
.replace(/>/g, ">")
|
|
113
|
+
.replace(/&/g, "&")
|
|
114
|
+
.replace(/"/g, '"')
|
|
115
|
+
.split("\n")
|
|
116
|
+
.slice(0, 8)
|
|
117
|
+
.join("\n")
|
|
118
|
+
.trim();
|
|
119
|
+
|
|
120
|
+
return `## ${i + 1}. ${repoName}
|
|
121
|
+
**File**: ${filePath}
|
|
122
|
+
\`\`\`
|
|
123
|
+
${cleanCode}
|
|
124
|
+
\`\`\``;
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
return `Found ${data.hits.hits.length} results (showing ${results.length}) in ${data.time}ms:
|
|
128
|
+
|
|
129
|
+
${formatted.join("\n\n")}`;
|
|
130
|
+
} catch (error: unknown) {
|
|
131
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
132
|
+
return `Error searching grep.app: ${message}`;
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
});
|
package/package.json
CHANGED