explainthisrepo 0.4.4 → 0.5.1

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 CHANGED
@@ -1,8 +1,8 @@
1
1
  # ExplainThisRepo
2
2
 
3
- ExplainThisRepo is a CLI that generates plain-English explanations of public GitHub repositories by analyzing repository structure, README content, and selected high signal files.
3
+ ExplainThisRepo is a CLI that generates plain-English explanations of public GitHub repositories and local directories by analyzing project structure, README content, and high signal files.
4
4
 
5
- It's helps developers understand unfamiliar repositories does by generating a structured `EXPLAIN.md` from real
5
+ It helps developers quickly understand unfamiliar codebases by deriving architectural explanations from real project structure and code signals, producing a clear, structured `EXPLAIN.md`.
6
6
 
7
7
  [![PyPI Version](https://img.shields.io/pypi/v/explainthisrepo?color=blue)](https://pypi.org/project/explainthisrepo/)
8
8
  [![PyPI Downloads](https://static.pepy.tech/personalized-badge/explainthisrepo?period=total&units=INTERNATIONAL_SYSTEM&left_color=BLACK&right_color=GREEN&left_text=downloads)](https://pepy.tech/projects/explainthisrepo)
@@ -19,15 +19,15 @@ It's helps developers understand unfamiliar repositories does by generating a st
19
19
 
20
20
  ## Key Features
21
21
 
22
- - Understand unfamiliar repositories instantly through structural and architechural summaries by turning structure and code signals into a readable architectural summary
23
- - Fetches public GitHub repositories automatically
24
- - Analyzes real repository data including file tree, configs, entrypoints, and high signal source files
22
+ - Generates architectural summaries from repository structure and code signals
23
+ - Fetches public repositories by GitHub URLs (with or without https), `owner/repo` format, issue links, query strings, and SSH clone links
24
+ - Analyzes repository data including file tree, configs, entrypoints, and high signal source files
25
25
  - Extracts repo signals from key files (package.json, pyproject.toml, config files, entrypoints)
26
26
  - Builds a file tree summary to understand project architecture
27
- - Detects programming languages via the GitHub API
28
- - Accepts repositories via owner/repo, GitHub URLs (with or without https), issue links, query strings, and SSH clone links
27
+ - Detects programming languages with the GitHub API
28
+ - Analyzes local project directories using the same pipeline as GitHub repositories
29
29
  - Generates a structured plain English explanation grounded in actual project files
30
- - Outputs an EXPLAIN.md file in your current directory (default mode)
30
+ - Outputs the explanation to an `EXPLAIN.md` file in your current directory or print it directly in the terminal
31
31
  - Multi mode command-line interface
32
32
 
33
33
  ---
@@ -38,13 +38,13 @@ It's helps developers understand unfamiliar repositories does by generating a st
38
38
 
39
39
  - `--quick` → One-sentence summary
40
40
 
41
- - `--simple` → Short, easy explanation
41
+ - `--simple` → Short, simplified explanation
42
42
 
43
43
  - `--detailed` → Deeper explanation including structure and architecture
44
44
 
45
45
  - `--stack` → Tech stack breakdown from repo signals
46
46
 
47
- - `--version` → Show CLI version
47
+ - `--version` → Check installed CLI version
48
48
 
49
49
  - `--help` → Show usage guide
50
50
 
@@ -56,9 +56,9 @@ It's helps developers understand unfamiliar repositories does by generating a st
56
56
 
57
57
  ExplainThisRepo uses Gemini models for code analysis.
58
58
 
59
- Set your API key as an environment variable.
59
+ Set your Google Gemini API key as an environment variable.
60
60
 
61
- macOS / Linux
61
+ Linux / macOS
62
62
 
63
63
  ```bash
64
64
  export GEMINI_API_KEY="your_api_key_here"
@@ -74,7 +74,7 @@ Restart your terminal after setting the key.
74
74
 
75
75
  ## Installation
76
76
 
77
- ### Option 1: install via pip (recommended):
77
+ ### Option 1: install with pip (recommended):
78
78
 
79
79
  Requirements: Python 3.9+
80
80
 
@@ -99,29 +99,32 @@ explainthisrepo owner/repo
99
99
  # or: npx explainthisrepo owner/repo
100
100
  ```
101
101
 
102
+ Replace `owner/repo` with the GitHub repository identifier (e.g., `facebook/react`).
103
+
102
104
  ---
103
105
 
104
- ## Flexible Repository Input
106
+ ## Flexible Repository and Local Directory Input
105
107
 
106
- You don’t need to reformat links anymore.
108
+ Accepts various formats for repository input, full GitHub URLs, issue links, and SSH clone links.
107
109
 
108
- ExplainThisRepo accepts GitHub repositories the way you actually copy them.
109
110
  ```bash
110
111
  explainthisrepo https://github.com/owner/repo
111
112
  explainthisrepo github.com/owner/repo
112
113
  explainthisrepo https://github.com/owner/repo/issues/123
113
114
  explainthisrepo https://github.com/owner/repo?tab=readme
114
115
  explainthisrepo git@github.com:owner/repo.git
116
+ explainthisrepo .
117
+ explainthisrepo ./path/to/directory
115
118
  ```
116
119
 
117
- All inputs are normalized internally to owner/repo.
120
+ All inputs are normalized internally to `owner/repo`.
118
121
 
119
122
  ---
120
123
 
121
124
  ## Usage
122
125
 
123
126
  ### Basic
124
- Generate a full explanation and saves it to `EXPLAIN.md`:
127
+ Writes a full explanation to `EXPLAIN.md`:
125
128
 
126
129
  ```bash
127
130
  explainthisrepo owner/repo
@@ -134,7 +137,8 @@ explainthisrepo facebook/react
134
137
 
135
138
  ### Quick mode
136
139
 
137
- Get a one-sentence definition (prints only, no file created):
140
+ Prints a one-sentence summary to stdout:
141
+
138
142
  ```bash
139
143
  explainthisrepo owner/repo --quick
140
144
  ```
@@ -148,7 +152,8 @@ explainthisrepo facebook/react --quick
148
152
 
149
153
  ### Detailed mode
150
154
 
151
- Generate a more detailed explanation (includes architecture / folder structure):
155
+ Writes a more detailed explanation of repository structure and architecture:
156
+
152
157
  ```bash
153
158
  explainthisrepo owner/repo --detailed
154
159
  ```
@@ -159,7 +164,8 @@ explainthisrepo owner/repo --detailed
159
164
 
160
165
  ### Simple mode
161
166
 
162
- Prints only the simple output (no EXPLAIN.md)
167
+ Prints a short, simplified explanation to stdout. No files are written.
168
+
163
169
  ```bash
164
170
  explainthisrepo owner/repo --simple
165
171
  ```
@@ -170,15 +176,43 @@ explainthisrepo owner/repo --simple
170
176
 
171
177
  ### Stack detector
172
178
 
173
- Get a tech stack breakdown detected from repo signals. No AI explanation. Prints only.
179
+ Tech stack breakdown detected from repo signals. No LLM calls are made.
180
+
174
181
  ```bash
175
182
  explainthisrepo owner/repo --stack
176
183
  ```
177
184
  ![Stack detector Output](assets/stack-command-output.png)
178
185
 
186
+ ### Local Directory Analysis
187
+
188
+ ExplainThisRepo can analyze local directories directly in the terminal, using the same modes and output formats as GitHub repositories
189
+
190
+ ```bash
191
+ explainthisrepo .
192
+ explainthisrepo ./path/to/directory
193
+ ```
194
+
195
+ This works with all existing modes:
196
+
197
+ ```bash
198
+ explainthisrepo . --quick
199
+ explainthisrepo . --simple
200
+ explainthisrepo . --detailed
201
+ explainthisrepo . --stack
202
+ ```
203
+
204
+ When analyzing a local directory:
205
+ - Repository structure is derived from the filesystem
206
+ - Key files (README, configs, entrypoints) are extracted locally
207
+ - No GitHub APIs calls are made
208
+ - All prompts and outputs remain identical
209
+
210
+ This allows analysis of projects directly from the local filesystem, without requiring a GitHub repository.
211
+
179
212
  ### Version
180
213
 
181
- Print the installed version:
214
+ Print the installed CLI version:
215
+
182
216
  ```bash
183
217
  explainthisrepo --version
184
218
  ```
@@ -187,12 +221,13 @@ explainthisrepo --version
187
221
 
188
222
  ### Doctor
189
223
 
190
- Check environment + connectivity (useful for debugging):
224
+ Check environment and connectivity (useful for debugging):
225
+
191
226
  ```bash
192
227
  explainthisrepo --doctor
193
228
  ```
194
229
 
195
- ## Termux (Android) install notes
230
+ ### Termux (Android) install notes
196
231
 
197
232
  Termux has some environment limitations that can make `pip install explainthisrepo` fail to create the `explainthisrepo` command in `$PREFIX/bin`.
198
233
 
@@ -216,7 +251,7 @@ If you do not want to modify PATH, you can run ExplainThisRepo as a module:
216
251
  python -m explain_this_repo owner/repo
217
252
  ```
218
253
 
219
- Gemini support on Termux (Optional)
254
+ ### Gemini support on Termux (Optional)
220
255
 
221
256
  Installing Gemini support may require building Rust-based dependencies on Android, which can take time on first install:
222
257
 
@@ -247,4 +282,4 @@ Caleb Wodi
247
282
 
248
283
  - Email: caleb@explainthisrepo.com
249
284
  - Twitter: [@calchiwo](https://x.com/calchiwo)
250
- - LinkedIn: [@calchiwo](https://linkedin.com/in/calchiwo)
285
+ - LinkedIn: [@calchiwo](https://linkedin.com/in/calchiwo)
package/dist/cli.js CHANGED
@@ -1,15 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import os from "node:os";
3
3
  import process from "node:process";
4
+ import fs from "node:fs";
4
5
  import { readFileSync } from "node:fs";
5
6
  import path from "node:path";
6
7
  import { fileURLToPath } from "node:url";
7
8
  import { Command } from "commander";
9
+ import ora from "ora";
8
10
  import { fetchRepo, fetchReadme } from "./github.js";
9
11
  import { buildPrompt, buildQuickPrompt, buildSimplePrompt } from "./prompt.js";
10
12
  import { generateExplanation } from "./generate.js";
11
13
  import { writeOutput } from "./writer.js";
12
14
  import { readRepoSignalFiles } from "./repo_reader.js";
15
+ import { readLocalRepoSignalFiles } from "./local_reader.js";
13
16
  import { fetchLanguages } from "./github.js";
14
17
  import { detectStack } from "./stack-detector.js";
15
18
  import { printStack } from "./stack_printer.js";
@@ -22,8 +25,8 @@ function resolveRepoTarget(target) {
22
25
  target = target.replace("http//", "http://");
23
26
  }
24
27
  if (target.startsWith("git@github.com:")) {
25
- const path = target.replace("git@github.com:", "");
26
- const [owner, repoRaw] = path.split("/", 2);
28
+ const p = target.replace("git@github.com:", "");
29
+ const [owner, repoRaw] = p.split("/", 2);
27
30
  if (!owner || !repoRaw)
28
31
  throw new Error("Invalid GitHub SSH URL");
29
32
  return { owner, repo: repoRaw.replace(/\.git$/, "") };
@@ -83,10 +86,7 @@ async function checkUrl(url, timeoutMs = 6000) {
83
86
  clearTimeout(t);
84
87
  const message = e instanceof Error ? e.message : String(e);
85
88
  const name = e instanceof Error ? e.name : "Error";
86
- return {
87
- ok: false,
88
- msg: `failed (${name}: ${message})`,
89
- };
89
+ return { ok: false, msg: `failed (${name}: ${message})` };
90
90
  }
91
91
  }
92
92
  async function runDoctor() {
@@ -135,7 +135,7 @@ async function main() {
135
135
  .name("explainthisrepo")
136
136
  .description("Explain GitHub repositories in plain English")
137
137
  .version(getPkgVersion(), "-v, --version", "Show version")
138
- .argument("[repository]", "GitHub repository (owner/repo or URL)")
138
+ .argument("[repository]", "GitHub repository (owner/repo or URL) or local path")
139
139
  .option("--doctor", "Run diagnostics")
140
140
  .option("--quick", "Quick summary mode")
141
141
  .option("--simple", "Simple summary mode")
@@ -151,6 +151,9 @@ Examples:
151
151
  $ explainthisrepo owner/repo --quick
152
152
  $ explainthisrepo owner/repo --simple
153
153
  $ explainthisrepo owner/repo --stack
154
+ $ explainthisrepo .
155
+ $ explainthisrepo ./path/to/directory
156
+ $ explainthisrepo . --stack
154
157
  $ explainthisrepo --doctor`);
155
158
  program.parse(process.argv);
156
159
  const options = program.opts();
@@ -172,27 +175,17 @@ Examples:
172
175
  if (!repository) {
173
176
  program.error("repository argument required");
174
177
  }
175
- let owner, repo;
176
- try {
177
- ({ owner, repo } = resolveRepoTarget(repository));
178
- }
179
- catch (e) {
180
- const message = e instanceof Error ? e.message : String(e);
181
- console.error(`error: ${message}`);
182
- process.exit(1);
178
+ const local = fs.existsSync(repository);
179
+ let owner = "";
180
+ let repo = "";
181
+ let localPath = "";
182
+ if (local) {
183
+ localPath = path.resolve(repository);
184
+ console.log(`Analyzing local directory: ${repository}`);
183
185
  }
184
- console.log(`Fetching ${owner}/${repo}...`);
185
- if (options.stack) {
186
+ else {
186
187
  try {
187
- const languages = await fetchLanguages(owner, repo);
188
- const read = await readRepoSignalFiles(owner, repo);
189
- const report = detectStack({
190
- languages,
191
- tree: read.tree,
192
- keyFiles: read.keyFiles,
193
- });
194
- printStack(report, owner, repo);
195
- return;
188
+ ({ owner, repo } = resolveRepoTarget(repository));
196
189
  }
197
190
  catch (e) {
198
191
  const message = e instanceof Error ? e.message : String(e);
@@ -200,49 +193,125 @@ Examples:
200
193
  process.exit(1);
201
194
  }
202
195
  }
203
- let repoData;
204
- try {
205
- repoData = await fetchRepo(owner, repo);
206
- }
207
- catch (e) {
208
- const message = e instanceof Error ? e.message : String(e);
209
- console.error("Failed to fetch repository data.");
210
- console.error(`error: ${message}`);
211
- console.error("\nfix:");
212
- console.error("- Ensure the repository exists and is public");
213
- console.error("- Or set GITHUB_TOKEN to avoid rate limits");
214
- process.exit(1);
196
+ if (options.stack) {
197
+ let read;
198
+ let languages = {};
199
+ if (local) {
200
+ const spinner = ora("Reading repository files…").start();
201
+ try {
202
+ read = readLocalRepoSignalFiles(localPath);
203
+ spinner.stop();
204
+ }
205
+ catch (e) {
206
+ spinner.stop();
207
+ const message = e instanceof Error ? e.message : String(e);
208
+ console.error(`error: ${message}`);
209
+ process.exit(1);
210
+ }
211
+ }
212
+ else {
213
+ const spinner = ora(`Fetching ${owner}/${repo}…`).start();
214
+ try {
215
+ languages = await fetchLanguages(owner, repo);
216
+ read = await readRepoSignalFiles(owner, repo);
217
+ spinner.stop();
218
+ }
219
+ catch (e) {
220
+ spinner.stop();
221
+ const message = e instanceof Error ? e.message : String(e);
222
+ console.error(`error: ${message}`);
223
+ process.exit(1);
224
+ }
225
+ }
226
+ const report = detectStack({
227
+ languages,
228
+ tree: read.tree,
229
+ keyFiles: read.keyFiles,
230
+ });
231
+ const label = local ? repository : owner;
232
+ const sublabel = local ? "" : repo;
233
+ printStack(report, label, sublabel);
234
+ return;
215
235
  }
236
+ let repoData = null;
216
237
  let readme = null;
217
- try {
218
- readme = await fetchReadme(owner, repo);
219
- }
220
- catch (e) {
221
- const message = e instanceof Error ? e.message : String(e);
222
- console.warn(`Warning: Could not fetch README: ${message}`);
223
- readme = null;
238
+ if (!local) {
239
+ const spinner = ora(`Fetching ${owner}/${repo}…`).start();
240
+ try {
241
+ repoData = await fetchRepo(owner, repo);
242
+ readme = await fetchReadme(owner, repo);
243
+ spinner.stop();
244
+ }
245
+ catch (e) {
246
+ spinner.stop();
247
+ const message = e instanceof Error ? e.message : String(e);
248
+ console.error("Failed to fetch repository data.");
249
+ console.error(`error: ${message}`);
250
+ console.error("\nfix:");
251
+ console.error("- Ensure the repository exists and is public");
252
+ console.error("- Or set GITHUB_TOKEN to avoid rate limits");
253
+ process.exit(1);
254
+ }
224
255
  }
225
256
  if (options.quick) {
226
- const prompt = buildQuickPrompt(repoData.full_name, repoData.description, readme);
227
- console.log("Generating explanation...");
228
- const output = await generateWithExit(prompt);
257
+ let quickReadme = readme;
258
+ const repoName = local ? localPath : (repoData?.full_name ?? "");
259
+ const description = local ? null : (repoData?.description ?? null);
260
+ if (local) {
261
+ const spinner = ora("Reading repository files…").start();
262
+ try {
263
+ const read = readLocalRepoSignalFiles(localPath);
264
+ spinner.stop();
265
+ const readmeKey = Object.keys(read.keyFiles).find((k) => k.toLowerCase().startsWith("readme"));
266
+ quickReadme = readmeKey !== undefined ? read.keyFiles[readmeKey] : null;
267
+ }
268
+ catch (e) {
269
+ spinner.stop();
270
+ throw e;
271
+ }
272
+ }
273
+ const prompt = buildQuickPrompt(repoName, description, quickReadme);
274
+ const spinner = ora("Generating explanation…").start();
275
+ const output = await generateWithExit(prompt).finally(() => spinner.stop());
229
276
  console.log("Quick summary 🎉");
230
277
  console.log(output.trim());
231
278
  return;
232
279
  }
233
280
  if (options.simple) {
234
- const readResult = await safeReadRepoFiles(owner, repo);
235
- const prompt = buildSimplePrompt(repoData.full_name, repoData.description, readme, readResult?.treeText ?? null);
236
- console.log("Generating explanation...");
237
- const output = await generateWithExit(prompt);
281
+ let readResult;
282
+ const spinner = ora("Reading repository files…").start();
283
+ try {
284
+ readResult = local
285
+ ? readLocalRepoSignalFiles(localPath)
286
+ : await safeReadRepoFiles(owner, repo);
287
+ spinner.stop();
288
+ }
289
+ catch (e) {
290
+ spinner.stop();
291
+ throw e;
292
+ }
293
+ const prompt = buildSimplePrompt(local ? localPath : (repoData?.full_name ?? ""), local ? null : (repoData?.description ?? null), local ? null : readme, readResult?.treeText ?? null);
294
+ const genSpinner = ora("Generating explanation…").start();
295
+ const output = await generateWithExit(prompt).finally(() => genSpinner.stop());
238
296
  console.log("Simple summary 🎉");
239
297
  console.log(output.trim());
240
298
  return;
241
299
  }
242
- const readResult = await safeReadRepoFiles(owner, repo);
243
- const prompt = buildPrompt(repoData.full_name, repoData.description, readme, options.detailed || false, readResult?.treeText ?? null, readResult?.filesText ?? null);
244
- console.log("Generating explanation...");
245
- const output = await generateWithExit(prompt);
300
+ let readResult;
301
+ const readSpinner = ora("Reading repository files…").start();
302
+ try {
303
+ readResult = local
304
+ ? readLocalRepoSignalFiles(localPath)
305
+ : await safeReadRepoFiles(owner, repo);
306
+ readSpinner.stop();
307
+ }
308
+ catch (e) {
309
+ readSpinner.stop();
310
+ throw e;
311
+ }
312
+ 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);
313
+ const genSpinner = ora("Generating explanation…").start();
314
+ const output = await generateWithExit(prompt).finally(() => genSpinner.stop());
246
315
  console.log("Writing EXPLAIN.md...");
247
316
  writeOutput(output);
248
317
  const wordCount = output.split(/\s+/).filter(Boolean).length;
@@ -0,0 +1,2 @@
1
+ import { type RepoReadResult } from "./repo_reader.js";
2
+ export declare function readLocalRepoSignalFiles(dirPath: string): RepoReadResult;
@@ -0,0 +1,77 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ const KEY_FILENAMES = new Set([
4
+ "readme.md", "readme.txt", "readme.rst", "readme",
5
+ "package.json", "pyproject.toml", "setup.py", "setup.cfg",
6
+ "requirements.txt", "cargo.toml", "go.mod", "pom.xml",
7
+ "build.gradle", "composer.json", "gemfile", "makefile",
8
+ "dockerfile", "docker-compose.yml", "docker-compose.yaml",
9
+ ".env.example", "tsconfig.json", "angular.json", "next.config.js",
10
+ "vite.config.js", "vite.config.ts", "webpack.config.js",
11
+ ]);
12
+ const SKIP_DIRS = new Set([
13
+ ".git", ".hg", ".svn", "node_modules", "__pycache__",
14
+ ".venv", "venv", "env", ".env", "dist", "build",
15
+ ".idea", ".vscode", ".mypy_cache", ".pytest_cache",
16
+ "coverage", ".coverage", "htmlcov",
17
+ ]);
18
+ const MAX_FILE_BYTES = 32_000;
19
+ const MAX_KEY_FILES = 12;
20
+ function walkDir(root, dir, tree, keyFiles) {
21
+ let entries;
22
+ try {
23
+ entries = fs.readdirSync(dir, { withFileTypes: true });
24
+ }
25
+ catch {
26
+ return;
27
+ }
28
+ const dirs = [];
29
+ const files = [];
30
+ for (const entry of entries) {
31
+ if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
32
+ dirs.push(entry);
33
+ }
34
+ else if (entry.isFile()) {
35
+ files.push(entry);
36
+ }
37
+ }
38
+ dirs.sort((a, b) => a.name.localeCompare(b.name));
39
+ files.sort((a, b) => a.name.localeCompare(b.name));
40
+ for (const file of files) {
41
+ const absPath = path.join(dir, file.name);
42
+ const relPath = path.relative(root, absPath).replace(/\\/g, "/");
43
+ tree.push({ path: relPath, type: "blob" });
44
+ if (KEY_FILENAMES.has(file.name.toLowerCase()) &&
45
+ Object.keys(keyFiles).length < MAX_KEY_FILES) {
46
+ try {
47
+ const fd = fs.openSync(absPath, "r");
48
+ const buf = Buffer.alloc(MAX_FILE_BYTES);
49
+ const bytesRead = fs.readSync(fd, buf, 0, MAX_FILE_BYTES, 0);
50
+ fs.closeSync(fd);
51
+ keyFiles[relPath] = buf.subarray(0, bytesRead).toString("utf8");
52
+ }
53
+ catch {
54
+ }
55
+ }
56
+ }
57
+ for (const subdir of dirs) {
58
+ walkDir(root, path.join(dir, subdir.name), tree, keyFiles);
59
+ }
60
+ }
61
+ export function readLocalRepoSignalFiles(dirPath) {
62
+ const root = path.resolve(dirPath);
63
+ const tree = [];
64
+ const keyFiles = {};
65
+ walkDir(root, root, tree, keyFiles);
66
+ const treeText = tree.map((item) => item.path).join("\n");
67
+ const filesText = Object.entries(keyFiles)
68
+ .map(([relPath, content]) => `### ${relPath}\n${content}`)
69
+ .join("\n\n");
70
+ return {
71
+ tree,
72
+ treeText,
73
+ keyFiles,
74
+ filesText,
75
+ selectedFiles: Object.keys(keyFiles),
76
+ };
77
+ }
@@ -1,4 +1,4 @@
1
- type TreeItem = {
1
+ export type TreeItem = {
2
2
  path: string;
3
3
  type: "blob" | "tree";
4
4
  size?: number;
@@ -11,4 +11,3 @@ export type RepoReadResult = {
11
11
  keyFiles: Record<string, string>;
12
12
  };
13
13
  export declare function readRepoSignalFiles(owner: string, repo: string): Promise<RepoReadResult>;
14
- export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "explainthisrepo",
3
- "version": "0.4.4",
3
+ "version": "0.5.1",
4
4
  "description": "A CLI developer tool to explain any GitHub repository in plain English",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,7 +47,8 @@
47
47
  "@google/generative-ai": "^0.24.1",
48
48
  "axios": "^1.13.2",
49
49
  "commander": "^14.0.3",
50
- "dotenv": "^17.2.3"
50
+ "dotenv": "^17.2.3",
51
+ "ora": "^9.3.0"
51
52
  },
52
53
  "devDependencies": {
53
54
  "@types/node": "^22.0.0",