claude-teach 0.1.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/LICENSE +21 -0
- package/README.md +184 -0
- package/dist/course.d.ts +4 -0
- package/dist/course.js +78 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +41 -0
- package/dist/lib/auth.d.ts +3 -0
- package/dist/lib/auth.js +93 -0
- package/dist/lib/client.d.ts +11 -0
- package/dist/lib/client.js +100 -0
- package/dist/lib/fs-reader.d.ts +3 -0
- package/dist/lib/fs-reader.js +179 -0
- package/dist/lib/github-reader.d.ts +26 -0
- package/dist/lib/github-reader.js +149 -0
- package/dist/lib/index.d.ts +5 -0
- package/dist/lib/index.js +21 -0
- package/dist/session.d.ts +7 -0
- package/dist/session.js +139 -0
- package/package.json +22 -0
- package/src/course.ts +56 -0
- package/src/index.ts +46 -0
- package/src/lib/auth.ts +56 -0
- package/src/lib/client.ts +123 -0
- package/src/lib/fs-reader.ts +147 -0
- package/src/lib/github-reader.ts +186 -0
- package/src/lib/index.ts +4 -0
- package/src/session.ts +124 -0
- package/tsconfig.json +8 -0
- package/tsconfig.tsbuildinfo +1 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# claude-teach
|
|
2
|
+
|
|
3
|
+
**Ask any question about any codebase and get answers with exact file and line citations — powered by Claude.**
|
|
4
|
+
|
|
5
|
+
[](https://www.npmjs.com/package/claude-teach)
|
|
6
|
+
[](https://www.npmjs.com/package/claude-teach)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
[](https://nodejs.org)
|
|
9
|
+
|
|
10
|
+
`claude-teach` loads an entire codebase into memory and opens an interactive terminal session where you can ask questions about it. Claude answers using the actual code — not general knowledge — and cites exact file paths and line numbers. When you're done, export the whole session as a structured course document.
|
|
11
|
+
|
|
12
|
+

|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Why this exists
|
|
17
|
+
|
|
18
|
+
Understanding an unfamiliar codebase is one of the most time-consuming parts of software engineering. `claude-teach` gives you a guide who has read every file and can answer:
|
|
19
|
+
|
|
20
|
+
- *"How does authentication work in this app?"*
|
|
21
|
+
- *"Walk me through the request lifecycle end to end"*
|
|
22
|
+
- *"What's the correct way to add a new API endpoint here?"*
|
|
23
|
+
- *"Where is rate limiting implemented?"*
|
|
24
|
+
|
|
25
|
+
Every answer cites the real `file:line`. No hallucination. No guessing.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npx claude-teach <target> # run without installing
|
|
33
|
+
npm install -g claude-teach # or install globally
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Requires Node.js 18+ and `ANTHROPIC_API_KEY`:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
export ANTHROPIC_API_KEY=sk-ant-...
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Usage
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
npx claude-teach https://github.com/expressjs/express # GitHub repo
|
|
48
|
+
npx claude-teach expressjs/express # short form
|
|
49
|
+
npx claude-teach . # current directory
|
|
50
|
+
npx claude-teach ~/projects/my-app # local path
|
|
51
|
+
npx claude-teach . --export onboarding-guide.md # export session as doc
|
|
52
|
+
npx claude-teach . --no-stream # disable streaming
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### Session example
|
|
56
|
+
|
|
57
|
+
```
|
|
58
|
+
Loading repository: expressjs/express
|
|
59
|
+
Read 47 files from expressjs/express
|
|
60
|
+
|
|
61
|
+
Ask questions about this codebase. Type "exit" or Ctrl+C to quit.
|
|
62
|
+
|
|
63
|
+
You: how does routing work?
|
|
64
|
+
|
|
65
|
+
Claude: Express routing is built around a Router class in `lib/router/index.js`.
|
|
66
|
+
|
|
67
|
+
When you call `app.get('/path', handler)`, Express calls `router.route(path)`
|
|
68
|
+
(lib/router/index.js:491) which creates a Route object (lib/router/route.js)
|
|
69
|
+
and adds it to the stack.
|
|
70
|
+
|
|
71
|
+
Each request passes through `router.handle()` (lib/router/index.js:136), which
|
|
72
|
+
iterates the Layer stack. Each Layer wraps a middleware function or a Route...
|
|
73
|
+
|
|
74
|
+
You: what's a Layer?
|
|
75
|
+
|
|
76
|
+
Claude: A Layer is defined in `lib/router/layer.js:1`. It wraps:
|
|
77
|
+
- A path pattern (converted to regexp via path-to-regexp)
|
|
78
|
+
- A handler function
|
|
79
|
+
- Match options (case sensitivity, strict mode)
|
|
80
|
+
|
|
81
|
+
The key method is `layer.match(path)` at line 103...
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Options
|
|
87
|
+
|
|
88
|
+
| Flag | Default | Description |
|
|
89
|
+
|---|---|---|
|
|
90
|
+
| `--export <file>` | — | Export full session as a course document on exit |
|
|
91
|
+
| `--no-stream` | streaming on | Disable token-by-token streaming |
|
|
92
|
+
| `--github-token <token>` | `$GITHUB_TOKEN` | Token for private repos / higher rate limits |
|
|
93
|
+
|
|
94
|
+
### Environment variables
|
|
95
|
+
|
|
96
|
+
| Variable | Required | Description |
|
|
97
|
+
|---|---|---|
|
|
98
|
+
| `ANTHROPIC_API_KEY` | Yes | Your Anthropic API key |
|
|
99
|
+
| `GITHUB_TOKEN` | No | Raises GitHub rate limit from 60 to 5,000 req/hr |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## How it works
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
Input (URL or path)
|
|
107
|
+
→ Load repo: fetch files via GitHub API or walk filesystem
|
|
108
|
+
→ Build system prompt: embed all file contents + file index
|
|
109
|
+
→ REPL loop: each question appended to message history, streamed response
|
|
110
|
+
→ On exit with --export: reorganize Q&A into structured course document
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**File prioritization:** When the repo is too large for one context window, files are prioritized by: entry points and configs first, then by directory depth (shallower = higher priority). Up to ~80,000 chars total.
|
|
114
|
+
|
|
115
|
+
**Citations:** Claude is instructed to always reference code as `filename.ts:42`. It traces through actual functions in the loaded files rather than relying on general framework knowledge.
|
|
116
|
+
|
|
117
|
+
**Context window management:** Session history is trimmed to the last 20 turns to prevent context overflow during long sessions.
|
|
118
|
+
|
|
119
|
+
---
|
|
120
|
+
|
|
121
|
+
## The `--export` course document
|
|
122
|
+
|
|
123
|
+
When you quit with `--export course.md`, Claude generates a proper onboarding document from your session:
|
|
124
|
+
|
|
125
|
+
```markdown
|
|
126
|
+
# expressjs/express — Codebase Guide
|
|
127
|
+
|
|
128
|
+
## Introduction
|
|
129
|
+
## Table of Contents
|
|
130
|
+
1. Routing System
|
|
131
|
+
2. Middleware Pipeline
|
|
132
|
+
3. Request / Response Lifecycle
|
|
133
|
+
|
|
134
|
+
## 1. Routing System
|
|
135
|
+
Routing centers on the Router class (`lib/router/index.js`). When you call
|
|
136
|
+
`app.get('/path', fn)`, Express calls `router.route(path)` at line 491...
|
|
137
|
+
|
|
138
|
+
## Summary
|
|
139
|
+
After reading this guide you understand how requests flow through Express...
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Use cases: team onboarding docs, open source contributor guides, personal reference for unfamiliar codebases.
|
|
143
|
+
|
|
144
|
+
---
|
|
145
|
+
|
|
146
|
+
## Questions that work well
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
how does [authentication / caching / queuing] work?
|
|
150
|
+
walk me through a request lifecycle end to end
|
|
151
|
+
what's the difference between [classA] and [classB]?
|
|
152
|
+
how do I add a new [endpoint / command / model]?
|
|
153
|
+
what does [FileName.ts] do and why does it exist?
|
|
154
|
+
which files would I need to change to add [feature]?
|
|
155
|
+
what are the most important files to understand first?
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Cost
|
|
161
|
+
|
|
162
|
+
Typical 10–15 question session on a medium repo: **~$0.10–0.30**. Export adds ~$0.05–0.10.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Troubleshooting
|
|
167
|
+
|
|
168
|
+
**"I don't have information about that file"** — Repo too large to fully load. Ask about core/entry-point files which are always prioritized.
|
|
169
|
+
|
|
170
|
+
**GitHub rate limit errors** — `npx claude-teach owner/repo --github-token $(gh auth token)`
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## Contributing
|
|
175
|
+
|
|
176
|
+
```bash
|
|
177
|
+
git clone https://github.com/amritessh/claude-teach && cd claude-teach
|
|
178
|
+
npm install && npm run dev
|
|
179
|
+
node dist/index.js .
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## License
|
|
183
|
+
|
|
184
|
+
MIT
|
package/dist/course.d.ts
ADDED
package/dist/course.js
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.exportCourse = exportCourse;
|
|
37
|
+
const lib_1 = require("./lib");
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
async function exportCourse(ctx, turns, outputPath) {
|
|
40
|
+
if (turns.length === 0) {
|
|
41
|
+
console.log("No session turns to export.");
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const sessionTranscript = turns
|
|
45
|
+
.map((t, i) => `## Q${i + 1}: ${t.question}\n\n${t.answer}`)
|
|
46
|
+
.join("\n\n---\n\n");
|
|
47
|
+
const prompt = `You are creating a structured course document from a Q&A session about the codebase "${ctx.owner}/${ctx.repo}".
|
|
48
|
+
|
|
49
|
+
Here is the Q&A session transcript:
|
|
50
|
+
|
|
51
|
+
${sessionTranscript}
|
|
52
|
+
|
|
53
|
+
Convert this into a well-structured course document with:
|
|
54
|
+
|
|
55
|
+
# ${ctx.owner}/${ctx.repo} — Codebase Guide
|
|
56
|
+
|
|
57
|
+
## Introduction
|
|
58
|
+
Brief intro to what this codebase does and what a developer will learn from this guide.
|
|
59
|
+
|
|
60
|
+
## Table of Contents
|
|
61
|
+
Numbered list of sections based on the topics covered in the Q&A.
|
|
62
|
+
|
|
63
|
+
## [Section for each major topic covered]
|
|
64
|
+
Reorganize and expand the Q&A content into coherent sections with:
|
|
65
|
+
- Clear explanations
|
|
66
|
+
- Code examples (preserve the file:line citations)
|
|
67
|
+
- Key takeaways at the end of each section
|
|
68
|
+
|
|
69
|
+
## Summary
|
|
70
|
+
What a developer now knows after reading this guide and next steps.
|
|
71
|
+
|
|
72
|
+
Write the course document only. Make it genuinely useful for onboarding a new developer.`;
|
|
73
|
+
console.log("\nGenerating course document...");
|
|
74
|
+
const courseContent = await (0, lib_1.generateText)("You are an expert technical writer creating developer onboarding documentation.", prompt, 4096);
|
|
75
|
+
fs.writeFileSync(outputPath, courseContent, "utf-8");
|
|
76
|
+
console.log(`Course exported to: ${outputPath}`);
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=course.js.map
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const lib_1 = require("./lib");
|
|
6
|
+
const session_1 = require("./session");
|
|
7
|
+
const course_1 = require("./course");
|
|
8
|
+
const program = new commander_1.Command();
|
|
9
|
+
program
|
|
10
|
+
.name("claude-teach")
|
|
11
|
+
.description("Interactive Q&A about any codebase, powered by Claude")
|
|
12
|
+
.version("0.1.0")
|
|
13
|
+
.argument("<source>", "GitHub URL or local path")
|
|
14
|
+
.option("--export <file>", "Export session as a course document when you quit")
|
|
15
|
+
.option("--no-stream", "Disable streaming output")
|
|
16
|
+
.option("--github-token <token>", "GitHub token for private/large repos")
|
|
17
|
+
.action(async (source, opts) => {
|
|
18
|
+
try {
|
|
19
|
+
console.log(`Loading repository: ${source}`);
|
|
20
|
+
const isGitHub = source.startsWith("https://github.com") ||
|
|
21
|
+
source.startsWith("github.com") ||
|
|
22
|
+
/^[a-zA-Z0-9_-]+\/[a-zA-Z0-9_.-]+$/.test(source);
|
|
23
|
+
const ctx = isGitHub
|
|
24
|
+
? await (0, lib_1.readGitHubRepo)(source, opts.githubToken ?? (0, lib_1.resolveGitHubToken)())
|
|
25
|
+
: await (0, lib_1.readLocalRepo)(source);
|
|
26
|
+
const turns = await (0, session_1.runSession)(ctx, opts.export, opts.stream !== false);
|
|
27
|
+
if (opts.export && turns.length > 0) {
|
|
28
|
+
await (0, course_1.exportCourse)(ctx, turns, opts.export);
|
|
29
|
+
}
|
|
30
|
+
console.log("Session ended.");
|
|
31
|
+
}
|
|
32
|
+
catch (err) {
|
|
33
|
+
if (err.code === "ERR_USE_AFTER_CLOSE") {
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
console.error("Error:", err instanceof Error ? err.message : String(err));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
program.parse();
|
|
41
|
+
//# sourceMappingURL=index.js.map
|
package/dist/lib/auth.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolveAnthropicKey = resolveAnthropicKey;
|
|
37
|
+
exports.resolveGitHubToken = resolveGitHubToken;
|
|
38
|
+
const fs = __importStar(require("fs"));
|
|
39
|
+
const path = __importStar(require("path"));
|
|
40
|
+
const os = __importStar(require("os"));
|
|
41
|
+
function readEnvFile(filePath) {
|
|
42
|
+
if (!fs.existsSync(filePath))
|
|
43
|
+
return {};
|
|
44
|
+
const lines = fs.readFileSync(filePath, "utf-8").split("\n");
|
|
45
|
+
const result = {};
|
|
46
|
+
for (const line of lines) {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (!trimmed || trimmed.startsWith("#"))
|
|
49
|
+
continue;
|
|
50
|
+
const eq = trimmed.indexOf("=");
|
|
51
|
+
if (eq === -1)
|
|
52
|
+
continue;
|
|
53
|
+
const key = trimmed.slice(0, eq).trim();
|
|
54
|
+
const val = trimmed.slice(eq + 1).trim().replace(/^["']|["']$/g, "");
|
|
55
|
+
result[key] = val;
|
|
56
|
+
}
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
59
|
+
function resolveAnthropicKey() {
|
|
60
|
+
if (process.env.ANTHROPIC_API_KEY)
|
|
61
|
+
return process.env.ANTHROPIC_API_KEY;
|
|
62
|
+
const envFile = readEnvFile(path.join(process.cwd(), ".env"));
|
|
63
|
+
if (envFile.ANTHROPIC_API_KEY)
|
|
64
|
+
return envFile.ANTHROPIC_API_KEY;
|
|
65
|
+
const credFile = path.join(os.homedir(), ".anthropic", "credentials");
|
|
66
|
+
if (fs.existsSync(credFile)) {
|
|
67
|
+
const creds = readEnvFile(credFile);
|
|
68
|
+
if (creds.ANTHROPIC_API_KEY)
|
|
69
|
+
return creds.ANTHROPIC_API_KEY;
|
|
70
|
+
}
|
|
71
|
+
throw new Error("ANTHROPIC_API_KEY not found.\n" +
|
|
72
|
+
"Set it via:\n" +
|
|
73
|
+
" export ANTHROPIC_API_KEY=sk-ant-...\n" +
|
|
74
|
+
" or add it to .env in your current directory");
|
|
75
|
+
}
|
|
76
|
+
function resolveGitHubToken() {
|
|
77
|
+
if (process.env.GITHUB_TOKEN)
|
|
78
|
+
return process.env.GITHUB_TOKEN;
|
|
79
|
+
const envFile = readEnvFile(path.join(process.cwd(), ".env"));
|
|
80
|
+
if (envFile.GITHUB_TOKEN)
|
|
81
|
+
return envFile.GITHUB_TOKEN;
|
|
82
|
+
try {
|
|
83
|
+
const { execSync } = require("child_process");
|
|
84
|
+
const token = execSync("gh auth token 2>/dev/null", { encoding: "utf-8" }).trim();
|
|
85
|
+
if (token)
|
|
86
|
+
return token;
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
// gh CLI not available or not logged in
|
|
90
|
+
}
|
|
91
|
+
return undefined;
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
2
|
+
export declare function generateText(system: string, prompt: string, maxTokens?: number): Promise<string>;
|
|
3
|
+
export declare function streamText(system: string, prompt: string, maxTokens?: number): AsyncGenerator<string>;
|
|
4
|
+
export type MessageParam = Anthropic.MessageParam;
|
|
5
|
+
export type Tool = Anthropic.Tool;
|
|
6
|
+
export interface ToolLoopResult {
|
|
7
|
+
finalText: string;
|
|
8
|
+
iterations: number;
|
|
9
|
+
}
|
|
10
|
+
export declare function runToolLoop(system: string, initialPrompt: string, tools: Tool[], toolExecutor: (name: string, input: Record<string, unknown>) => Promise<unknown>, maxIterations?: number): Promise<ToolLoopResult>;
|
|
11
|
+
//# sourceMappingURL=client.d.ts.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.generateText = generateText;
|
|
7
|
+
exports.streamText = streamText;
|
|
8
|
+
exports.runToolLoop = runToolLoop;
|
|
9
|
+
const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
|
|
10
|
+
const auth_1 = require("./auth");
|
|
11
|
+
let _client = null;
|
|
12
|
+
function getClient() {
|
|
13
|
+
if (!_client) {
|
|
14
|
+
_client = new sdk_1.default({ apiKey: (0, auth_1.resolveAnthropicKey)() });
|
|
15
|
+
}
|
|
16
|
+
return _client;
|
|
17
|
+
}
|
|
18
|
+
const DEFAULT_MODEL = "claude-opus-4-5";
|
|
19
|
+
async function generateText(system, prompt, maxTokens = 4096) {
|
|
20
|
+
const client = getClient();
|
|
21
|
+
const response = await client.messages.create({
|
|
22
|
+
model: DEFAULT_MODEL,
|
|
23
|
+
max_tokens: maxTokens,
|
|
24
|
+
system,
|
|
25
|
+
messages: [{ role: "user", content: prompt }],
|
|
26
|
+
});
|
|
27
|
+
const block = response.content[0];
|
|
28
|
+
if (block.type !== "text")
|
|
29
|
+
throw new Error("Unexpected response type");
|
|
30
|
+
return block.text;
|
|
31
|
+
}
|
|
32
|
+
async function* streamText(system, prompt, maxTokens = 4096) {
|
|
33
|
+
const client = getClient();
|
|
34
|
+
const stream = client.messages.stream({
|
|
35
|
+
model: DEFAULT_MODEL,
|
|
36
|
+
max_tokens: maxTokens,
|
|
37
|
+
system,
|
|
38
|
+
messages: [{ role: "user", content: prompt }],
|
|
39
|
+
});
|
|
40
|
+
for await (const event of stream) {
|
|
41
|
+
if (event.type === "content_block_delta" &&
|
|
42
|
+
event.delta.type === "text_delta") {
|
|
43
|
+
yield event.delta.text;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
async function runToolLoop(system, initialPrompt, tools, toolExecutor, maxIterations = 20) {
|
|
48
|
+
const client = getClient();
|
|
49
|
+
const messages = [
|
|
50
|
+
{ role: "user", content: initialPrompt },
|
|
51
|
+
];
|
|
52
|
+
let iterations = 0;
|
|
53
|
+
let finalText = "";
|
|
54
|
+
while (iterations < maxIterations) {
|
|
55
|
+
iterations++;
|
|
56
|
+
const response = await client.messages.create({
|
|
57
|
+
model: DEFAULT_MODEL,
|
|
58
|
+
max_tokens: 8096,
|
|
59
|
+
system,
|
|
60
|
+
tools,
|
|
61
|
+
messages,
|
|
62
|
+
});
|
|
63
|
+
const assistantContent = response.content;
|
|
64
|
+
messages.push({ role: "assistant", content: assistantContent });
|
|
65
|
+
if (response.stop_reason === "end_turn") {
|
|
66
|
+
const textBlock = assistantContent.find((b) => b.type === "text");
|
|
67
|
+
if (textBlock && textBlock.type === "text")
|
|
68
|
+
finalText = textBlock.text;
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
if (response.stop_reason === "tool_use") {
|
|
72
|
+
const toolResults = [];
|
|
73
|
+
for (const block of assistantContent) {
|
|
74
|
+
if (block.type !== "tool_use")
|
|
75
|
+
continue;
|
|
76
|
+
try {
|
|
77
|
+
const result = await toolExecutor(block.name, block.input);
|
|
78
|
+
toolResults.push({
|
|
79
|
+
type: "tool_result",
|
|
80
|
+
tool_use_id: block.id,
|
|
81
|
+
content: typeof result === "string" ? result : JSON.stringify(result),
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
catch (err) {
|
|
85
|
+
toolResults.push({
|
|
86
|
+
type: "tool_result",
|
|
87
|
+
tool_use_id: block.id,
|
|
88
|
+
content: `Error: ${err instanceof Error ? err.message : String(err)}`,
|
|
89
|
+
is_error: true,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
messages.push({ role: "user", content: toolResults });
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
break;
|
|
97
|
+
}
|
|
98
|
+
return { finalText, iterations };
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=client.js.map
|