explainthisrepo 0.1.3 → 0.1.5

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/dist/cli.js CHANGED
@@ -5,15 +5,19 @@ import { readFileSync } from "node:fs";
5
5
  import path from "node:path";
6
6
  import { fileURLToPath } from "node:url";
7
7
  import { fetchRepo, fetchReadme } from "./github.js";
8
- import { buildPrompt } from "./prompt.js";
8
+ import { buildPrompt, buildSimplePrompt } from "./prompt.js";
9
9
  import { generateExplanation } from "./generate.js";
10
10
  import { writeOutput } from "./writer.js";
11
11
  import { readRepoSignalFiles } from "./repo_reader.js";
12
+ import { fetchLanguages } from "./github.js";
13
+ import { detectStack } from "./stack-detector.js";
14
+ import { printStack } from "./stack_printer.js";
12
15
  function usage() {
13
16
  console.log("usage:");
14
17
  console.log(" explainthisrepo owner/repo");
15
18
  console.log(" explainthisrepo owner/repo --detailed");
16
19
  console.log(" explainthisrepo owner/repo --quick");
20
+ console.log(" explainthisrepo owner/repo --simple");
17
21
  console.log(" explainthisrepo --doctor");
18
22
  console.log(" explainthisrepo --version");
19
23
  }
@@ -88,11 +92,17 @@ async function main() {
88
92
  }
89
93
  let detailed = false;
90
94
  let quick = false;
95
+ let simple = false;
96
+ let stack = false;
91
97
  if (args.length === 2) {
92
98
  if (args[1] === "--detailed")
93
99
  detailed = true;
94
100
  else if (args[1] === "--quick")
95
101
  quick = true;
102
+ else if (args[1] === "--simple")
103
+ simple = true;
104
+ else if (args[1] === "--stack")
105
+ stack = true;
96
106
  else {
97
107
  usage();
98
108
  process.exit(1);
@@ -102,6 +112,13 @@ async function main() {
102
112
  usage();
103
113
  process.exit(1);
104
114
  }
115
+ if ((quick && simple) ||
116
+ (detailed && simple) ||
117
+ (detailed && quick) ||
118
+ (stack && (quick || simple || detailed))) {
119
+ usage();
120
+ process.exit(1);
121
+ }
105
122
  const target = args[0];
106
123
  if (!target.includes("/") || target.split("/").length !== 2) {
107
124
  console.log("Invalid format. Use owner/repo");
@@ -113,6 +130,17 @@ async function main() {
113
130
  process.exit(1);
114
131
  }
115
132
  console.log(`Fetching ${owner}/${repo}...`);
133
+ if (stack) {
134
+ const languages = await fetchLanguages(owner, repo);
135
+ const read = await readRepoSignalFiles(owner, repo);
136
+ const report = detectStack({
137
+ languages,
138
+ tree: read.tree,
139
+ keyFiles: read.keyFiles,
140
+ });
141
+ printStack(report, owner, repo);
142
+ return;
143
+ }
116
144
  try {
117
145
  const repoData = await fetchRepo(owner, repo);
118
146
  const readme = await fetchReadme(owner, repo);
@@ -125,6 +153,14 @@ async function main() {
125
153
  console.log(output.trim());
126
154
  return;
127
155
  }
156
+ if (simple) {
157
+ console.log("Summarizing...");
158
+ const simplePrompt = buildSimplePrompt(output);
159
+ const simpleOutput = await generateExplanation(simplePrompt);
160
+ console.log("Simple summary 🎉");
161
+ console.log(simpleOutput.trim());
162
+ return;
163
+ }
128
164
  console.log("Writing EXPLAIN.md...");
129
165
  writeOutput(output);
130
166
  const wordCount = output.split(/\s+/).filter(Boolean).length;
package/dist/github.d.ts CHANGED
@@ -2,3 +2,5 @@ export declare function fetchRepo(owner: string, repo: string): Promise<any>;
2
2
  export declare function fetchReadme(owner: string, repo: string): Promise<string | null>;
3
3
  export declare function fetchTree(owner: string, repo: string): Promise<any[]>;
4
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>;
package/dist/github.js CHANGED
@@ -120,3 +120,9 @@ export async function fetchFile(owner, repo, filePath) {
120
120
  return res.data;
121
121
  });
122
122
  }
123
+ export async function fetchLanguages(owner, repo) {
124
+ return requestWithRetry(async () => {
125
+ const res = await github.get(`/repos/${owner}/${repo}/languages`);
126
+ return res.data;
127
+ });
128
+ }
package/dist/prompt.d.ts CHANGED
@@ -1 +1,2 @@
1
1
  export declare function buildPrompt(repoName: string, description: string | null, readme: string | null, detailed?: boolean, quick?: boolean, treeText?: string | null, filesText?: string | null): string;
2
+ export declare function buildSimplePrompt(longExplanation: string): string;
package/dist/prompt.js CHANGED
@@ -36,7 +36,7 @@ README content:
36
36
  ${readme || "No README provided"}
37
37
 
38
38
  Repository structure:
39
- ${treeText || "No tree rovided"}
39
+ ${treeText || "No tree provided"}
40
40
 
41
41
  Key files (snippets):
42
42
  ${filesText || "No code files provided"}
@@ -71,3 +71,32 @@ Output format:
71
71
  `;
72
72
  return prompt.trim();
73
73
  }
74
+ export function buildSimplePrompt(longExplanation) {
75
+ return `
76
+ You are a senior software engineer.
77
+
78
+ Rewrite the long repository explanation below into a SIMPLE version in the exact style specified.
79
+
80
+ Input explanation:
81
+ ${longExplanation}
82
+
83
+ Output style rules:
84
+ - Plain English.
85
+ - No markdown.
86
+ - Do NOT use headings like "Overview", "What this project does", etc.
87
+ - Start with exactly this line:
88
+ Key points from the repo:
89
+ - Then output 4 to 7 bullets only.
90
+ - Each bullet MUST start with: ⬤
91
+ - Each bullet title should be 1–3 words only (example: "Purpose", "Stack", "Entrypoints", "How it works", "Usage", "Structure").
92
+ - Each bullet body should be 1–2 lines max.
93
+ - If the input contains architecture/pipeline steps, capture them naturally.
94
+ - If the input does NOT contain architecture/pipeline steps, do NOT invent them.
95
+ - Optional: end with one extra line starting with:
96
+ Also interesting:
97
+ - Do NOT add features not present in the input.
98
+ - No quotes.
99
+
100
+ Make it feel like a human developer explaining to another developer in simple terms.
101
+ `.trim();
102
+ }
@@ -1,6 +1,14 @@
1
+ type TreeItem = {
2
+ path: string;
3
+ type: "blob" | "tree";
4
+ size?: number;
5
+ };
1
6
  export type RepoReadResult = {
7
+ tree: TreeItem[];
2
8
  treeText: string;
3
9
  filesText: string;
4
10
  selectedFiles: string[];
11
+ keyFiles: Record<string, string>;
5
12
  };
6
13
  export declare function readRepoSignalFiles(owner: string, repo: string): Promise<RepoReadResult>;
14
+ export {};
@@ -113,9 +113,36 @@ export async function readRepoSignalFiles(owner, repo) {
113
113
  catch {
114
114
  }
115
115
  }
116
+ const keyFiles = {};
117
+ const STACK_KEY_FILES = [
118
+ "package.json",
119
+ "pyproject.toml",
120
+ "requirements.txt",
121
+ "go.mod",
122
+ "Cargo.toml",
123
+ "composer.json",
124
+ "pom.xml",
125
+ "build.gradle",
126
+ "Dockerfile",
127
+ "docker-compose.yml",
128
+ "vercel.json",
129
+ "netlify.toml",
130
+ ];
131
+ for (const path of STACK_KEY_FILES) {
132
+ const match = tree.find(t => t.path.toLowerCase() === path.toLowerCase());
133
+ if (!match)
134
+ continue;
135
+ try {
136
+ const content = await fetchFile(owner, repo, path);
137
+ keyFiles[path] = content.slice(0, 20000);
138
+ }
139
+ catch { }
140
+ }
116
141
  return {
142
+ tree,
117
143
  treeText,
118
144
  filesText: fileBlocks.join("\n"),
119
145
  selectedFiles: selected,
146
+ keyFiles,
120
147
  };
121
148
  }
@@ -0,0 +1,23 @@
1
+ import type { RepoLanguageMap } from "./github.js";
2
+ type TreeItem = {
3
+ path: string;
4
+ type: string;
5
+ size?: number;
6
+ };
7
+ type StackInput = {
8
+ languages: RepoLanguageMap;
9
+ tree: TreeItem[];
10
+ keyFiles: Record<string, string>;
11
+ };
12
+ export type StackReport = {
13
+ languages: string[];
14
+ runtimes: string[];
15
+ frontend: string[];
16
+ backend: string[];
17
+ databases: string[];
18
+ tooling: string[];
19
+ infra: string[];
20
+ packageManagers: string[];
21
+ };
22
+ export declare function detectStack(input: StackInput): StackReport;
23
+ export {};
@@ -0,0 +1,54 @@
1
+ export function detectStack(input) {
2
+ const report = {
3
+ languages: [],
4
+ runtimes: [],
5
+ frontend: [],
6
+ backend: [],
7
+ databases: [],
8
+ tooling: [],
9
+ infra: [],
10
+ packageManagers: [],
11
+ };
12
+ const total = Object.values(input.languages).reduce((a, b) => a + b, 0);
13
+ report.languages = Object.entries(input.languages)
14
+ .filter(([_, bytes]) => bytes / total > 0.03)
15
+ .map(([lang]) => lang);
16
+ const paths = input.tree.map(t => t.path.toLowerCase());
17
+ if (paths.includes("dockerfile"))
18
+ report.infra.push("Docker");
19
+ if (paths.includes("vercel.json"))
20
+ report.infra.push("Vercel");
21
+ if (paths.includes("netlify.toml"))
22
+ report.infra.push("Netlify");
23
+ if (paths.some(p => p.startsWith(".github/workflows")))
24
+ report.infra.push("GitHub Actions");
25
+ const pkgRaw = input.keyFiles["package.json"];
26
+ if (pkgRaw) {
27
+ report.runtimes.push("Node.js");
28
+ report.packageManagers.push("npm");
29
+ try {
30
+ const pkg = JSON.parse(pkgRaw);
31
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
32
+ if (deps.react)
33
+ report.frontend.push("React");
34
+ if (deps.next)
35
+ report.frontend.push("Next.js");
36
+ if (deps.express)
37
+ report.backend.push("Express");
38
+ if (deps.fastify)
39
+ report.backend.push("Fastify");
40
+ if (deps.prisma)
41
+ report.databases.push("Prisma");
42
+ if (deps.mongoose)
43
+ report.databases.push("MongoDB");
44
+ if (deps.jest)
45
+ report.tooling.push("Jest");
46
+ if (deps.vite)
47
+ report.tooling.push("Vite");
48
+ if (deps.eslint)
49
+ report.tooling.push("ESLint");
50
+ }
51
+ catch { }
52
+ }
53
+ return report;
54
+ }
@@ -0,0 +1,2 @@
1
+ import type { StackReport } from "./stack-detector.js";
2
+ export declare function printStack(report: StackReport, owner: string, repo: string): void;
@@ -0,0 +1,20 @@
1
+ function section(title, items) {
2
+ if (!items || items.length === 0)
3
+ return;
4
+ console.log(`\n${title}:`);
5
+ for (const item of items) {
6
+ console.log(`- ${item}`);
7
+ }
8
+ }
9
+ export function printStack(report, owner, repo) {
10
+ console.log(`\nStack summary for ${owner}/${repo}`);
11
+ section("Languages", report.languages);
12
+ section("Runtime", report.runtimes);
13
+ section("Frontend", report.frontend);
14
+ section("Backend", report.backend);
15
+ section("Databases / ORM", report.databases);
16
+ section("Tooling", report.tooling);
17
+ section("Infrastructure / Deploy", report.infra);
18
+ section("Package Managers", report.packageManagers);
19
+ console.log("");
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "explainthisrepo",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "description": "ExplainThisRepo is a CLI developer tool to explain any GitHub repository in plain English",
5
5
  "license": "MIT",
6
6
  "type": "module",