@lakindu_perera/toren 1.0.5 → 1.0.8

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/CHANGELOG.md ADDED
@@ -0,0 +1,19 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.0.7] - 2026-08-29
9
+ ### Added
10
+ - **Project Intelligence Upgrade**: Added Package Manager detection, project metadata extraction (name, language, runtime, architecture), and important file detection.
11
+ - **Project Health Checks**: New `--health` flag to evaluate repository health (checking for README, License, linting, tests, docker files).
12
+ - **Richer Output Formats**: Expanded HTML and Markdown renderers to include the newly extracted project details, ensuring a highly readable and comprehensive project overview.
13
+ - **Enhanced JSON schema**: Output JSON schema has been substantially updated to support these rich data properties in a deterministic order.
14
+ - **New CLI flags**: `--summary`, `--important-files`, and `--health`.
15
+
16
+ ### Changed
17
+ - Refactored `ScanResult` object with `packageManager`, `projectInfo`, `health`, and `importantFiles`.
18
+ - Updated test suite for new CLI flags and deterministic JSON output.
19
+
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Toren — Codebase Analyzer CLI
1
+ # Toren — Fast Repository Discovery CLI
2
2
 
3
3
  > The fastest way to understand any project structure. A zero-dependency codebase scanner CLI for modern developers.
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@lakindu_perera/toren.svg)](https://www.npmjs.com/package/@lakindu_perera/toren)
5
+ [![npm version](https://img.shields.io/npm/v/@lakindudev/toren.svg)](https://www.npmjs.com/package/@lakindudev/toren)
6
6
  [![Node.js Version](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org)
7
7
  [![Zero Dependencies](https://img.shields.io/badge/dependencies-zero-success.svg)](package.json)
8
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
@@ -23,6 +23,8 @@ Built with zero external dependencies, this **Node.js repository explorer** is t
23
23
  - **Framework Detection**: Instantly identifies Node.js, React, Next.js, Vue, Nuxt, Angular, Svelte, Python, Go, Rust, Spring Boot, Ruby, PHP, Elixir and more.
24
24
  - **Entry Point Detection**: Automatically pinpoints where execution begins (e.g., `index.js`, `main.ts`, `App.tsx`, `main.go`).
25
25
  - **Project Structure Visualizer**: Generates clean, hierarchical file trees.
26
+ - **Project Health Diagnostics**: Provides observations and warnings for common project health issues.
27
+ - **Package Manager Detection**: Identifies whether a project uses npm, pnpm, yarn, bun, etc.
26
28
  - **Multiple Output Formats**: Choose between `console` (default), `json`, `markdown`, or `html` reports.
27
29
  - **Zero Dependencies**: A pure Node.js CLI tool with no external runtime packages. Lightning fast install, infinitely secure.
28
30
  - **CLI Lifecycle Tools**: Native diagnostic and uninstallation tools (`--doctor`, `--uninstall`).
@@ -34,7 +36,7 @@ Built with zero external dependencies, this **Node.js repository explorer** is t
34
36
  Install Toren globally via npm to make the **repository inspection CLI** available anywhere on your machine:
35
37
 
36
38
  ```bash
37
- npm install -g @lakindu_perera/toren
39
+ npm install -g @lakindudev/toren
38
40
  ```
39
41
 
40
42
  **Requirements:** Node.js 18.0.0 or higher.
@@ -69,6 +71,9 @@ toren --entry-points
69
71
  toren --structure
70
72
  toren --configs
71
73
  toren --scripts
74
+ toren --summary
75
+ toren --important-files
76
+ toren --health
72
77
 
73
78
  # Lifecycle & Help Commands
74
79
  toren --help
@@ -77,10 +82,10 @@ toren --doctor
77
82
  toren --uninstall
78
83
  ```
79
84
 
80
- ### All Flags
85
+ ### CLI Reference
81
86
 
82
- | Flag | Description |
83
- |------|-------------|
87
+ | Command / Flag | Description |
88
+ |----------------|-------------|
84
89
  | `[path]` | Directory to scan. Defaults to the current directory (`.`). |
85
90
  | `--project-type` | Show detected project type only. |
86
91
  | `--frameworks` | Show detected frameworks only. |
@@ -88,70 +93,46 @@ toren --uninstall
88
93
  | `--structure` | Show repository structure only. |
89
94
  | `--configs` | Show detected project configuration files. |
90
95
  | `--scripts` | Show available package scripts only. |
96
+ | `--summary` | Show project summary only. |
97
+ | `--important-files` | Show important files only. |
98
+ | `--health` | Show project health observations only. |
91
99
  | `--format <type>` | Output format: `console` (default), `json`, `markdown`, `html`. |
92
100
  | `--include-hidden` | Include hidden files and dot-directories in the scan. |
93
- | `--max-files <N>` | Override the default 50,000-file scan limit. |
94
- | `--help` / `-h` | Show usage information. |
95
- | `--version` / `-V` | Print the installed version number. |
101
+ | `--max-files <n>` | Override the default 50,000-file scan limit. |
102
+ | `--help`, `-h` | Show usage and help message. |
103
+ | `--version`, `-v` | Print the installed version number. |
96
104
  | `--doctor` | Diagnose the global installation health. |
97
105
  | `--uninstall` | Safely remove Toren from the global npm environment. |
98
106
 
99
107
  > **Note:** `--format md` is not a valid alias. Use `--format markdown` in full.
100
- > **Note:** Focused output flags (`--project-type`, `--frameworks`, `--entry-points`, `--structure`, `--configs`, `--scripts`) are mutually exclusive.
108
+ > **Note:** Focused output flags are mutually exclusive.
101
109
 
102
110
  ---
103
111
 
104
- ## Output Examples
112
+ ## Screenshots & Output Examples
105
113
 
106
- ### Focused Output Example
107
- Sometimes you only need a specific piece of intelligence for use in a script or a quick lookup. Use the focused output flags to bypass the full report:
108
-
109
- ```bash
110
- $ toren --project-type
111
- Project Type: Node.js / JavaScript
114
+ ### Console Output Example
115
+ The default `console` format renders a beautiful summary directly in your terminal:
112
116
 
113
- $ toren --frameworks
114
- Frameworks:
115
- - React
117
+ ```text
118
+ Toren v1.0.7 — Fast Repository Discovery CLI
116
119
 
117
- $ toren --entry-points
118
- Entry Points:
119
- - src/main.tsx
120
+ Project Summary
121
+ ───────────────
120
122
 
121
- $ toren --configs
122
- Configuration Files
123
- ───────────────────
123
+ Path: ./my-react-app
124
+ Project type: React
125
+ Total files: 32
126
+ Total folders: 6
124
127
 
125
- package.json
126
- ✓ vite.config.ts
128
+ Entry Points
129
+ ────────────
127
130
 
128
- $ toren --scripts
129
- Available Scripts
131
+ src/main.tsx
130
132
 
131
- start node bin/toren.js
132
- test node --test
133
- ```
133
+ Folder Structure (first 20 files)
134
+ ──────────────────────────────────
134
135
 
135
- ### Console Output Example
136
- The default `console` format renders a beautiful summary directly in your terminal:
137
-
138
- ```text
139
- Toren v1.0.4 — Codebase Onboarding Intelligence
140
-
141
- Project Summary
142
- ────────────────────────────────────────────────────────────────────────────────
143
- Path: ./my-react-app
144
- Project type: React
145
- Total files: 32
146
- Total folders: 6
147
- Scan duration: 4 ms
148
-
149
- Entry Points
150
- ────────────────────────────────────────────────────────────────────────────────
151
- → src/main.tsx
152
-
153
- Folder Structure (first 20 files)
154
- ────────────────────────────────────────────────────────────────────────────────
155
136
  my-react-app/
156
137
  ├── public/
157
138
  │ └── vite.svg
@@ -165,10 +146,27 @@ my-react-app/
165
146
  ├── index.html
166
147
  ├── package.json
167
148
  └── vite.config.ts
168
- … and 21 more file(s) not shown
149
+ … and 21 more file(s) not shown
150
+
151
+ Scan completed in 4 ms
152
+ ```
153
+
154
+ ### Focused Output Examples
155
+ Sometimes you only need a specific piece of intelligence for use in a script or a quick lookup. Use the focused output flags to bypass the full report:
169
156
 
170
- ────────────────────────────────────────────────────────────────────────────────
171
- Scan complete.
157
+ ```bash
158
+ $ toren --project-type
159
+ Project Type
160
+ ────────────
161
+
162
+ Node.js / JavaScript
163
+
164
+ $ toren --scripts
165
+ Package Scripts
166
+ ───────────────
167
+
168
+ start vite
169
+ test vitest
172
170
  ```
173
171
 
174
172
  ### JSON Output Example
@@ -176,19 +174,54 @@ Generate machine-readable output for scripts, toolchains, or AI context windows
176
174
 
177
175
  ```json
178
176
  {
177
+ "meta": {
178
+ "version": "1.0.7",
179
+ "scanDurationMs": 4,
180
+ "timestamp": "2026-08-29T23:51:00.000Z"
181
+ },
179
182
  "project": {
183
+ "name": "my-react-app",
180
184
  "path": "./my-react-app",
181
185
  "type": "React",
182
- "framework": "React"
183
- },
184
- "summary": {
185
- "totalFiles": 32,
186
- "totalFolders": 6,
187
- "scanDurationMs": 4
186
+ "framework": "React",
187
+ "packageManager": "npm"
188
188
  },
189
+ "frameworks": [
190
+ "React"
191
+ ],
189
192
  "entryPoints": [
190
193
  "src/main.tsx"
191
194
  ],
195
+ "configs": [
196
+ "package.json",
197
+ "vite.config.ts"
198
+ ],
199
+ "scripts": [
200
+ {
201
+ "name": "start",
202
+ "command": "vite",
203
+ "description": "Start the development server",
204
+ "category": "development",
205
+ "usage": "npm start"
206
+ }
207
+ ],
208
+ "importantFiles": [
209
+ {
210
+ "path": "package.json",
211
+ "reason": "Defines dependencies"
212
+ }
213
+ ],
214
+ "health": [
215
+ {
216
+ "id": "readme",
217
+ "status": "pass",
218
+ "message": "README file is present"
219
+ }
220
+ ],
221
+ "statistics": {
222
+ "totalFiles": 32,
223
+ "totalFolders": 6
224
+ },
192
225
  "structure": [
193
226
  {
194
227
  "type": "folder",
@@ -199,7 +232,12 @@ Generate machine-readable output for scripts, toolchains, or AI context windows
199
232
  ]
200
233
  },
201
234
  { "type": "file", "name": "package.json" }
202
- ]
235
+ ],
236
+ "summary": {
237
+ "totalFiles": 32,
238
+ "totalFolders": 6,
239
+ "scanDurationMs": 4
240
+ }
203
241
  }
204
242
  ```
205
243
 
@@ -207,6 +245,23 @@ Generate machine-readable output for scripts, toolchains, or AI context windows
207
245
 
208
246
  ---
209
247
 
248
+ ## Release Notes
249
+
250
+ ### v1.0.7 - Intelligence Upgrade
251
+ - **Project Intelligence Upgrade**: Added Package Manager detection, project metadata extraction (name, language, runtime, architecture), and important file detection.
252
+ - **Project Health Checks**: New `--health` flag to evaluate repository health (checking for README, License, linting, tests, docker files).
253
+ - **Richer Output Formats**: Expanded HTML and Markdown renderers to include the newly extracted project details, ensuring a highly readable and comprehensive project overview.
254
+ - **Enhanced JSON schema**: Output JSON schema has been substantially updated to support these rich data properties in a deterministic order.
255
+
256
+ ### v1.0.6 - CLI Experience Update
257
+ - **Redesigned CLI Output**: Completely revamped the console layout to use minimal typography, bold section titles, and dynamic divider lines matching the title width.
258
+ - **Improved Error Handling**: Transformed raw exceptions into user-friendly error messages outlining the issue and potential fixes, ensuring consistent exit codes.
259
+ - **Enhanced Focused Modes**: Focused output flags (`--configs`, `--entry-points`, `--structure`, etc.) now strictly mirror the design tokens of the full console renderer, providing identical spacing and layouts.
260
+ - **Streamlined Help & Version**: The `--help` interface was fully redesigned for faster reading, and `-v` / `--version` flags were added to output the exact binary version directly from `package.json`.
261
+ - **Accurate Timing Metrics**: Scan duration logic was improved to utilize native timing APIs, precisely measuring execution and printing results natively as "Scan completed in X ms" without clogging the project summary.
262
+
263
+ ---
264
+
210
265
  ## Architecture
211
266
 
212
267
  Toren's internal architecture emphasizes modular design, separation of concerns, and a strict **zero dependency** philosophy.
@@ -253,6 +308,60 @@ Contributions to improve this codebase analyzer CLI are always welcome!
253
308
 
254
309
  ---
255
310
 
311
+ ## Maintainer: Releasing a New Version
312
+
313
+ Toren uses **automated npm publishing via GitHub Actions** and [npm Trusted Publishing (OIDC)](https://docs.npmjs.com/generating-provenance-statements).
314
+ **Do not run `npm publish` manually** — GitHub Actions owns the publish step.
315
+
316
+ ### Release checklist
317
+
318
+ ```text
319
+ 1. Implement changes on a feature branch.
320
+
321
+ 2. Update the version in package.json:
322
+ "version": "1.0.8"
323
+
324
+ 3. Run tests and lint locally:
325
+ npm test
326
+ npm run lint
327
+
328
+ 4. Commit the version bump and any other changes:
329
+ git commit -m "chore: release v1.0.8"
330
+
331
+ 5. Open a Pull Request → merge to main.
332
+
333
+ 6. On GitHub, create a new Release:
334
+ Tag: v1.0.8 ← must match package.json exactly (with leading v)
335
+ Title: v1.0.8
336
+ Body: paste CHANGELOG.md entry
337
+
338
+ 7. Click "Publish Release".
339
+
340
+ 8. GitHub Actions runs automatically:
341
+ ✔ npm ci
342
+ ✔ npm test (all 233+ tests must pass)
343
+ ✔ npm run lint
344
+ ✔ tag format validated (vX.Y.Z)
345
+ ✔ tag v1.0.8 == package.json 1.0.8
346
+ ✔ npm pack --dry-run
347
+ ✔ npm publish --access public --provenance
348
+
349
+ 9. @lakindudev/toren@1.0.8 is live on npmjs.com.
350
+ ```
351
+
352
+ ### If the workflow fails
353
+
354
+ | Failure step | Cause | Fix |
355
+ |---|---|---|
356
+ | Run tests | A test is failing | Fix the test, push, re-create release |
357
+ | Run lint | Syntax error in source | Fix lint error, push, re-create release |
358
+ | Validate tag format | Tag is not `vX.Y.Z` | Delete the release, re-create with correct tag |
359
+ | Verify version matches | `package.json` not bumped | Update `package.json`, push, re-create release |
360
+ | Verify package contents | `files` array misconfigured | Fix `package.json`, push, re-create release |
361
+ | Publish to npm | Trusted Publisher not configured | Follow the npm Trusted Publisher setup in `.github/workflows/publish.yml` header |
362
+
363
+ ---
364
+
256
365
  ## License
257
366
 
258
367
  Distributed under the MIT License. See `LICENSE` for more information.
package/bin/toren.js CHANGED
@@ -38,43 +38,59 @@ const DEFAULT_FORMAT = 'console';
38
38
  const SUPPORTED_FORMATS = Object.keys(renderers);
39
39
 
40
40
  // ---------------------------------------------------------------------------
41
- // Help
41
+ // Helpers
42
42
  // ---------------------------------------------------------------------------
43
43
 
44
+ function printError(title, message, detailLabel, detailValue) {
45
+ console.error(`\x1b[31m✖ ${title}\x1b[0m\n`);
46
+ console.error(`${message}\n`);
47
+ if (detailLabel && detailValue) {
48
+ console.error(`${detailLabel}:\n`);
49
+ console.error(`${detailValue}\n`);
50
+ }
51
+ }
52
+
44
53
  function printHelp() {
45
- const formatList = SUPPORTED_FORMATS.map(f => ` ${f}`).join('\n');
54
+ const formatList = SUPPORTED_FORMATS.map(f => {
55
+ const suffix = f === DEFAULT_FORMAT ? ' (default)' : '';
56
+ return ` --format ${f.padEnd(10)}${suffix}`;
57
+ }).join('\n');
58
+
46
59
  console.log(`
47
- \x1b[1mUsage:\x1b[0m
60
+ \x1b[1mToren\x1b[0m v${pkg.version}
61
+ Fast Repository Discovery CLI
62
+
63
+ \x1b[1mUSAGE\x1b[0m
48
64
  toren [path] [options]
49
65
 
50
- \x1b[1mOptions:\x1b[0m
66
+ \x1b[1mCOMMANDS\x1b[0m
51
67
  --project-type Show detected project type only
52
68
  --frameworks Show detected frameworks only
53
69
  --entry-points Show detected entry points only
54
70
  --structure Show repository structure only
55
71
  --configs Show detected project configuration files
56
72
  --scripts Show available package scripts only
57
- --format <type> Output as console, json, markdown, or html
73
+ --summary Show project summary only
74
+ --important-files Show important files only
75
+ --health Show project health observations only
58
76
  --include-hidden Include hidden files and folders
59
77
  --max-files <n> Set scan file limit
60
- --help Show this help message
61
- --version Show version number
62
78
  --doctor Run CLI diagnostics
63
79
  --uninstall Remove Toren global installation
80
+ --help Show this help message
81
+ --version Show version number
64
82
 
65
- \x1b[1mOutput Formats:\x1b[0m
83
+ \x1b[1mOUTPUT FORMATS\x1b[0m
66
84
  ${formatList}
67
85
 
68
- console is the default.
69
-
70
- \x1b[1mExamples:\x1b[0m
86
+ \x1b[1mEXAMPLES\x1b[0m
71
87
  toren . Scan the current directory
72
88
  toren ./my-project Scan a specific project folder
73
89
  toren --format json . Output results as JSON
74
90
  toren --format markdown . > out.md Save a Markdown report to a file
75
91
  toren --format html . > out.html Save an HTML report to a file
76
- toren --include-hidden . Include hidden dot-files in scan
77
- toren --max-files 100000 . Override the 50k file scan limit
92
+ toren . --configs Show project configuration files
93
+ toren . --frameworks Show detected frameworks
78
94
  `);
79
95
  }
80
96
 
@@ -110,7 +126,7 @@ function parseArgs() {
110
126
  }
111
127
 
112
128
  if (args.includes('--version') || args.includes('-V') || args.includes('-v')) {
113
- console.log(pkg.version);
129
+ console.log(`Toren ${pkg.version}`);
114
130
  return { action: 'exit', code: 0 };
115
131
  }
116
132
 
@@ -135,13 +151,12 @@ function parseArgs() {
135
151
  const nextToken = args[formatIdx + 1];
136
152
  // If the next token is missing or starts with '-', the user omitted the value.
137
153
  if (nextToken === undefined || nextToken.startsWith('-')) {
138
- console.error('');
139
- console.error('\x1b[31m --format requires a value.\x1b[0m');
140
- console.error('');
141
- console.error(` Supported formats: ${SUPPORTED_FORMATS.join(', ')}`);
142
- console.error('');
143
- console.error(' Run \x1b[36mtoren --help\x1b[0m for usage.');
144
- console.error('');
154
+ printError(
155
+ 'Missing value',
156
+ 'The --format flag requires a valid output format.',
157
+ 'Supported formats',
158
+ SUPPORTED_FORMATS.join(', ')
159
+ );
145
160
  return { action: 'exit', code: 1 };
146
161
  }
147
162
  format = nextToken;
@@ -153,7 +168,7 @@ function parseArgs() {
153
168
  const focusedInfo = getFocusedModeInfo(args);
154
169
 
155
170
  if (focusedInfo.error) {
156
- console.error(focusedInfo.message);
171
+ printError(focusedInfo.title, focusedInfo.message, focusedInfo.detailLabel, focusedInfo.detailValue);
157
172
  return { action: 'exit', code: 1 };
158
173
  }
159
174
 
@@ -176,17 +191,22 @@ function parseArgs() {
176
191
  if (maxFilesIdx !== -1) {
177
192
  const rawVal = args[maxFilesIdx + 1];
178
193
  if (rawVal === undefined || rawVal.startsWith('-')) {
179
- console.error('');
180
- console.error('\x1b[31m --max-files requires a numeric value.\x1b[0m');
181
- console.error(' Example: toren --max-files 100000');
182
- console.error('');
194
+ printError(
195
+ 'Missing value',
196
+ 'The --max-files flag requires a numeric value.',
197
+ 'Example',
198
+ 'toren --max-files 100000'
199
+ );
183
200
  return { action: 'exit', code: 1 };
184
201
  }
185
202
  maxFiles = parseInt(rawVal, 10);
186
203
  if (isNaN(maxFiles) || maxFiles < 1) {
187
- console.error('');
188
- console.error(`\x1b[31m --max-files must be a positive integer, got: ${rawVal}\x1b[0m`);
189
- console.error('');
204
+ printError(
205
+ 'Invalid value',
206
+ 'The --max-files flag must be a positive integer.',
207
+ 'Provided value',
208
+ rawVal
209
+ );
190
210
  return { action: 'exit', code: 1 };
191
211
  }
192
212
  consumedValues.add(rawVal);
@@ -213,8 +233,12 @@ function parseArgs() {
213
233
  );
214
234
 
215
235
  if (unknownFlag) {
216
- console.error(`\x1b[31m Unknown flag: ${unknownFlag}\x1b[0m`);
217
- console.error(` Run \x1b[36mtoren --help\x1b[0m for usage.\n`);
236
+ printError(
237
+ 'Unknown option',
238
+ 'An unrecognized flag was provided.',
239
+ 'Flag',
240
+ unknownFlag
241
+ );
218
242
  return { action: 'exit', code: 1 };
219
243
  }
220
244
 
@@ -234,16 +258,12 @@ function parseArgs() {
234
258
  function assertValidFormat(format) {
235
259
  if (renderers[format]) return;
236
260
 
237
- const list = SUPPORTED_FORMATS.map(f => ` • ${f}`).join('\n');
238
- console.error('');
239
- console.error(`\x1b[31m Unknown output format: ${format}\x1b[0m`);
240
- console.error('');
241
- console.error(' Supported formats:');
242
- console.error('');
243
- console.error(list);
244
- console.error('');
245
- console.error(' Run \x1b[36mtoren --help\x1b[0m for usage.');
246
- console.error('');
261
+ printError(
262
+ 'Unsupported output format',
263
+ 'The requested output format is not supported.',
264
+ 'Format',
265
+ format
266
+ );
247
267
  process.exit(1);
248
268
  }
249
269
 
@@ -280,9 +300,11 @@ function assertValidFormat(format) {
280
300
  if (parsed.format === 'json') {
281
301
  console.error(JSON.stringify({ error: err.message }, null, 2));
282
302
  } else {
283
- console.error('');
284
- console.error(`\x1b[31m ❌ Error: ${err.message}\x1b[0m`);
285
- console.error('');
303
+ if (err.title) {
304
+ printError(err.title, err.message, err.detailLabel, err.detailValue);
305
+ } else {
306
+ printError('Scan failed', err.message);
307
+ }
286
308
  }
287
309
  process.exit(1);
288
310
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lakindu_perera/toren",
3
- "version": "1.0.5",
3
+ "version": "1.0.8",
4
4
  "description": "A powerful codebase scanner CLI tool for project analysis, framework detection, and understanding repository structure instantly.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -10,12 +10,16 @@
10
10
  "bin",
11
11
  "src",
12
12
  "LICENSE",
13
- "README.md"
13
+ "README.md",
14
+ "CHANGELOG.md"
14
15
  ],
16
+ "publishConfig": {
17
+ "access": "public"
18
+ },
15
19
  "scripts": {
16
20
  "start": "node bin/toren.js",
17
21
  "test": "node --test",
18
- "lint": "node --check bin/toren.js src/scanner/scan.js src/lifecycle.js src/renderers/index.js src/renderers/console-renderer.js src/renderers/json-renderer.js src/renderers/markdown-renderer.js src/renderers/html-renderer.js"
22
+ "lint": "node --check bin/toren.js src/scanner/scan.js src/lifecycle.js src/renderers/index.js src/renderers/console-renderer.js src/renderers/json-renderer.js src/renderers/markdown-renderer.js src/renderers/html-renderer.js src/detectors/config-detector.js src/detectors/script-detector.js src/detectors/package-manager-detector.js"
19
23
  },
20
24
  "keywords": [
21
25
  "cli tool",
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Detect project health observations.
3
+ */
4
+ export function detectHealth({
5
+ flatFiles,
6
+ configs,
7
+ importantFiles,
8
+ scripts,
9
+ projectType
10
+ }) {
11
+ const health = [];
12
+ const fileSet = new Set(flatFiles);
13
+
14
+ // README
15
+ if (fileSet.has('README.md') || fileSet.has('README') || fileSet.has('readme.md')) {
16
+ health.push({ id: 'readme', status: 'pass', message: 'README found' });
17
+ } else {
18
+ health.push({ id: 'readme', status: 'warning', message: 'README not found' });
19
+ }
20
+
21
+ // LICENSE
22
+ if (fileSet.has('LICENSE') || fileSet.has('LICENSE.md') || fileSet.has('LICENSE.txt')) {
23
+ health.push({ id: 'license', status: 'pass', message: 'LICENSE found' });
24
+ } else {
25
+ health.push({ id: 'license', status: 'warning', message: 'LICENSE not found' });
26
+ }
27
+
28
+ // ENVIRONMENT EXAMPLE
29
+ if (fileSet.has('.env.example') || fileSet.has('.env.sample')) {
30
+ health.push({ id: 'env-example', status: 'pass', message: 'Environment example found' });
31
+ }
32
+
33
+ // TESTS
34
+ const hasTestFiles = flatFiles.some(f =>
35
+ f.startsWith('test/') ||
36
+ f.startsWith('tests/') ||
37
+ f.startsWith('__tests__/') ||
38
+ f.includes('.test.') ||
39
+ f.includes('.spec.')
40
+ );
41
+ const hasTestScripts = (scripts || []).some(s => s.category === 'testing' || s.name === 'test');
42
+ if (hasTestFiles || hasTestScripts) {
43
+ health.push({ id: 'tests', status: 'pass', message: 'Tests detected' });
44
+ }
45
+
46
+ // LINTER
47
+ const hasLinter = (configs || []).some(c =>
48
+ c.includes('eslint') ||
49
+ c.includes('biome') ||
50
+ c.includes('stylelint')
51
+ ) || (scripts || []).some(s => s.category === 'quality' || s.name === 'lint');
52
+ if (hasLinter) {
53
+ health.push({ id: 'lint', status: 'pass', message: 'Linter detected' });
54
+ }
55
+
56
+ // TYPESCRIPT
57
+ if (fileSet.has('tsconfig.json')) {
58
+ health.push({ id: 'typescript', status: 'pass', message: 'TypeScript configuration found' });
59
+ }
60
+
61
+ // GIT
62
+ if (fileSet.has('.gitignore')) {
63
+ health.push({ id: 'git', status: 'info', message: '.gitignore found' });
64
+ }
65
+
66
+ // DOCKER
67
+ if (fileSet.has('Dockerfile') || fileSet.has('docker-compose.yml') || fileSet.has('compose.yml')) {
68
+ health.push({ id: 'docker', status: 'info', message: 'Docker configuration found' });
69
+ }
70
+
71
+ return { health };
72
+ }