@chappibunny/repolens 1.8.2 → 1.9.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 +9 -1
- package/package.json +1 -1
- package/src/cli.js +18 -16
- package/src/core/scan.js +143 -7
- package/src/utils/logger.js +47 -0
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
|
|
18
18
|
RepoLens scans your repository, generates living architecture documentation, and publishes it to Notion, Confluence, GitHub Wiki, or Markdown — automatically on every push. Engineers get technical docs. Stakeholders get readable system overviews. Nobody writes a word.
|
|
19
19
|
|
|
20
|
-
> Stable as of v1.0 — [API guarantees](STABILITY.md) · [Security hardened](SECURITY.md) · v1.
|
|
20
|
+
> Stable as of v1.0 — [API guarantees](STABILITY.md) · [Security hardened](SECURITY.md) · v1.9.0
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
@@ -29,6 +29,14 @@ RepoLens scans your repository, generates living architecture documentation, and
|
|
|
29
29
|
|
|
30
30
|
▶️ *Click to watch on YouTube*
|
|
31
31
|
|
|
32
|
+
<details>
|
|
33
|
+
<summary>🔍 <strong>Supported Languages</strong> (16 auto-detected)</summary>
|
|
34
|
+
|
|
35
|
+
`JavaScript` `TypeScript` `Python` `Go` `Rust` `Java` `C` `C++` `C#` `Ruby` `PHP` `Swift` `Kotlin` `Scala` `Shell` `SQL`
|
|
36
|
+
|
|
37
|
+
Plus framework detection: **Django** · **FastAPI** · **Flask** · **Gin** · **Echo** · **Fiber** · **Actix** · **Rocket** — and all major JS frameworks (React, Next.js, Vue, Angular, Express, NestJS, Svelte, etc.)
|
|
38
|
+
</details>
|
|
39
|
+
|
|
32
40
|
---
|
|
33
41
|
|
|
34
42
|
## 🚀 Quick Start (60 seconds)
|
package/package.json
CHANGED
package/src/cli.js
CHANGED
|
@@ -21,7 +21,7 @@ import { upsertPrComment } from "./delivery/comment.js";
|
|
|
21
21
|
import { runInit } from "./init.js";
|
|
22
22
|
import { runMigrate } from "./migrate.js";
|
|
23
23
|
import { runWatch } from "./watch.js";
|
|
24
|
-
import { info, error, warn } from "./utils/logger.js";
|
|
24
|
+
import { info, error, warn, fmt } from "./utils/logger.js";
|
|
25
25
|
import { formatError } from "./utils/errors.js";
|
|
26
26
|
import { checkForUpdates } from "./utils/update-check.js";
|
|
27
27
|
import { generateDocumentSet } from "./docs/generate-doc-set.js";
|
|
@@ -520,23 +520,25 @@ async function main() {
|
|
|
520
520
|
info("\nTo publish to Notion, Confluence, or GitHub Wiki, run: repolens publish");
|
|
521
521
|
|
|
522
522
|
if (aiAutoEnabled) {
|
|
523
|
-
info(
|
|
523
|
+
info(`\n🤖 AI-enhanced docs were generated using ${fmt.boldGreen("GitHub Models (FREE)")}`);
|
|
524
524
|
info(" To keep AI enabled permanently, run: repolens init --interactive");
|
|
525
525
|
} else if (!cfg.ai?.enabled && process.env.REPOLENS_AI_ENABLED !== "true") {
|
|
526
|
-
info(
|
|
527
|
-
info("
|
|
528
|
-
info("
|
|
529
|
-
info("
|
|
530
|
-
info(" •
|
|
531
|
-
info(" •
|
|
532
|
-
info(" •
|
|
533
|
-
info("");
|
|
534
|
-
info("
|
|
535
|
-
info("
|
|
536
|
-
info("
|
|
537
|
-
info("");
|
|
538
|
-
info("
|
|
539
|
-
info("
|
|
526
|
+
info(`\n${fmt.cyan("┌──────────────────────────────────────────────────────────────────┐")}`);
|
|
527
|
+
info(`${fmt.cyan("│")} ${fmt.boldYellow("✨ Unlock AI-Enhanced Documentation")} ${fmt.cyan("│")}`);
|
|
528
|
+
info(`${fmt.cyan("├──────────────────────────────────────────────────────────────────┤")}`);
|
|
529
|
+
info(`${fmt.cyan("│")} Your docs are missing these AI-powered sections: ${fmt.cyan("│")}`);
|
|
530
|
+
info(`${fmt.cyan("│")} ${fmt.yellow("•")} Executive Summary — plain language overview for leadership ${fmt.cyan("│")}`);
|
|
531
|
+
info(`${fmt.cyan("│")} ${fmt.yellow("•")} Business Domains — what the system does for stakeholders ${fmt.cyan("│")}`);
|
|
532
|
+
info(`${fmt.cyan("│")} ${fmt.yellow("•")} Architecture Overview — deeper narrative for architects ${fmt.cyan("│")}`);
|
|
533
|
+
info(`${fmt.cyan("│")} ${fmt.yellow("•")} Data Flows — how information moves through your system ${fmt.cyan("│")}`);
|
|
534
|
+
info(`${fmt.cyan("│")} ${fmt.yellow("•")} Developer Onboarding — getting started guide for new hires ${fmt.cyan("│")}`);
|
|
535
|
+
info(`${fmt.cyan("│")} ${fmt.cyan("│")}`);
|
|
536
|
+
info(`${fmt.cyan("│")} ${fmt.boldGreen("🆓 Enable for FREE with GitHub Models:")} ${fmt.cyan("│")}`);
|
|
537
|
+
info(`${fmt.cyan("│")} ${fmt.green("export GITHUB_TOKEN=<your-token>")} ${fmt.cyan("│")}`);
|
|
538
|
+
info(`${fmt.cyan("│")} ${fmt.green("repolens demo")} ${fmt.cyan("│")}`);
|
|
539
|
+
info(`${fmt.cyan("│")} ${fmt.cyan("│")}`);
|
|
540
|
+
info(`${fmt.cyan("│")} Or run: ${fmt.brightCyan("repolens init --interactive")} → select GitHub Models ${fmt.cyan("│")}`);
|
|
541
|
+
info(`${fmt.cyan("└──────────────────────────────────────────────────────────────────┘")}`);
|
|
540
542
|
}
|
|
541
543
|
|
|
542
544
|
printPerformanceSummary();
|
package/src/core/scan.js
CHANGED
|
@@ -136,7 +136,70 @@ function routePathFromFile(file) {
|
|
|
136
136
|
return file;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
|
|
139
|
+
/**
|
|
140
|
+
* Detect languages from file extensions
|
|
141
|
+
*/
|
|
142
|
+
function detectLanguagesFromFiles(files) {
|
|
143
|
+
const languages = new Set();
|
|
144
|
+
|
|
145
|
+
// Extension to language mapping
|
|
146
|
+
const extensionMap = {
|
|
147
|
+
// JavaScript/TypeScript
|
|
148
|
+
".js": "JavaScript",
|
|
149
|
+
".mjs": "JavaScript",
|
|
150
|
+
".cjs": "JavaScript",
|
|
151
|
+
".jsx": "JavaScript",
|
|
152
|
+
".ts": "TypeScript",
|
|
153
|
+
".tsx": "TypeScript",
|
|
154
|
+
// Python
|
|
155
|
+
".py": "Python",
|
|
156
|
+
".pyw": "Python",
|
|
157
|
+
".pyi": "Python",
|
|
158
|
+
// Go
|
|
159
|
+
".go": "Go",
|
|
160
|
+
// Rust
|
|
161
|
+
".rs": "Rust",
|
|
162
|
+
// Java
|
|
163
|
+
".java": "Java",
|
|
164
|
+
// C/C++
|
|
165
|
+
".c": "C",
|
|
166
|
+
".h": "C",
|
|
167
|
+
".cpp": "C++",
|
|
168
|
+
".hpp": "C++",
|
|
169
|
+
".cc": "C++",
|
|
170
|
+
".cxx": "C++",
|
|
171
|
+
// C#
|
|
172
|
+
".cs": "C#",
|
|
173
|
+
// Ruby
|
|
174
|
+
".rb": "Ruby",
|
|
175
|
+
// PHP
|
|
176
|
+
".php": "PHP",
|
|
177
|
+
// Swift
|
|
178
|
+
".swift": "Swift",
|
|
179
|
+
// Kotlin
|
|
180
|
+
".kt": "Kotlin",
|
|
181
|
+
".kts": "Kotlin",
|
|
182
|
+
// Scala
|
|
183
|
+
".scala": "Scala",
|
|
184
|
+
// Shell
|
|
185
|
+
".sh": "Shell",
|
|
186
|
+
".bash": "Shell",
|
|
187
|
+
".zsh": "Shell",
|
|
188
|
+
// SQL
|
|
189
|
+
".sql": "SQL",
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
for (const file of files) {
|
|
193
|
+
const ext = path.extname(file).toLowerCase();
|
|
194
|
+
if (extensionMap[ext]) {
|
|
195
|
+
languages.add(extensionMap[ext]);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
return languages;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function extractRepoMetadata(repoRoot, files = []) {
|
|
140
203
|
const metadata = {
|
|
141
204
|
hasPackageJson: false,
|
|
142
205
|
frameworks: [],
|
|
@@ -191,12 +254,9 @@ async function extractRepoMetadata(repoRoot) {
|
|
|
191
254
|
if (allDeps["swc"] || allDeps["@swc/core"]) metadata.buildTools.push("SWC");
|
|
192
255
|
if (allDeps["parcel"]) metadata.buildTools.push("Parcel");
|
|
193
256
|
|
|
194
|
-
// Detect
|
|
257
|
+
// Detect TypeScript from dependencies (supplements file detection)
|
|
195
258
|
if (allDeps["typescript"]) metadata.languages.add("TypeScript");
|
|
196
259
|
|
|
197
|
-
// Infer JavaScript if package.json exists (any npm project uses JS/Node)
|
|
198
|
-
metadata.languages.add("JavaScript");
|
|
199
|
-
|
|
200
260
|
// Detect Node.js runtime indicators
|
|
201
261
|
const hasNodeEngines = pkg.engines && pkg.engines.node;
|
|
202
262
|
const hasBin = pkg.bin != null;
|
|
@@ -210,6 +270,82 @@ async function extractRepoMetadata(repoRoot) {
|
|
|
210
270
|
// No package.json or invalid JSON
|
|
211
271
|
}
|
|
212
272
|
|
|
273
|
+
// Detect Python frameworks and tools
|
|
274
|
+
try {
|
|
275
|
+
// Check for pyproject.toml (modern Python)
|
|
276
|
+
const pyprojectPath = path.join(repoRoot, "pyproject.toml");
|
|
277
|
+
const pyprojectContent = await fs.readFile(pyprojectPath, "utf8");
|
|
278
|
+
metadata.languages.add("Python");
|
|
279
|
+
|
|
280
|
+
// Detect Python frameworks from pyproject.toml
|
|
281
|
+
if (/django/i.test(pyprojectContent)) metadata.frameworks.push("Django");
|
|
282
|
+
if (/fastapi/i.test(pyprojectContent)) metadata.frameworks.push("FastAPI");
|
|
283
|
+
if (/flask/i.test(pyprojectContent)) metadata.frameworks.push("Flask");
|
|
284
|
+
if (/pytest/i.test(pyprojectContent)) metadata.testFrameworks.push("pytest");
|
|
285
|
+
if (/poetry/i.test(pyprojectContent)) metadata.buildTools.push("Poetry");
|
|
286
|
+
} catch {
|
|
287
|
+
// No pyproject.toml
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
try {
|
|
291
|
+
// Check for requirements.txt
|
|
292
|
+
const reqPath = path.join(repoRoot, "requirements.txt");
|
|
293
|
+
const reqContent = await fs.readFile(reqPath, "utf8");
|
|
294
|
+
metadata.languages.add("Python");
|
|
295
|
+
|
|
296
|
+
if (/django/i.test(reqContent)) metadata.frameworks.push("Django");
|
|
297
|
+
if (/fastapi/i.test(reqContent)) metadata.frameworks.push("FastAPI");
|
|
298
|
+
if (/flask/i.test(reqContent)) metadata.frameworks.push("Flask");
|
|
299
|
+
if (/pytest/i.test(reqContent)) metadata.testFrameworks.push("pytest");
|
|
300
|
+
} catch {
|
|
301
|
+
// No requirements.txt
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
try {
|
|
305
|
+
// Check for setup.py (legacy Python)
|
|
306
|
+
await fs.access(path.join(repoRoot, "setup.py"));
|
|
307
|
+
metadata.languages.add("Python");
|
|
308
|
+
} catch {
|
|
309
|
+
// No setup.py
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Detect Go modules
|
|
313
|
+
try {
|
|
314
|
+
const goModPath = path.join(repoRoot, "go.mod");
|
|
315
|
+
const goModContent = await fs.readFile(goModPath, "utf8");
|
|
316
|
+
metadata.languages.add("Go");
|
|
317
|
+
|
|
318
|
+
if (/gin-gonic/i.test(goModContent)) metadata.frameworks.push("Gin");
|
|
319
|
+
if (/echo/i.test(goModContent)) metadata.frameworks.push("Echo");
|
|
320
|
+
if (/fiber/i.test(goModContent)) metadata.frameworks.push("Fiber");
|
|
321
|
+
} catch {
|
|
322
|
+
// No go.mod
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// Detect Rust via Cargo.toml
|
|
326
|
+
try {
|
|
327
|
+
const cargoPath = path.join(repoRoot, "Cargo.toml");
|
|
328
|
+
const cargoContent = await fs.readFile(cargoPath, "utf8");
|
|
329
|
+
metadata.languages.add("Rust");
|
|
330
|
+
|
|
331
|
+
if (/actix/i.test(cargoContent)) metadata.frameworks.push("Actix");
|
|
332
|
+
if (/rocket/i.test(cargoContent)) metadata.frameworks.push("Rocket");
|
|
333
|
+
if (/tokio/i.test(cargoContent)) metadata.frameworks.push("Tokio");
|
|
334
|
+
} catch {
|
|
335
|
+
// No Cargo.toml
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// Merge languages detected from file extensions
|
|
339
|
+
const fileLanguages = detectLanguagesFromFiles(files);
|
|
340
|
+
for (const lang of fileLanguages) {
|
|
341
|
+
metadata.languages.add(lang);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// De-duplicate frameworks (in case detected from multiple sources)
|
|
345
|
+
metadata.frameworks = [...new Set(metadata.frameworks)];
|
|
346
|
+
metadata.testFrameworks = [...new Set(metadata.testFrameworks)];
|
|
347
|
+
metadata.buildTools = [...new Set(metadata.buildTools)];
|
|
348
|
+
|
|
213
349
|
return metadata;
|
|
214
350
|
}
|
|
215
351
|
|
|
@@ -435,8 +571,8 @@ export async function scanRepo(cfg) {
|
|
|
435
571
|
}
|
|
436
572
|
}
|
|
437
573
|
|
|
438
|
-
// Extract repository metadata
|
|
439
|
-
const metadata = await extractRepoMetadata(repoRoot);
|
|
574
|
+
// Extract repository metadata (pass files for language detection)
|
|
575
|
+
const metadata = await extractRepoMetadata(repoRoot, files);
|
|
440
576
|
|
|
441
577
|
// Detect external API integrations
|
|
442
578
|
const externalApis = await detectExternalApis(files, repoRoot);
|
package/src/utils/logger.js
CHANGED
|
@@ -3,6 +3,53 @@ import { sanitizeSecrets } from "./secrets.js";
|
|
|
3
3
|
const isVerbose = process.argv.includes("--verbose");
|
|
4
4
|
const isTest = process.env.NODE_ENV === "test";
|
|
5
5
|
|
|
6
|
+
// Terminal color support detection
|
|
7
|
+
const supportsColor = !process.env.NO_COLOR &&
|
|
8
|
+
(process.env.FORCE_COLOR ||
|
|
9
|
+
(process.stdout.isTTY && process.env.TERM !== "dumb"));
|
|
10
|
+
|
|
11
|
+
// ANSI color codes
|
|
12
|
+
const colors = {
|
|
13
|
+
reset: "\x1b[0m",
|
|
14
|
+
bold: "\x1b[1m",
|
|
15
|
+
dim: "\x1b[2m",
|
|
16
|
+
green: "\x1b[32m",
|
|
17
|
+
yellow: "\x1b[33m",
|
|
18
|
+
cyan: "\x1b[36m",
|
|
19
|
+
magenta: "\x1b[35m",
|
|
20
|
+
brightGreen: "\x1b[92m",
|
|
21
|
+
brightCyan: "\x1b[96m",
|
|
22
|
+
brightYellow: "\x1b[93m",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Apply color formatting to text (respects NO_COLOR)
|
|
27
|
+
*/
|
|
28
|
+
export function colorize(text, ...styles) {
|
|
29
|
+
if (!supportsColor) return text;
|
|
30
|
+
const codes = styles.map(s => colors[s] || "").join("");
|
|
31
|
+
return `${codes}${text}${colors.reset}`;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Shorthand color helpers
|
|
36
|
+
*/
|
|
37
|
+
export const fmt = {
|
|
38
|
+
bold: (text) => colorize(text, "bold"),
|
|
39
|
+
green: (text) => colorize(text, "green"),
|
|
40
|
+
brightGreen: (text) => colorize(text, "brightGreen"),
|
|
41
|
+
cyan: (text) => colorize(text, "cyan"),
|
|
42
|
+
brightCyan: (text) => colorize(text, "brightCyan"),
|
|
43
|
+
yellow: (text) => colorize(text, "yellow"),
|
|
44
|
+
brightYellow: (text) => colorize(text, "brightYellow"),
|
|
45
|
+
magenta: (text) => colorize(text, "magenta"),
|
|
46
|
+
dim: (text) => colorize(text, "dim"),
|
|
47
|
+
// Combined styles
|
|
48
|
+
boldGreen: (text) => colorize(text, "bold", "brightGreen"),
|
|
49
|
+
boldCyan: (text) => colorize(text, "bold", "brightCyan"),
|
|
50
|
+
boldYellow: (text) => colorize(text, "bold", "brightYellow"),
|
|
51
|
+
};
|
|
52
|
+
|
|
6
53
|
/**
|
|
7
54
|
* Sanitize arguments for logging
|
|
8
55
|
*/
|