explainthisrepo 0.5.0 → 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.
Files changed (3) hide show
  1. package/README.md +62 -27
  2. package/dist/cli.js +60 -25
  3. package/package.json +3 -2
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
@@ -6,6 +6,7 @@ import { readFileSync } from "node:fs";
6
6
  import path from "node:path";
7
7
  import { fileURLToPath } from "node:url";
8
8
  import { Command } from "commander";
9
+ import ora from "ora";
9
10
  import { fetchRepo, fetchReadme } from "./github.js";
10
11
  import { buildPrompt, buildQuickPrompt, buildSimplePrompt } from "./prompt.js";
11
12
  import { generateExplanation } from "./generate.js";
@@ -191,20 +192,32 @@ Examples:
191
192
  console.error(`error: ${message}`);
192
193
  process.exit(1);
193
194
  }
194
- console.log(`Fetching ${owner}/${repo}...`);
195
195
  }
196
196
  if (options.stack) {
197
197
  let read;
198
198
  let languages = {};
199
199
  if (local) {
200
- read = readLocalRepoSignalFiles(localPath);
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
+ }
201
211
  }
202
212
  else {
213
+ const spinner = ora(`Fetching ${owner}/${repo}…`).start();
203
214
  try {
204
215
  languages = await fetchLanguages(owner, repo);
205
216
  read = await readRepoSignalFiles(owner, repo);
217
+ spinner.stop();
206
218
  }
207
219
  catch (e) {
220
+ spinner.stop();
208
221
  const message = e instanceof Error ? e.message : String(e);
209
222
  console.error(`error: ${message}`);
210
223
  process.exit(1);
@@ -223,10 +236,14 @@ Examples:
223
236
  let repoData = null;
224
237
  let readme = null;
225
238
  if (!local) {
239
+ const spinner = ora(`Fetching ${owner}/${repo}…`).start();
226
240
  try {
227
241
  repoData = await fetchRepo(owner, repo);
242
+ readme = await fetchReadme(owner, repo);
243
+ spinner.stop();
228
244
  }
229
245
  catch (e) {
246
+ spinner.stop();
230
247
  const message = e instanceof Error ? e.message : String(e);
231
248
  console.error("Failed to fetch repository data.");
232
249
  console.error(`error: ${message}`);
@@ -235,48 +252,66 @@ Examples:
235
252
  console.error("- Or set GITHUB_TOKEN to avoid rate limits");
236
253
  process.exit(1);
237
254
  }
238
- try {
239
- readme = await fetchReadme(owner, repo);
240
- }
241
- catch (e) {
242
- const message = e instanceof Error ? e.message : String(e);
243
- console.warn(`Warning: Could not fetch README: ${message}`);
244
- readme = null;
245
- }
246
255
  }
247
256
  if (options.quick) {
248
257
  let quickReadme = readme;
249
258
  const repoName = local ? localPath : (repoData?.full_name ?? "");
250
259
  const description = local ? null : (repoData?.description ?? null);
251
260
  if (local) {
252
- const read = readLocalRepoSignalFiles(localPath);
253
- const readmeKey = Object.keys(read.keyFiles).find((k) => k.toLowerCase().startsWith("readme"));
254
- quickReadme = readmeKey !== undefined ? read.keyFiles[readmeKey] : null;
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
+ }
255
272
  }
256
273
  const prompt = buildQuickPrompt(repoName, description, quickReadme);
257
- console.log("Generating explanation...");
258
- const output = await generateWithExit(prompt);
274
+ const spinner = ora("Generating explanation").start();
275
+ const output = await generateWithExit(prompt).finally(() => spinner.stop());
259
276
  console.log("Quick summary 🎉");
260
277
  console.log(output.trim());
261
278
  return;
262
279
  }
263
280
  if (options.simple) {
264
- const readResult = local
265
- ? readLocalRepoSignalFiles(localPath)
266
- : await safeReadRepoFiles(owner, repo);
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
+ }
267
293
  const prompt = buildSimplePrompt(local ? localPath : (repoData?.full_name ?? ""), local ? null : (repoData?.description ?? null), local ? null : readme, readResult?.treeText ?? null);
268
- console.log("Generating explanation...");
269
- const output = await generateWithExit(prompt);
294
+ const genSpinner = ora("Generating explanation").start();
295
+ const output = await generateWithExit(prompt).finally(() => genSpinner.stop());
270
296
  console.log("Simple summary 🎉");
271
297
  console.log(output.trim());
272
298
  return;
273
299
  }
274
- const readResult = local
275
- ? readLocalRepoSignalFiles(localPath)
276
- : await safeReadRepoFiles(owner, repo);
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
+ }
277
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);
278
- console.log("Generating explanation...");
279
- const output = await generateWithExit(prompt);
313
+ const genSpinner = ora("Generating explanation").start();
314
+ const output = await generateWithExit(prompt).finally(() => genSpinner.stop());
280
315
  console.log("Writing EXPLAIN.md...");
281
316
  writeOutput(output);
282
317
  const wordCount = output.split(/\s+/).filter(Boolean).length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "explainthisrepo",
3
- "version": "0.5.0",
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",