explainthisrepo 0.9.6 → 0.20.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.
Files changed (42) hide show
  1. package/README.md +55 -14
  2. package/dist/cli.js +39 -371
  3. package/dist/native/win-x64/explainthisrepo.exe +0 -0
  4. package/package.json +2 -9
  5. package/dist/config.d.ts +0 -5
  6. package/dist/config.js +0 -46
  7. package/dist/generate.d.ts +0 -1
  8. package/dist/generate.js +0 -15
  9. package/dist/github.d.ts +0 -6
  10. package/dist/github.js +0 -128
  11. package/dist/init.d.ts +0 -1
  12. package/dist/init.js +0 -174
  13. package/dist/local_reader.d.ts +0 -2
  14. package/dist/local_reader.js +0 -77
  15. package/dist/prompt.d.ts +0 -3
  16. package/dist/prompt.js +0 -127
  17. package/dist/providers/anthropic.d.ts +0 -17
  18. package/dist/providers/anthropic.js +0 -68
  19. package/dist/providers/base.d.ts +0 -8
  20. package/dist/providers/base.js +0 -6
  21. package/dist/providers/gemini.d.ts +0 -16
  22. package/dist/providers/gemini.js +0 -47
  23. package/dist/providers/groq.d.ts +0 -17
  24. package/dist/providers/groq.js +0 -70
  25. package/dist/providers/index.d.ts +0 -1
  26. package/dist/providers/index.js +0 -1
  27. package/dist/providers/ollama.d.ts +0 -15
  28. package/dist/providers/ollama.js +0 -82
  29. package/dist/providers/openai.d.ts +0 -17
  30. package/dist/providers/openai.js +0 -57
  31. package/dist/providers/openrouter.d.ts +0 -17
  32. package/dist/providers/openrouter.js +0 -72
  33. package/dist/providers/registry.d.ts +0 -4
  34. package/dist/providers/registry.js +0 -38
  35. package/dist/repo_reader.d.ts +0 -13
  36. package/dist/repo_reader.js +0 -148
  37. package/dist/stack-detector.d.ts +0 -23
  38. package/dist/stack-detector.js +0 -54
  39. package/dist/stack_printer.d.ts +0 -2
  40. package/dist/stack_printer.js +0 -20
  41. package/dist/writer.d.ts +0 -1
  42. package/dist/writer.js +0 -6
package/README.md CHANGED
@@ -20,6 +20,7 @@ ExplainThisRepo analyzes real project signals; configs, entrypoints, manifests,
20
20
  - Derives architectural summaries from repository structure and code signals.
21
21
  Not blind AI summarization.
22
22
  - Translates complex code structures into plain English
23
+ - Speeds up understanding of unfamiliar codebases
23
24
  - Extract architecture signals from configs, entrypoints, and manifests
24
25
  - Works with GitHub repositories, local directories, private repositories, and monorepos
25
26
  - Outputs the explanation to an `EXPLAIN.md` file in your current directory or prints it directly in the terminal
@@ -27,7 +28,7 @@ Not blind AI summarization.
27
28
 
28
29
  ## Installation
29
30
 
30
- ### Option 1: install with pip (recommended):
31
+ ### Option 1: install with pip (Python source version):
31
32
 
32
33
  Requirements: Python 3.9+
33
34
 
@@ -46,6 +47,14 @@ pipx install explainthisrepo
46
47
  explainthisrepo owner/repo
47
48
  ```
48
49
 
50
+ After installation, use any of the available commands:
51
+
52
+ ```bash
53
+ explainthisrepo owner/repo
54
+ explain-this-repo owner/repo
55
+ etr owner/repo
56
+ ```
57
+
49
58
  To install support for specific models:
50
59
 
51
60
  ```bash
@@ -55,7 +64,9 @@ pip install explainthisrepo[anthropic]
55
64
  pip install explainthisrepo[groq]
56
65
  ```
57
66
 
58
- ### Option 2: Install with npm
67
+ Replace `owner/repo` with the GitHub repository identifier (e.g., `facebook/react`, `torvalds/linux`).
68
+
69
+ ### Option 2: Install with npm (prebuilt binary, no Python required)
59
70
 
60
71
  Install globally and use forever:
61
72
 
@@ -71,9 +82,19 @@ Or without install:
71
82
 
72
83
  ```bash
73
84
  npx explainthisrepo owner/repo
85
+
86
+ # npx explainthisrepo .
74
87
  ```
75
88
 
76
- Replace `owner/repo` with the GitHub repository identifier (e.g., `facebook/react`).
89
+ ## How it works
90
+
91
+ ExplainThisRepo uses a hybrid architecture:
92
+
93
+ - Python → core implementaion (analysis, prompts, providers, output)
94
+ - npm → ships prebuilt native binaries (no Python required)
95
+ - pip → installs the full Python package
96
+
97
+ > The npm and pip versions run the same core engine.
77
98
 
78
99
  ### Option 3: Download standalone binary
79
100
 
@@ -124,8 +145,22 @@ explainthisrepo init
124
145
  # or npx explainthisrepo init
125
146
  ```
126
147
 
127
- > For details about how initialization works, see [INIT.md](INIT.md).
148
+ > For details about how initialization works, see [docs/INIT.md](docs/INIT.md).
149
+
150
+ ## GitHub token Access (Private Repos & Rate Limits)
151
+
152
+ ExplainThisRepo supports GitHub authentication for:
153
+
154
+ - Accessing private repositories
155
+ - Higher API rate limits on public repositories
156
+
157
+ Run:
158
+
159
+ ```bash
160
+ explainthisrepo init
161
+ ```
128
162
 
163
+ For step-by-step instructions, see [docs/GITHUB_TOKEN.md](docs/GITHUB_TOKEN.md)
129
164
 
130
165
  ## Flag options
131
166
 
@@ -147,8 +182,6 @@ explainthisrepo init
147
182
 
148
183
  - `--llm` → Override provider selection
149
184
 
150
- - `--token/-t` → Set GitHub token for private repositories and to avoid rate limits
151
-
152
185
  ## Flexible Repository and Local Directory Input
153
186
 
154
187
  Accepts various formats for repository input, full GitHub URLs (with or without https), `owner/repo` format, issue links, query strings, and SSH clone links
@@ -165,6 +198,22 @@ explainthisrepo ./path/to/directory
165
198
 
166
199
  All inputs are normalized internally to `owner/repo`.
167
200
 
201
+ ## CLI aliases
202
+
203
+ ExplainThisRepo ships with multiple command names that all map to the same entrypoint:
204
+
205
+ - `explainthisrepo` → primary command
206
+ - `explain-this-repo` → readable alias
207
+ - `etr` → short alias for faster typing
208
+
209
+ All three commands run the same tool and support the same flags and modes.
210
+
211
+ ```bash
212
+ explainthisrepo owner/repo
213
+ explain-this-repo owner/repo
214
+ etr owner/repo
215
+ ```
216
+
168
217
  ## Model selection
169
218
 
170
219
  The `--llm` flag selects which configured model backend to use for the current command.
@@ -269,14 +318,6 @@ When analyzing a local directory:
269
318
 
270
319
  This allows analysis of projects directly from the local filesystem, without requiring a GitHub repository.
271
320
 
272
- ### For private repositories, use the --token/-t option.
273
-
274
- Setting a `GITHUB_TOKEN` environment variable is recommended to avoid rate limits when analyzing public repositories.
275
-
276
- ```bash
277
- export GITHUB_TOKEN=yourActualTokenHere
278
- ```
279
-
280
321
  ### Version
281
322
 
282
323
  Check the installed CLI version:
package/dist/cli.js CHANGED
@@ -1,383 +1,51 @@
1
1
  #!/usr/bin/env node
2
- import os from "node:os";
3
- import process from "node:process";
4
2
  import fs from "node:fs";
5
- import { readFileSync } from "node:fs";
3
+ import { spawnSync } from "node:child_process";
6
4
  import path from "node:path";
5
+ import process from "node:process";
7
6
  import { fileURLToPath } from "node:url";
8
- import { Command } from "commander";
9
- import ora from "ora";
10
- import { runInit } from "./init.js";
11
- import { fetchRepo, fetchReadme } from "./github.js";
12
- import { buildPrompt, buildQuickPrompt, buildSimplePrompt } from "./prompt.js";
13
- import { generateExplanation } from "./generate.js";
14
- import { writeOutput } from "./writer.js";
15
- import { readRepoSignalFiles } from "./repo_reader.js";
16
- import { readLocalRepoSignalFiles } from "./local_reader.js";
17
- import { fetchLanguages } from "./github.js";
18
- import { detectStack } from "./stack-detector.js";
19
- import { printStack } from "./stack_printer.js";
20
- function resolveRepoTarget(target) {
21
- target = target.trim();
22
- if (target.startsWith("https//")) {
23
- target = target.replace("https//", "https://");
24
- }
25
- if (target.startsWith("http//")) {
26
- target = target.replace("http//", "http://");
27
- }
28
- if (target.startsWith("git@github.com:")) {
29
- const p = target.replace("git@github.com:", "");
30
- const [owner, repoRaw] = p.split("/", 2);
31
- if (!owner || !repoRaw)
32
- throw new Error("Invalid GitHub SSH URL");
33
- return { owner, repo: repoRaw.replace(/\.git$/, "") };
34
- }
35
- if (target.startsWith("github.com/")) {
36
- target = "https://" + target;
37
- }
38
- if (target.startsWith("http://") || target.startsWith("https://")) {
39
- const url = new URL(target);
40
- if (!["github.com", "www.github.com"].includes(url.hostname)) {
41
- throw new Error("Only GitHub repository URLs are supported");
42
- }
43
- const parts = url.pathname.split("/").filter(Boolean);
44
- if (parts.length < 2) {
45
- throw new Error("URL must point to a repository");
46
- }
47
- return { owner: parts[0], repo: parts[1].replace(/\.git$/, "") };
48
- }
49
- const parts = target.split("/");
50
- if (parts.length === 2 && parts[0] && parts[1]) {
51
- return { owner: parts[0], repo: parts[1] };
52
- }
53
- throw new Error("Invalid format. Use owner/repo or a GitHub repo URL");
54
- }
55
- function getPkgVersion() {
56
- try {
57
- const __filename = fileURLToPath(import.meta.url);
58
- const __dirname = path.dirname(__filename);
59
- const pkgPath = path.join(__dirname, "..", "package.json");
60
- const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
61
- return pkg.version ?? "unknown";
62
- }
63
- catch {
64
- return "unknown";
65
- }
66
- }
67
- function hasEnv(key) {
68
- const v = process.env[key];
69
- return Boolean(v && v.trim());
70
- }
71
- async function checkUrl(url, timeoutMs = 6000) {
72
- const controller = new AbortController();
73
- const t = setTimeout(() => controller.abort(), timeoutMs);
7
+ function getTargetKey() {
8
+ const platform = process.platform;
9
+ const arch = process.arch;
10
+ if (platform === "darwin" && arch === "x64")
11
+ return "darwin-x64";
12
+ if (platform === "darwin" && arch === "arm64")
13
+ return "darwin-arm64";
14
+ if (platform === "linux" && arch === "x64")
15
+ return "linux-x64";
16
+ if (platform === "linux" && arch === "arm64")
17
+ return "linux-arm64";
18
+ if (platform === "win32" && arch === "x64")
19
+ return "win-x64";
20
+ throw new Error(`Unsupported platform: ${platform} ${arch}`);
21
+ }
22
+ function getBinaryName() {
23
+ return process.platform === "win32" ? "explainthisrepo.exe" : "explainthisrepo";
24
+ }
25
+ function getBinaryPath() {
26
+ const launcherDir = path.dirname(fileURLToPath(import.meta.url));
27
+ const binaryPath = path.join(launcherDir, "native", getTargetKey(), getBinaryName());
28
+ if (!fs.existsSync(binaryPath)) {
29
+ throw new Error(`Bundled binary not found: ${binaryPath}`);
30
+ }
31
+ return binaryPath;
32
+ }
33
+ function main() {
74
34
  try {
75
- const res = await fetch(url, {
76
- method: "GET",
77
- headers: { "User-Agent": "explainthisrepo" },
78
- signal: controller.signal,
35
+ const binaryPath = getBinaryPath();
36
+ const result = spawnSync(binaryPath, process.argv.slice(2), {
37
+ stdio: "inherit",
38
+ windowsHide: true,
79
39
  });
80
- clearTimeout(t);
81
- return { ok: res.ok, msg: `ok (${res.status})` };
82
- }
83
- catch (e) {
84
- clearTimeout(t);
85
- const message = e instanceof Error ? e.message : String(e);
86
- const name = e instanceof Error ? e.name : "Error";
87
- return { ok: false, msg: `failed (${name}: ${message})` };
88
- }
89
- }
90
- async function runDoctor(llmOverride) {
91
- console.log("explainthisrepo doctor report\n");
92
- console.log(`node: ${process.version}`);
93
- console.log(`os: ${os.type()} ${os.release()}`);
94
- console.log(`platform: ${process.platform} ${process.arch}`);
95
- console.log(`version: ${getPkgVersion()}`);
96
- console.log("\nenvironment:");
97
- console.log(`- GITHUB_TOKEN set: ${hasEnv("GITHUB_TOKEN")}`);
98
- console.log("\nnetwork checks:");
99
- const gh = await checkUrl("https://api.github.com");
100
- console.log(`- github api: ${gh.msg}`);
101
- console.log("\nprovider diagnostics:");
102
- let providerOk = true;
103
- try {
104
- const { getActiveProvider } = await import("./providers/registry.js");
105
- const provider = await getActiveProvider(llmOverride);
106
- const providerName = provider.name ?? llmOverride ?? "unknown";
107
- console.log(`- active provider: ${providerName}`);
108
- const doctorFn = provider.doctor;
109
- if (typeof doctorFn === "function") {
110
- const result = await doctorFn.call(provider);
111
- if (typeof result === "boolean") {
112
- console.log(`- ${providerName}: ${result ? "ok" : "checks did not pass"}`);
113
- providerOk = result;
114
- }
115
- else if (Array.isArray(result)) {
116
- if (result.length === 0) {
117
- console.log(`- ${providerName}: ok`);
118
- }
119
- else {
120
- for (const line of result) {
121
- console.log(`- ${providerName}: ${line}`);
122
- }
123
- providerOk = false;
124
- }
125
- }
126
- else {
127
- console.log(`- ${providerName}: ok`);
128
- }
129
- }
130
- else {
131
- console.log(`- ${providerName}: no diagnostics implemented`);
40
+ if (result.error) {
41
+ throw result.error;
132
42
  }
43
+ process.exit(result.status ?? 1);
133
44
  }
134
- catch (e) {
135
- const message = e instanceof Error ? e.message : String(e);
136
- if (llmOverride) {
137
- console.log(`- provider '${llmOverride}' could not be resolved`);
138
- console.log("- check that the provider name is correct and properly configured");
139
- }
140
- else {
141
- console.log(`- provider registry error: ${message}`);
142
- console.log("- run `explainthisrepo init` to configure a provider");
143
- }
144
- providerOk = false;
145
- }
146
- return gh.ok && providerOk ? 0 : 1;
147
- }
148
- async function safeReadRepoFiles(owner, repo) {
149
- try {
150
- return await readRepoSignalFiles(owner, repo);
151
- }
152
- catch (e) {
153
- const message = e instanceof Error ? e.message : String(e);
154
- console.warn(`Warning: Could not read repo files: ${message}`);
155
- return null;
156
- }
157
- }
158
- async function generateWithExit(prompt, llm) {
159
- try {
160
- return await generateExplanation(prompt, llm);
161
- }
162
- catch (e) {
163
- const message = e instanceof Error ? e.message : String(e);
164
- console.error("Failed to generate explanation.");
45
+ catch (error) {
46
+ const message = error instanceof Error ? error.message : String(error);
165
47
  console.error(`error: ${message}`);
166
- console.error("\nfix:");
167
- console.error("- Check that the provider name is correct (e.g. gemini, openai, ollama)");
168
- console.error("- Ensure your API key is set for the selected provider");
169
- console.error("- Or run: explainthisrepo --doctor");
170
- process.exit(1);
171
- }
172
- }
173
- async function runAnalysis(repository, options) {
174
- if (options.doctor) {
175
- const code = await runDoctor(options.llm);
176
- process.exit(code);
177
- }
178
- const modeFlags = [
179
- options.quick,
180
- options.simple,
181
- options.detailed,
182
- options.stack,
183
- ].filter(Boolean);
184
- if (modeFlags.length > 1) {
185
- console.error("error: only one mode flag can be used at a time");
186
48
  process.exit(1);
187
49
  }
188
- const local = fs.existsSync(repository);
189
- let owner = "";
190
- let repo = "";
191
- let localPath = "";
192
- if (local) {
193
- localPath = path.resolve(repository);
194
- console.log(`Analyzing local directory: ${repository}`);
195
- }
196
- else {
197
- try {
198
- ({ owner, repo } = resolveRepoTarget(repository));
199
- }
200
- catch (e) {
201
- const message = e instanceof Error ? e.message : String(e);
202
- console.error(`error: ${message}`);
203
- process.exit(1);
204
- }
205
- }
206
- if (options.stack) {
207
- let read;
208
- let languages = {};
209
- if (local) {
210
- const spinner = ora("Reading repository files…").start();
211
- try {
212
- read = readLocalRepoSignalFiles(localPath);
213
- spinner.stop();
214
- }
215
- catch (e) {
216
- spinner.stop();
217
- const message = e instanceof Error ? e.message : String(e);
218
- console.error(`error: ${message}`);
219
- process.exit(1);
220
- }
221
- }
222
- else {
223
- const spinner = ora(`Fetching ${owner}/${repo}…`).start();
224
- try {
225
- languages = await fetchLanguages(owner, repo);
226
- read = await readRepoSignalFiles(owner, repo);
227
- spinner.stop();
228
- }
229
- catch (e) {
230
- spinner.stop();
231
- const message = e instanceof Error ? e.message : String(e);
232
- console.error(`error: ${message}`);
233
- process.exit(1);
234
- }
235
- }
236
- const report = detectStack({
237
- languages,
238
- tree: read.tree,
239
- keyFiles: read.keyFiles,
240
- });
241
- const label = local ? repository : owner;
242
- const sublabel = local ? "" : repo;
243
- printStack(report, label, sublabel);
244
- return;
245
- }
246
- let repoData = null;
247
- let readme = null;
248
- if (!local) {
249
- const spinner = ora(`Fetching ${owner}/${repo}…`).start();
250
- try {
251
- repoData = await fetchRepo(owner, repo);
252
- readme = await fetchReadme(owner, repo);
253
- spinner.stop();
254
- }
255
- catch (e) {
256
- spinner.stop();
257
- const message = e instanceof Error ? e.message : String(e);
258
- console.error("Failed to fetch repository data.");
259
- console.error(`error: ${message}`);
260
- console.error("\nfix:");
261
- console.error("- Ensure the repository exists and is public");
262
- console.error("- Or set GITHUB_TOKEN to avoid rate limits");
263
- process.exit(1);
264
- }
265
- }
266
- if (options.quick) {
267
- let quickReadme = readme;
268
- const repoName = local ? localPath : (repoData?.full_name ?? "");
269
- const description = local
270
- ? null
271
- : (repoData?.description ?? null);
272
- if (local) {
273
- const spinner = ora("Reading repository files…").start();
274
- try {
275
- const read = readLocalRepoSignalFiles(localPath);
276
- spinner.stop();
277
- const readmeKey = Object.keys(read.keyFiles).find((k) => k.toLowerCase().startsWith("readme"));
278
- quickReadme = readmeKey !== undefined ? read.keyFiles[readmeKey] : null;
279
- }
280
- catch (e) {
281
- spinner.stop();
282
- throw e;
283
- }
284
- }
285
- const prompt = buildQuickPrompt(repoName, description, quickReadme);
286
- const spinner = ora("Generating explanation…").start();
287
- const output = await generateWithExit(prompt, options.llm).finally(() => spinner.stop());
288
- console.log("Quick summary 🎉");
289
- console.log(output.trim());
290
- return;
291
- }
292
- if (options.simple) {
293
- let readResult;
294
- const spinner = ora("Reading repository files…").start();
295
- try {
296
- readResult = local
297
- ? readLocalRepoSignalFiles(localPath)
298
- : await safeReadRepoFiles(owner, repo);
299
- spinner.stop();
300
- }
301
- catch (e) {
302
- spinner.stop();
303
- throw e;
304
- }
305
- const prompt = buildSimplePrompt(local ? localPath : (repoData?.full_name ?? ""), local ? null : (repoData?.description ?? null), local ? null : readme, readResult?.treeText ?? null);
306
- const genSpinner = ora("Generating explanation…").start();
307
- const output = await generateWithExit(prompt, options.llm).finally(() => genSpinner.stop());
308
- console.log("Simple summary 🎉");
309
- console.log(output.trim());
310
- return;
311
- }
312
- let readResult;
313
- const readSpinner = ora("Reading repository files…").start();
314
- try {
315
- readResult = local
316
- ? readLocalRepoSignalFiles(localPath)
317
- : await safeReadRepoFiles(owner, repo);
318
- readSpinner.stop();
319
- }
320
- catch (e) {
321
- readSpinner.stop();
322
- throw e;
323
- }
324
- const prompt = buildPrompt(local ? localPath : (repoData?.full_name ?? ""), local ? null : (repoData?.description ?? null), local ? null : readme, options.detailed || false, readResult?.treeText ?? null, readResult?.filesText ?? null);
325
- const genSpinner = ora("Generating explanation…").start();
326
- const output = await generateWithExit(prompt, options.llm).finally(() => genSpinner.stop());
327
- console.log("Writing EXPLAIN.md...");
328
- writeOutput(output);
329
- const wordCount = output.split(/\s+/).filter(Boolean).length;
330
- console.log("EXPLAIN.md generated successfully 🎉");
331
- console.log(`Words: ${wordCount}`);
332
- console.log("Open EXPLAIN.md to read it.");
333
50
  }
334
- const program = new Command();
335
- program
336
- .name("explainthisrepo")
337
- .description("CLI that generates plain English explanations of any codebase")
338
- .version(getPkgVersion(), "-v, --version", "Show version")
339
- .argument("[repository]", "GitHub repository (owner/repo or URL) or local directories")
340
- .option("--doctor", "Run diagnostics")
341
- .option("--quick", "Quick summary mode")
342
- .option("--simple", "Simple summary mode")
343
- .option("--detailed", "Detailed explanation mode")
344
- .option("--stack", "Stack detection mode")
345
- .option("--llm <provider>", "LLM provider to use (e.g. gemini, openai, ollama). Overrides config default.")
346
- .addHelpText("after", `
347
- Examples:
348
- $ explainthisrepo owner/repo
349
- $ explainthisrepo https://github.com/owner/repo
350
- $ explainthisrepo github.com/owner/repo
351
- $ explainthisrepo git@github.com:owner/repo.git
352
- $ explainthisrepo owner/repo --detailed
353
- $ explainthisrepo owner/repo --quick
354
- $ explainthisrepo owner/repo --simple
355
- $ explainthisrepo owner/repo --stack
356
- $ explainthisrepo owner/repo --llm gemini
357
- $ explainthisrepo owner/repo --llm openai
358
- $ explainthisrepo owner/repo --llm ollama
359
- $ explainthisrepo .
360
- $ explainthisrepo ./path/to/directory
361
- $ explainthisrepo . --stack
362
- $ explainthisrepo --doctor
363
- $ explainthisrepo --doctor --llm gemini
364
- $ explainthisrepo --doctor --llm openai
365
- $ explainthisrepo --doctor --llm ollama`)
366
- .action(async (repository, options) => {
367
- if (options.doctor) {
368
- const code = await runDoctor(options.llm);
369
- process.exit(code);
370
- }
371
- if (!repository) {
372
- program.error("repository argument required (or use `explainthisrepo init` to configure a provider)");
373
- return;
374
- }
375
- await runAnalysis(repository, options);
376
- });
377
- program
378
- .command("init")
379
- .description("Configure your LLM provider (Gemini, OpenAI, or Ollama)")
380
- .action(async () => {
381
- await runInit();
382
- });
383
- program.parseAsync(process.argv);
51
+ main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "explainthisrepo",
3
- "version": "0.9.6",
3
+ "version": "0.20.0",
4
4
  "description": "The fastest way to understand any codebase in plain English. Not blind AI summarization",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -46,14 +46,7 @@
46
46
  "node": ">=20"
47
47
  },
48
48
  "dependencies": {
49
- "@google/generative-ai": "^0.24.1",
50
- "@iarna/toml": "^2.2.5",
51
- "axios": "^1.13.2",
52
- "commander": "^14.0.3",
53
- "dotenv": "^17.2.3",
54
- "openai": "^4.0.0",
55
- "ora": "^9.3.0",
56
- "toml": "^3.0.0"
49
+ "commander": "^14.0.3"
57
50
  },
58
51
  "devDependencies": {
59
52
  "@types/node": "^22.0.0",
package/dist/config.d.ts DELETED
@@ -1,5 +0,0 @@
1
- export declare function getConfigPath(): string;
2
- export declare function ensureConfigDir(): string;
3
- export declare function writeConfig(contents: string): void;
4
- export declare function readConfig(): string | null;
5
- export declare function loadConfig(): any;
package/dist/config.js DELETED
@@ -1,46 +0,0 @@
1
- import fs from "node:fs";
2
- import os from "node:os";
3
- import path from "node:path";
4
- import { parse } from "@iarna/toml";
5
- const CONFIG_DIR_NAME = "ExplainThisRepo";
6
- const CONFIG_FILE_NAME = "config.toml";
7
- export function getConfigPath() {
8
- if (process.platform === "win32") {
9
- const appdata = process.env.APPDATA;
10
- if (!appdata) {
11
- throw new Error("APPDATA environment variable is not set");
12
- }
13
- return path.join(appdata, CONFIG_DIR_NAME, CONFIG_FILE_NAME);
14
- }
15
- const xdg = process.env.XDG_CONFIG_HOME;
16
- const base = xdg ?? path.join(os.homedir(), ".config");
17
- return path.join(base, "explainthisrepo", CONFIG_FILE_NAME);
18
- }
19
- export function ensureConfigDir() {
20
- const configPath = getConfigPath();
21
- const dir = path.dirname(configPath);
22
- fs.mkdirSync(dir, { recursive: true });
23
- return configPath;
24
- }
25
- export function writeConfig(contents) {
26
- const path = ensureConfigDir();
27
- fs.writeFileSync(path, contents, { encoding: "utf-8" });
28
- }
29
- export function readConfig() {
30
- const path = getConfigPath();
31
- if (!fs.existsSync(path))
32
- return null;
33
- return fs.readFileSync(path, "utf-8");
34
- }
35
- export function loadConfig() {
36
- const raw = readConfig();
37
- if (!raw) {
38
- return {};
39
- }
40
- try {
41
- return parse(raw);
42
- }
43
- catch (err) {
44
- throw new Error("Invalid config.toml format");
45
- }
46
- }
@@ -1 +0,0 @@
1
- export declare function generateExplanation(prompt: string, providerOverride?: string): Promise<string>;
package/dist/generate.js DELETED
@@ -1,15 +0,0 @@
1
- import { getActiveProvider } from "./providers/registry.js";
2
- export async function generateExplanation(prompt, providerOverride) {
3
- const provider = getActiveProvider(providerOverride);
4
- try {
5
- const output = await provider.generate(prompt);
6
- if (!output || !output.trim()) {
7
- throw new Error(`${provider.name} returned no output`);
8
- }
9
- return output.trim();
10
- }
11
- catch (err) {
12
- const message = err?.message ? String(err.message) : String(err);
13
- throw new Error(`${provider.name} generation failed: ${message}`);
14
- }
15
- }
package/dist/github.d.ts DELETED
@@ -1,6 +0,0 @@
1
- export declare function fetchRepo(owner: string, repo: string): Promise<any>;
2
- export declare function fetchReadme(owner: string, repo: string): Promise<string | null>;
3
- export declare function fetchTree(owner: string, repo: string): Promise<any[]>;
4
- export declare function fetchFile(owner: string, repo: string, filePath: string): Promise<string>;
5
- export type RepoLanguageMap = Record<string, number>;
6
- export declare function fetchLanguages(owner: string, repo: string): Promise<RepoLanguageMap>;