claude-all-hands 1.0.6 → 1.0.8
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/.claude/envoy/README.md +11 -0
- package/.claude/envoy/package-lock.json +7 -0
- package/.claude/envoy/package.json +1 -0
- package/.claude/envoy/src/cli.ts +1 -0
- package/.claude/envoy/src/commands/context7.ts +176 -0
- package/.claude/envoy/src/commands/{gemini.ts → oracle.ts} +251 -358
- package/.claude/envoy/src/lib/gemini-provider.ts +38 -0
- package/.claude/envoy/src/lib/index.ts +17 -1
- package/.claude/envoy/src/lib/observability.ts +12 -0
- package/.claude/envoy/src/lib/openai-provider.ts +90 -0
- package/.claude/envoy/src/lib/providers.ts +71 -0
- package/.claude/envoy/src/lib/retry.ts +11 -4
- package/.claude/settings.json +2 -1
- package/.claude/skills/research-tools/SKILL.md +44 -2
- package/package.json +1 -1
package/.claude/envoy/README.md
CHANGED
|
@@ -21,6 +21,15 @@ envoy <group> --help
|
|
|
21
21
|
| `tavily extract` | Extract full content from URLs |
|
|
22
22
|
| `xai search` | X/Twitter search for community opinions, alternatives, discussions |
|
|
23
23
|
|
|
24
|
+
### Context7 (External Documentation)
|
|
25
|
+
|
|
26
|
+
| Tool | Use Case |
|
|
27
|
+
|------|----------|
|
|
28
|
+
| `context7 search` | Find library by name, returns IDs for context command |
|
|
29
|
+
| `context7 context` | Get documentation for known library (use search first) |
|
|
30
|
+
|
|
31
|
+
*Flow: search → get library ID → context with query*
|
|
32
|
+
|
|
24
33
|
### Vertex (Gemini)
|
|
25
34
|
|
|
26
35
|
| Tool | Use Case |
|
|
@@ -44,6 +53,7 @@ envoy <group> --help
|
|
|
44
53
|
- Pre-synthesized findings → `perplexity research`
|
|
45
54
|
- Raw sources for processing → `tavily search` → `tavily extract`
|
|
46
55
|
- Community opinions/alternatives → `xai search` (can build on previous findings with `--context`)
|
|
56
|
+
- Library documentation → `context7 search <lib>` → `context7 context <id> <query>`
|
|
47
57
|
|
|
48
58
|
**Vertex:**
|
|
49
59
|
- Arbitrary Gemini query → `vertex ask`
|
|
@@ -63,6 +73,7 @@ These tools read files directly and pass to external LLMs. Claude only receives
|
|
|
63
73
|
| `TAVILY_API_KEY` | tavily | Tavily API key |
|
|
64
74
|
| `VERTEX_API_KEY` | vertex | Google AI API key (Vertex Express) |
|
|
65
75
|
| `X_AI_API_KEY` | xai | xAI Grok API key |
|
|
76
|
+
| `CONTEXT7_API_KEY` | context7 | Context7 API key (upstash.com) |
|
|
66
77
|
| `ENVOY_TIMEOUT_MS` | optional | Global timeout (default: 120000) |
|
|
67
78
|
|
|
68
79
|
## Discovery
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"version": "0.1.0",
|
|
10
10
|
"dependencies": {
|
|
11
11
|
"@google/genai": "^0.14.0",
|
|
12
|
+
"@upstash/context7-sdk": "^0.3.0",
|
|
12
13
|
"@visheratin/tokenizers-node": "0.1.5",
|
|
13
14
|
"@visheratin/web-ai-node": "^1.4.5",
|
|
14
15
|
"chokidar": "^5.0.0",
|
|
@@ -522,6 +523,12 @@
|
|
|
522
523
|
"pino": "*"
|
|
523
524
|
}
|
|
524
525
|
},
|
|
526
|
+
"node_modules/@upstash/context7-sdk": {
|
|
527
|
+
"version": "0.3.0",
|
|
528
|
+
"resolved": "https://registry.npmjs.org/@upstash/context7-sdk/-/context7-sdk-0.3.0.tgz",
|
|
529
|
+
"integrity": "sha512-kW5UV49mG9hh30sWP7nLq0mF7YHbTtfWrnm1VsT0UFW8mR6ovlYp7anobUh5qOaewSzraq9o2QyY77KVpI1twg==",
|
|
530
|
+
"license": "MIT"
|
|
531
|
+
},
|
|
525
532
|
"node_modules/@visheratin/tokenizers-node": {
|
|
526
533
|
"version": "0.1.5",
|
|
527
534
|
"resolved": "https://registry.npmjs.org/@visheratin/tokenizers-node/-/tokenizers-node-0.1.5.tgz",
|
package/.claude/envoy/src/cli.ts
CHANGED
|
@@ -30,6 +30,7 @@ async function getInfo(
|
|
|
30
30
|
TAVILY_API_KEY: process.env.TAVILY_API_KEY ? "set" : "missing",
|
|
31
31
|
VERTEX_API_KEY: process.env.VERTEX_API_KEY ? "set" : "missing",
|
|
32
32
|
X_AI_API_KEY: process.env.X_AI_API_KEY ? "set" : "missing",
|
|
33
|
+
CONTEXT7_API_KEY: process.env.CONTEXT7_API_KEY ? "set" : "missing",
|
|
33
34
|
},
|
|
34
35
|
timeout_ms: process.env.ENVOY_TIMEOUT_MS ?? "120000",
|
|
35
36
|
},
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context7 API commands - library documentation search and context retrieval.
|
|
3
|
+
*
|
|
4
|
+
* Flow: search (find library) → context (get docs for known library)
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { Command } from "commander";
|
|
8
|
+
import { BaseCommand, type CommandResult } from "./base.js";
|
|
9
|
+
import { Context7, Context7Error, type Library } from "@upstash/context7-sdk";
|
|
10
|
+
|
|
11
|
+
/** Shared base for Context7 commands - DRY for auth + error handling */
|
|
12
|
+
abstract class Context7BaseCommand extends BaseCommand {
|
|
13
|
+
protected requireApiKey(): CommandResult | Context7 {
|
|
14
|
+
const apiKey = process.env.CONTEXT7_API_KEY;
|
|
15
|
+
if (!apiKey) {
|
|
16
|
+
return this.error("auth_error", "CONTEXT7_API_KEY not set");
|
|
17
|
+
}
|
|
18
|
+
return new Context7({ apiKey });
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
protected async withTimeout<T>(fn: () => Promise<T>): Promise<[T, number]> {
|
|
22
|
+
const start = performance.now();
|
|
23
|
+
const timeout = new Promise<never>((_, reject) =>
|
|
24
|
+
setTimeout(() => reject(new Error("timeout")), this.timeoutMs)
|
|
25
|
+
);
|
|
26
|
+
const result = await Promise.race([fn(), timeout]);
|
|
27
|
+
return [result, Math.round(performance.now() - start)];
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected handleError(e: unknown, extraHint?: string): CommandResult {
|
|
31
|
+
if (e instanceof Context7Error) {
|
|
32
|
+
return this.error("api_error", e.message, extraHint);
|
|
33
|
+
}
|
|
34
|
+
if (e instanceof Error && e.message.includes("timeout")) {
|
|
35
|
+
return this.error("timeout", `Request timed out after ${this.timeoutMs}ms`);
|
|
36
|
+
}
|
|
37
|
+
return this.error("api_error", e instanceof Error ? e.message : String(e));
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
class Context7SearchCommand extends Context7BaseCommand {
|
|
42
|
+
readonly name = "search";
|
|
43
|
+
readonly description = "Search for libraries by name, returns IDs for context command";
|
|
44
|
+
|
|
45
|
+
defineArguments(cmd: Command): void {
|
|
46
|
+
cmd
|
|
47
|
+
.argument("<library>", "Library name to search (e.g., react, fastify)")
|
|
48
|
+
.argument("[query]", "Optional query for relevance ranking")
|
|
49
|
+
.option("--limit <n>", "Max results (default: 5)", parseInt);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
53
|
+
const clientOrError = this.requireApiKey();
|
|
54
|
+
if ("status" in clientOrError) return clientOrError;
|
|
55
|
+
|
|
56
|
+
const library = args.library as string;
|
|
57
|
+
const query = (args.query as string) ?? `How to use ${library}`;
|
|
58
|
+
const limit = (args.limit as number) ?? 5;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
const [libraries, durationMs] = await this.withTimeout(() =>
|
|
62
|
+
clientOrError.searchLibrary(query, library)
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
if (!Array.isArray(libraries)) {
|
|
66
|
+
return this.error("api_error", "Unexpected response format from Context7");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const results = libraries.slice(0, limit).map((lib: Library) => ({
|
|
70
|
+
id: lib.id, // Required for context command
|
|
71
|
+
name: lib.name,
|
|
72
|
+
description: lib.description,
|
|
73
|
+
snippets: lib.totalSnippets,
|
|
74
|
+
trust: lib.trustScore,
|
|
75
|
+
}));
|
|
76
|
+
|
|
77
|
+
return this.success(
|
|
78
|
+
{
|
|
79
|
+
query: library,
|
|
80
|
+
results,
|
|
81
|
+
usage: results.length > 0
|
|
82
|
+
? `Use: envoy context7 context "${results[0].id}" "your question"`
|
|
83
|
+
: undefined,
|
|
84
|
+
...(results.length === 0 && {
|
|
85
|
+
suggestion: "Library not found. Try different search term or library may not be indexed.",
|
|
86
|
+
}),
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
result_count: results.length,
|
|
90
|
+
command: "context7 search",
|
|
91
|
+
duration_ms: durationMs,
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
} catch (e) {
|
|
95
|
+
return this.handleError(e);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
class Context7ContextCommand extends Context7BaseCommand {
|
|
101
|
+
readonly name = "context";
|
|
102
|
+
readonly description = "Get documentation context for a known library (use search first)";
|
|
103
|
+
|
|
104
|
+
defineArguments(cmd: Command): void {
|
|
105
|
+
cmd
|
|
106
|
+
.argument("<libraryId>", "Library ID from search (e.g., /facebook/react)")
|
|
107
|
+
.argument("<query>", "What you need docs for (e.g., 'hooks usage')")
|
|
108
|
+
.option("--text", "Return plain text instead of JSON (better for direct LLM use)");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async execute(args: Record<string, unknown>): Promise<CommandResult> {
|
|
112
|
+
const clientOrError = this.requireApiKey();
|
|
113
|
+
if ("status" in clientOrError) return clientOrError;
|
|
114
|
+
|
|
115
|
+
const libraryId = args.libraryId as string;
|
|
116
|
+
const query = args.query as string;
|
|
117
|
+
const useText = args.text as boolean;
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
if (useText) {
|
|
121
|
+
// Plain text mode - directly usable in LLM prompts
|
|
122
|
+
const [content, durationMs] = await this.withTimeout(() =>
|
|
123
|
+
clientOrError.getContext(query, libraryId, { type: "txt" })
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return this.success(
|
|
127
|
+
{
|
|
128
|
+
library: libraryId,
|
|
129
|
+
query,
|
|
130
|
+
content,
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
format: "text",
|
|
134
|
+
command: "context7 context",
|
|
135
|
+
duration_ms: durationMs,
|
|
136
|
+
}
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// JSON mode - structured docs
|
|
141
|
+
const [docs, durationMs] = await this.withTimeout(() =>
|
|
142
|
+
clientOrError.getContext(query, libraryId, { type: "json" })
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (!Array.isArray(docs)) {
|
|
146
|
+
return this.error("api_error", "Unexpected response format from Context7");
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const documentation = docs.map((doc) => ({
|
|
150
|
+
title: doc.title,
|
|
151
|
+
content: doc.content,
|
|
152
|
+
source: doc.source,
|
|
153
|
+
}));
|
|
154
|
+
|
|
155
|
+
return this.success(
|
|
156
|
+
{
|
|
157
|
+
library: libraryId,
|
|
158
|
+
query,
|
|
159
|
+
docs: documentation,
|
|
160
|
+
},
|
|
161
|
+
{
|
|
162
|
+
doc_count: documentation.length,
|
|
163
|
+
command: "context7 context",
|
|
164
|
+
duration_ms: durationMs,
|
|
165
|
+
}
|
|
166
|
+
);
|
|
167
|
+
} catch (e) {
|
|
168
|
+
return this.handleError(e, "Ensure libraryId is valid (from search results)");
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export const COMMANDS = {
|
|
174
|
+
search: Context7SearchCommand,
|
|
175
|
+
context: Context7ContextCommand,
|
|
176
|
+
};
|