@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 +21 -0
- package/README.md +354 -0
- package/bin/toren.js +203 -0
- package/package.json +39 -0
- package/src/lifecycle.js +124 -0
- package/src/renderers/console-renderer.js +218 -0
- package/src/renderers/html-renderer.js +913 -0
- package/src/renderers/index.js +41 -0
- package/src/renderers/json-renderer.js +54 -0
- package/src/renderers/markdown-renderer.js +328 -0
- package/src/renderers/tree-renderer.js +67 -0
- package/src/scanner/scan.js +341 -0
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
|
+
[](https://www.npmjs.com/package/toren)
|
|
6
|
+
[](LICENSE)
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](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
|
+
}
|