@lakindu_perera/toren 1.0.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Lakindu Perera
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,354 @@
1
+ # Toren
2
+
3
+ > Understand any codebase in seconds.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/toren.svg)](https://www.npmjs.com/package/toren)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
7
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org)
8
+ [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-success.svg)](package.json)
9
+
10
+ ---
11
+
12
+ ## What is Toren?
13
+
14
+ **Toren** is a lightweight, zero-dependency CLI tool that scans a software project and gives you an instant, structured overview — without reading a single line of code.
15
+
16
+ Drop it into any unfamiliar repository and immediately see:
17
+
18
+ - What technology stack the project uses
19
+ - Where the application starts (entry points)
20
+ - How many files and folders exist
21
+ - A visual preview of the directory structure
22
+
23
+ Whether you've just cloned an open-source project, joined a new team, or are reviewing a client's codebase, Toren cuts through the noise and gets you oriented fast.
24
+
25
+ ---
26
+
27
+ ## Features
28
+
29
+ - ⚡ **Fast scanning** — recursively walks a project in milliseconds
30
+ - 📦 **Zero external dependencies** — pure Node.js stdlib only
31
+ - 🧠 **Framework detection** — identifies React, Next.js, Vue, Angular, Go, Rust, Python, and more
32
+ - 🚪 **Entry point detection** — pinpoints `index.js`, `main.ts`, `App.tsx`, `Application.java`, and other common entry files
33
+ - 🌲 **File tree preview** — visual directory structure, up to 4 levels deep
34
+ - 🎨 **Beautiful terminal output** — ANSI-styled, readable at a glance
35
+ - 🔧 **JSON output** — machine-readable format for scripting and tooling integration
36
+ - 🙈 **Smart ignore rules** — skips `node_modules`, `.git`, `dist`, `build`, `.venv`, and more
37
+ - 🔌 **Extensible renderer architecture** — add new output formats without touching core logic
38
+
39
+ ---
40
+
41
+ ## Installation
42
+
43
+ Install globally with npm:
44
+
45
+ ```bash
46
+ npm install -g toren
47
+ ```
48
+
49
+ Or run without installing:
50
+
51
+ ```bash
52
+ npx toren
53
+ ```
54
+
55
+ **Requirements:** Node.js 18.0.0 or higher.
56
+
57
+ ---
58
+
59
+ ## Quick Start
60
+
61
+ Scan the current directory:
62
+
63
+ ```bash
64
+ toren
65
+ ```
66
+
67
+ Scan a specific path:
68
+
69
+ ```bash
70
+ toren .
71
+ toren ../my-project
72
+ toren /path/to/any/repo
73
+ ```
74
+
75
+ Output results as JSON:
76
+
77
+ ```bash
78
+ toren --json
79
+ ```
80
+
81
+ Check your global installation:
82
+
83
+ ```bash
84
+ toren --doctor
85
+ ```
86
+
87
+ ---
88
+
89
+ ## Example Output
90
+
91
+ ```
92
+ Toren v1.0.0 — Codebase Onboarding Intelligence
93
+
94
+ 🔍 Project Summary
95
+ ────────────────────────────────────────────────────────────────────────────────
96
+ Path: ./my-app
97
+ Project type: Next.js
98
+ Total files: 48
99
+ Total folders: 11
100
+ Scan duration: 3 ms
101
+
102
+ 🚪 Entry Points
103
+ ────────────────────────────────────────────────────────────────────────────────
104
+ → src/app/page.tsx
105
+ → src/app/layout.tsx
106
+
107
+ 📁 Folder Structure (first 20 files)
108
+ ────────────────────────────────────────────────────────────────────────────────
109
+ my-app/
110
+ ├── public/
111
+ │ └── favicon.ico
112
+ ├── src/
113
+ │ ├── app/
114
+ │ │ ├── layout.tsx
115
+ │ │ └── page.tsx
116
+ │ ├── components/
117
+ │ │ ├── Header.tsx
118
+ │ │ └── Footer.tsx
119
+ │ └── lib/
120
+ │ └── utils.ts
121
+ ├── .eslintrc.json
122
+ ├── next.config.js
123
+ ├── package.json
124
+ └── tsconfig.json
125
+ … and 28 more file(s) not shown
126
+
127
+ ────────────────────────────────────────────────────────────────────────────────
128
+ ✅ Scan complete.
129
+ ```
130
+
131
+ ---
132
+
133
+ ## JSON Output
134
+
135
+ Use `toren --json` to get a machine-readable result suitable for piping into other tools:
136
+
137
+ ```json
138
+ {
139
+ "project": {
140
+ "path": "./my-app",
141
+ "type": "Next.js",
142
+ "framework": "Next.js"
143
+ },
144
+ "summary": {
145
+ "totalFiles": 48,
146
+ "totalFolders": 11,
147
+ "scanDurationMs": 3
148
+ },
149
+ "entryPoints": [
150
+ "src/app/page.tsx",
151
+ "src/app/layout.tsx"
152
+ ],
153
+ "structure": [
154
+ {
155
+ "type": "folder",
156
+ "name": "src",
157
+ "children": [
158
+ {
159
+ "type": "folder",
160
+ "name": "app",
161
+ "children": [
162
+ { "type": "file", "name": "layout.tsx" },
163
+ { "type": "file", "name": "page.tsx" }
164
+ ]
165
+ }
166
+ ]
167
+ },
168
+ { "type": "file", "name": "package.json" },
169
+ { "type": "file", "name": "next.config.js" }
170
+ ]
171
+ }
172
+ ```
173
+
174
+ ---
175
+
176
+ ## Supported Project Types
177
+
178
+ Toren detects the following project types automatically:
179
+
180
+ | Marker File | Detected Type |
181
+ |--------------------------------------|----------------------------|
182
+ | `package.json` | Node.js / JavaScript |
183
+ | `package.json` + `next` dep | Next.js |
184
+ | `package.json` + `react` dep | React |
185
+ | `package.json` + `vue` dep | Vue.js |
186
+ | `package.json` + `@angular/core` dep | Angular |
187
+ | `package.json` + `svelte` dep | Svelte |
188
+ | `package.json` + `express` dep | Node.js / Express |
189
+ | `package.json` + `fastify` dep | Node.js / Fastify |
190
+ | `package.json` + `koa` dep | Node.js / Koa |
191
+ | `package.json` + `typescript` dep | Node.js / TypeScript |
192
+ | `requirements.txt` | Python |
193
+ | `Pipfile` | Python (Pipenv) |
194
+ | `pyproject.toml` | Python (pyproject) |
195
+ | `go.mod` | Go |
196
+ | `Cargo.toml` | Rust |
197
+ | `pom.xml` | Java / Spring Boot |
198
+ | `build.gradle` | Java / Gradle |
199
+ | `composer.json` | PHP / Composer |
200
+ | `Gemfile` | Ruby |
201
+ | `mix.exs` | Elixir |
202
+
203
+ If no marker is found, Toren reports `Unknown` without failing.
204
+
205
+ ---
206
+
207
+ ## CLI Reference
208
+
209
+ | Command | Description |
210
+ |----------------------|--------------------------------------------------|
211
+ | `toren` | Scan the current directory |
212
+ | `toren [path]` | Scan a specific directory or file path |
213
+ | `toren --json` | Output scan results as formatted JSON |
214
+ | `toren --version` | Print the installed version number |
215
+ | `toren --help` | Show usage information |
216
+ | `toren --doctor` | Diagnose the global installation health |
217
+ | `toren --uninstall` | Guided removal of the global installation |
218
+
219
+ ---
220
+
221
+ ## Project Structure
222
+
223
+ ```
224
+ toren/
225
+ ├── bin/
226
+ │ └── toren.js # CLI entry point — argument parsing and orchestration
227
+ ├── src/
228
+ │ ├── lifecycle.js # --doctor and --uninstall command implementations
229
+ │ ├── scanner/
230
+ │ │ └── scan.js # Core scanning engine — file walker, type detection, entry point detection
231
+ │ └── renderers/
232
+ │ ├── console-renderer.js # ANSI-styled terminal output
233
+ │ ├── json-renderer.js # Machine-readable JSON output
234
+ │ └── tree-renderer.js # Standalone flat-file tree formatter (utility)
235
+ └── package.json
236
+ ```
237
+
238
+ ---
239
+
240
+ ## How It Works
241
+
242
+ ### 1. Scanner
243
+
244
+ `src/scanner/scan.js` is the core engine. It takes a target path and recursively walks the file system using Node's `fs.readdirSync`, collecting every file and directory while skipping entries in the ignore list (`node_modules`, `.git`, `dist`, `build`, etc.).
245
+
246
+ The result is an in-memory tree of `DirNode` and `FileNode` objects, along with a flat list of all relative file paths.
247
+
248
+ ### 2. Framework Detection
249
+
250
+ After walking the directory, the scanner checks for known marker files at the project root (e.g. `package.json`, `go.mod`, `Cargo.toml`). For `package.json`, it reads the file and inspects `dependencies`, `devDependencies`, and `peerDependencies` to determine the specific framework (React, Next.js, Vue, Angular, etc.).
251
+
252
+ ### 3. Entry Point Detection
253
+
254
+ During the walk, each filename is checked against a known set of entry points: `index.js`, `index.ts`, `main.py`, `App.tsx`, `Application.java`, `page.tsx`, `layout.tsx`, etc. All matches are collected and surfaced in the output.
255
+
256
+ ### 4. Renderer
257
+
258
+ The scan result — a plain JavaScript object — is passed to a renderer. Renderers are completely decoupled from the scanner; they only read data and produce output.
259
+
260
+ ### 5. Output
261
+
262
+ The CLI selects the appropriate renderer based on flags (`--json` → JSON renderer; default → console renderer). Errors are caught and formatted consistently in both modes.
263
+
264
+ ---
265
+
266
+ ## Architecture
267
+
268
+ ```
269
+ CLI (bin/toren.js)
270
+
271
+
272
+ Argument Parser
273
+
274
+
275
+ Scanner (scan.js)
276
+
277
+ ├── Directory Walker
278
+ ├── Ignore Filter
279
+ ├── Framework Detector
280
+ └── Entry Point Detector
281
+
282
+
283
+ ScanResult (plain object)
284
+
285
+ ├──────────────────┐
286
+ ▼ ▼
287
+ Console Renderer JSON Renderer
288
+ (ANSI terminal) (stdout / pipe)
289
+ ```
290
+
291
+ Adding a new output format is as simple as creating a new file in `src/renderers/` and importing it in `bin/toren.js`.
292
+
293
+ ---
294
+
295
+ ## Why Toren?
296
+
297
+ When you encounter a new codebase, the usual approach is to start opening files, guessing at folder names, and reading `package.json` manually. This works — but it's slow and inconsistent.
298
+
299
+ Toren automates that first pass. In one command, you get a structured summary of what the project is, where it starts, and what's inside. This is especially useful when:
300
+
301
+ - **Onboarding to a new job** — quickly orient yourself before your first meeting
302
+ - **Reviewing a pull request or open-source repo** — understand the scope at a glance
303
+ - **Auditing a legacy codebase** — know what you're dealing with before diving in
304
+ - **Building tooling** — use `--json` to feed project metadata into scripts or AI tools
305
+
306
+ ---
307
+
308
+ ## Roadmap
309
+
310
+ - [x] Console renderer
311
+ - [x] JSON renderer
312
+ - [x] Framework detection (11+ project types)
313
+ - [x] Entry point detection
314
+ - [x] `--doctor` install health check
315
+ - [x] `--uninstall` guided removal
316
+ - [x] Markdown renderer (`--format md`)
317
+ - [x] HTML renderer (`--format html`)
318
+ - [ ] YAML output
319
+ - [ ] `.torenignore` configuration file
320
+ - [ ] Plugin / custom renderer system
321
+ - [ ] Dependency graph analysis
322
+ - [ ] AI-generated project summary
323
+ - [ ] Project health score
324
+ - [ ] Architecture visualisation layer
325
+
326
+ ---
327
+
328
+ ## Contributing
329
+
330
+ Contributions are welcome and appreciated.
331
+
332
+ 1. **Fork** this repository
333
+ 2. **Create** a feature branch: `git checkout -b feature/my-feature`
334
+ 3. **Commit** your changes: `git commit -m "feat: add my feature"`
335
+ 4. **Push** to your branch: `git push origin feature/my-feature`
336
+ 5. **Open** a pull request
337
+
338
+ For bugs or feature requests, please [open an issue](https://github.com/your-username/toren/issues). Try to include a clear description and, for bugs, the output of `toren --doctor`.
339
+
340
+ **Code style notes:**
341
+ - Zero external runtime dependencies — keep it that way
342
+ - No TypeScript compilation step — plain ES modules only
343
+ - Keep scanner and renderers strictly decoupled
344
+ - Document public functions with JSDoc
345
+
346
+ ---
347
+
348
+ ## License
349
+
350
+ [MIT](LICENSE) — free to use, modify, and distribute.
351
+
352
+ ---
353
+
354
+ *If Toren saves you time, consider starring the repository — it helps others discover the project.* ⭐
package/bin/toren.js ADDED
@@ -0,0 +1,203 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview Toren CLI — Entry Point
4
+ *
5
+ * Usage:
6
+ * toren [path] Scan <path> (defaults to current directory)
7
+ * toren --format <type> Select output format (default: console)
8
+ * toren --json Legacy alias for --format json
9
+ * toren --version Print version
10
+ * toren --help Print usage
11
+ * toren --doctor Check global installation health
12
+ * toren --uninstall Safely remove global installation
13
+ *
14
+ * Adding a new output format
15
+ * ──────────────────────────
16
+ * 1. Create src/renderers/<format>-renderer.js
17
+ * and export: render(result, options?) => void
18
+ * 2. Register it in src/renderers/index.js
19
+ *
20
+ * No changes to this file are required.
21
+ */
22
+
23
+ import { createRequire } from 'node:module';
24
+ import { scan } from '../src/scanner/scan.js';
25
+ import renderers from '../src/renderers/index.js';
26
+ import { runDoctor, runUninstall } from '../src/lifecycle.js';
27
+
28
+ const require = createRequire(import.meta.url);
29
+ const pkg = require('../package.json');
30
+
31
+ /** Format used when no --format flag is supplied. */
32
+ const DEFAULT_FORMAT = 'console';
33
+
34
+ /** Derived from the registry — always in sync with available renderers. */
35
+ const SUPPORTED_FORMATS = Object.keys(renderers);
36
+
37
+ // ---------------------------------------------------------------------------
38
+ // Help
39
+ // ---------------------------------------------------------------------------
40
+
41
+ function printHelp() {
42
+ const formatList = SUPPORTED_FORMATS.map(f => ` ${f}`).join('\n');
43
+ console.log(`
44
+ \x1b[1mUsage:\x1b[0m
45
+ toren [path] Scan a directory (defaults to current directory)
46
+ toren --format <type> Select output format (default: console)
47
+ toren --help Show this help message
48
+ toren --version Show version number
49
+ toren --doctor Check global installation health
50
+ toren --uninstall Safely remove global installation
51
+
52
+ \x1b[1mOutput Formats:\x1b[0m
53
+ ${formatList}
54
+
55
+ console is the default.
56
+
57
+ \x1b[1mExamples:\x1b[0m
58
+ toren . Scan the current directory
59
+ toren ./my-project Scan a specific project folder
60
+ toren --format json Output results as JSON
61
+ toren --format json . Scan a path and output as JSON
62
+ `);
63
+ }
64
+
65
+ // ---------------------------------------------------------------------------
66
+ // Argument parsing
67
+ // ---------------------------------------------------------------------------
68
+
69
+ /**
70
+ * @typedef {Object} ParsedArgs
71
+ * @property {'exit'|'wait'|'scan'} action
72
+ * @property {string} [target] - Resolved path to scan
73
+ * @property {string} [format] - Renderer format name
74
+ */
75
+
76
+ /**
77
+ * Parse process.argv into a structured options object.
78
+ *
79
+ * Returns { action: 'exit' } when the process should exit immediately.
80
+ * Returns { action: 'wait' } when an async interactive command is running.
81
+ * Returns { action: 'scan', target, format } for normal scan operations.
82
+ *
83
+ * @returns {ParsedArgs}
84
+ */
85
+ function parseArgs() {
86
+ const args = process.argv.slice(2);
87
+
88
+ // ── Informational flags ─────────────────────────────────────────────────
89
+ if (args.includes('--help') || args.includes('-h')) {
90
+ printHelp();
91
+ return { action: 'exit' };
92
+ }
93
+
94
+ if (args.includes('--version') || args.includes('-v') || args.includes('--v')) {
95
+ console.log(pkg.version);
96
+ return { action: 'exit' };
97
+ }
98
+
99
+ // ── Lifecycle commands ──────────────────────────────────────────────────
100
+ if (args.includes('--doctor')) {
101
+ runDoctor(pkg.version);
102
+ return { action: 'exit' };
103
+ }
104
+
105
+ if (args.includes('--uninstall')) {
106
+ runUninstall();
107
+ return { action: 'wait' };
108
+ }
109
+
110
+ // ── Output format ───────────────────────────────────────────────────────
111
+ // --json is a legacy alias for --format json, kept for backwards
112
+ // compatibility. It will be removed in a future major version.
113
+ let format = DEFAULT_FORMAT;
114
+
115
+ const formatIdx = args.indexOf('--format');
116
+ if (formatIdx !== -1) {
117
+ // Accept the token immediately following --format.
118
+ // If the user omits the value (e.g. toren --format) default is used.
119
+ format = args[formatIdx + 1] ?? DEFAULT_FORMAT;
120
+ } else if (args.includes('--json')) {
121
+ format = 'json';
122
+ }
123
+
124
+ // ── Target path ─────────────────────────────────────────────────────────
125
+ // Build the set of tokens that are consumed as values by named flags so
126
+ // we don't accidentally treat them as the positional path argument.
127
+ // Currently only --format consumes a value token.
128
+ const consumedValues = new Set();
129
+ if (formatIdx !== -1 && args[formatIdx + 1] !== undefined) {
130
+ consumedValues.add(args[formatIdx + 1]);
131
+ }
132
+
133
+ // First non-flag, non-consumed token is the target path; default to cwd.
134
+ const target = args.find(a => !a.startsWith('-') && !consumedValues.has(a)) ?? '.';
135
+
136
+ // ── Unknown flag check ──────────────────────────────────────────────────
137
+ const knownFlags = new Set(['--help', '-h', '--version', '-v', '--v', '--doctor', '--uninstall', '--format', '--json']);
138
+ const unknownFlag = args.find(a => a.startsWith('-') && !knownFlags.has(a) && !consumedValues.has(a));
139
+
140
+ if (unknownFlag) {
141
+ console.error(`\x1b[31m Unknown flag: ${unknownFlag}\x1b[0m`);
142
+ console.error(` Run \x1b[36mtoren --help\x1b[0m for usage.\n`);
143
+ return { action: 'exit' };
144
+ }
145
+
146
+ return { action: 'scan', target, format };
147
+ }
148
+
149
+ // ---------------------------------------------------------------------------
150
+ // Format validation
151
+ // ---------------------------------------------------------------------------
152
+
153
+ /**
154
+ * Assert that `format` has a registered renderer.
155
+ * Prints a descriptive error message and exits with code 1 if not found.
156
+ *
157
+ * @param {string} format
158
+ */
159
+ function assertValidFormat(format) {
160
+ if (renderers[format]) return;
161
+
162
+ const list = SUPPORTED_FORMATS.map(f => ` • ${f}`).join('\n');
163
+ console.error('');
164
+ console.error(`\x1b[31m Unknown output format: ${format}\x1b[0m`);
165
+ console.error('');
166
+ console.error(' Supported formats:');
167
+ console.error('');
168
+ console.error(list);
169
+ console.error('');
170
+ console.error(' Run \x1b[36mtoren --help\x1b[0m for usage.');
171
+ console.error('');
172
+ process.exit(1);
173
+ }
174
+
175
+ // ---------------------------------------------------------------------------
176
+ // Main
177
+ // ---------------------------------------------------------------------------
178
+
179
+ (function main() {
180
+ const parsed = parseArgs();
181
+ if (parsed.action === 'exit') process.exit(0);
182
+ if (parsed.action === 'wait') return;
183
+
184
+ // Validate before scanning — fail fast on bad format names.
185
+ assertValidFormat(parsed.format);
186
+
187
+ const render = renderers[parsed.format];
188
+
189
+ try {
190
+ const result = scan(parsed.target);
191
+ render(result, { cwd: process.cwd() });
192
+ } catch (err) {
193
+ // Render errors in the requested format where possible.
194
+ if (parsed.format === 'json') {
195
+ console.error(JSON.stringify({ error: err.message }, null, 2));
196
+ } else {
197
+ console.error('');
198
+ console.error(`\x1b[31m ❌ Error: ${err.message}\x1b[0m`);
199
+ console.error('');
200
+ }
201
+ process.exit(1);
202
+ }
203
+ })();
package/package.json ADDED
@@ -0,0 +1,39 @@
1
+ {
2
+ "name": "@lakindu_perera/toren",
3
+ "version": "1.0.0",
4
+ "description": "A CLI that analyzes software codebases and generates onboarding insights.",
5
+ "type": "module",
6
+ "bin": {
7
+ "toren": "./bin/toren.js"
8
+ },
9
+ "preferGlobal": true,
10
+ "main": "bin/toren.js",
11
+ "files": [
12
+ "bin",
13
+ "src"
14
+ ],
15
+ "scripts": {
16
+ "start": "node bin/toren.js",
17
+ "lint": "node --check bin/toren.js src/scanner/scan.js"
18
+ },
19
+ "keywords": [
20
+ "cli",
21
+ "codebase",
22
+ "onboarding",
23
+ "developer-tool",
24
+ "scanner"
25
+ ],
26
+ "author": "Lakindu Perera",
27
+ "repository": {
28
+ "type": "git",
29
+ "url": "https://github.com/lakindudev/toren.git"
30
+ },
31
+ "bugs": {
32
+ "url": "https://github.com/lakindudev/toren/issues"
33
+ },
34
+ "homepage": "https://github.com/lakindudev/toren#readme",
35
+ "license": "MIT",
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ }
39
+ }