@lakindu_perera/toren 1.0.5 → 1.0.7

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 CHANGED
@@ -77,10 +77,10 @@ toren --doctor
77
77
  toren --uninstall
78
78
  ```
79
79
 
80
- ### All Flags
80
+ ### CLI Reference
81
81
 
82
- | Flag | Description |
83
- |------|-------------|
82
+ | Command / Flag | Description |
83
+ |----------------|-------------|
84
84
  | `[path]` | Directory to scan. Defaults to the current directory (`.`). |
85
85
  | `--project-type` | Show detected project type only. |
86
86
  | `--frameworks` | Show detected frameworks only. |
@@ -90,9 +90,9 @@ toren --uninstall
90
90
  | `--scripts` | Show available package scripts only. |
91
91
  | `--format <type>` | Output format: `console` (default), `json`, `markdown`, `html`. |
92
92
  | `--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. |
93
+ | `--max-files <n>` | Override the default 50,000-file scan limit. |
94
+ | `--help`, `-h` | Show usage and help message. |
95
+ | `--version`, `-v` | Print the installed version number. |
96
96
  | `--doctor` | Diagnose the global installation health. |
97
97
  | `--uninstall` | Safely remove Toren from the global npm environment. |
98
98
 
@@ -101,57 +101,30 @@ toren --uninstall
101
101
 
102
102
  ---
103
103
 
104
- ## Output Examples
104
+ ## Screenshots & Output Examples
105
105
 
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
112
-
113
- $ toren --frameworks
114
- Frameworks:
115
- - React
106
+ ### Console Output Example
107
+ The default `console` format renders a beautiful summary directly in your terminal:
116
108
 
117
- $ toren --entry-points
118
- Entry Points:
119
- - src/main.tsx
109
+ ```text
110
+ Toren v1.0.6 — Codebase Onboarding Intelligence
120
111
 
121
- $ toren --configs
122
- Configuration Files
123
- ───────────────────
112
+ Project Summary
113
+ ───────────────
124
114
 
125
- ✓ package.json
126
- vite.config.ts
115
+ Path: ./my-react-app
116
+ Project type: React
117
+ Total files: 32
118
+ Total folders: 6
127
119
 
128
- $ toren --scripts
129
- Available Scripts
120
+ Entry Points
121
+ ────────────
130
122
 
131
- start node bin/toren.js
132
- test node --test
133
- ```
123
+ src/main.tsx
134
124
 
135
- ### Console Output Example
136
- The default `console` format renders a beautiful summary directly in your terminal:
125
+ Folder Structure (first 20 files)
126
+ ──────────────────────────────────
137
127
 
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
128
  my-react-app/
156
129
  ├── public/
157
130
  │ └── vite.svg
@@ -165,10 +138,46 @@ my-react-app/
165
138
  ├── index.html
166
139
  ├── package.json
167
140
  └── vite.config.ts
168
- … and 21 more file(s) not shown
141
+ … and 21 more file(s) not shown
169
142
 
170
- ────────────────────────────────────────────────────────────────────────────────
171
- Scan complete.
143
+ Scan completed in 4 ms
144
+ ```
145
+
146
+ ### Focused Output Examples
147
+ 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:
148
+
149
+ ```bash
150
+ $ toren --project-type
151
+ Project Type
152
+ ────────────
153
+
154
+ Node.js / JavaScript
155
+
156
+ $ toren --frameworks
157
+ Frameworks
158
+ ──────────
159
+
160
+ React
161
+
162
+ $ toren --entry-points
163
+ Entry Points
164
+ ────────────
165
+
166
+ src/main.tsx
167
+
168
+ $ toren --configs
169
+ Configuration Files
170
+ ───────────────────
171
+
172
+ package.json
173
+ vite.config.ts
174
+
175
+ $ toren --scripts
176
+ Package Scripts
177
+ ───────────────
178
+
179
+ start vite
180
+ test vitest
172
181
  ```
173
182
 
174
183
  ### JSON Output Example
@@ -207,6 +216,17 @@ Generate machine-readable output for scripts, toolchains, or AI context windows
207
216
 
208
217
  ---
209
218
 
219
+ ## Release Notes
220
+
221
+ ### v1.0.6 - CLI Experience Update
222
+ - **Redesigned CLI Output**: Completely revamped the console layout to use minimal typography, bold section titles, and dynamic divider lines matching the title width.
223
+ - **Improved Error Handling**: Transformed raw exceptions into user-friendly error messages outlining the issue and potential fixes, ensuring consistent exit codes.
224
+ - **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.
225
+ - **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`.
226
+ - **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.
227
+
228
+ ---
229
+
210
230
  ## Architecture
211
231
 
212
232
  Toren's internal architecture emphasizes modular design, separation of concerns, and a strict **zero dependency** philosophy.
package/bin/toren.js CHANGED
@@ -38,43 +38,56 @@ 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
58
73
  --include-hidden Include hidden files and folders
59
74
  --max-files <n> Set scan file limit
60
- --help Show this help message
61
- --version Show version number
62
75
  --doctor Run CLI diagnostics
63
76
  --uninstall Remove Toren global installation
77
+ --help Show this help message
78
+ --version Show version number
64
79
 
65
- \x1b[1mOutput Formats:\x1b[0m
80
+ \x1b[1mOUTPUT FORMATS\x1b[0m
66
81
  ${formatList}
67
82
 
68
- console is the default.
69
-
70
- \x1b[1mExamples:\x1b[0m
83
+ \x1b[1mEXAMPLES\x1b[0m
71
84
  toren . Scan the current directory
72
85
  toren ./my-project Scan a specific project folder
73
86
  toren --format json . Output results as JSON
74
87
  toren --format markdown . > out.md Save a Markdown report to a file
75
88
  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
89
+ toren . --configs Show project configuration files
90
+ toren . --frameworks Show detected frameworks
78
91
  `);
79
92
  }
80
93
 
@@ -110,7 +123,7 @@ function parseArgs() {
110
123
  }
111
124
 
112
125
  if (args.includes('--version') || args.includes('-V') || args.includes('-v')) {
113
- console.log(pkg.version);
126
+ console.log(`Toren ${pkg.version}`);
114
127
  return { action: 'exit', code: 0 };
115
128
  }
116
129
 
@@ -135,13 +148,12 @@ function parseArgs() {
135
148
  const nextToken = args[formatIdx + 1];
136
149
  // If the next token is missing or starts with '-', the user omitted the value.
137
150
  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('');
151
+ printError(
152
+ 'Missing value',
153
+ 'The --format flag requires a valid output format.',
154
+ 'Supported formats',
155
+ SUPPORTED_FORMATS.join(', ')
156
+ );
145
157
  return { action: 'exit', code: 1 };
146
158
  }
147
159
  format = nextToken;
@@ -153,7 +165,7 @@ function parseArgs() {
153
165
  const focusedInfo = getFocusedModeInfo(args);
154
166
 
155
167
  if (focusedInfo.error) {
156
- console.error(focusedInfo.message);
168
+ printError(focusedInfo.title, focusedInfo.message, focusedInfo.detailLabel, focusedInfo.detailValue);
157
169
  return { action: 'exit', code: 1 };
158
170
  }
159
171
 
@@ -176,17 +188,22 @@ function parseArgs() {
176
188
  if (maxFilesIdx !== -1) {
177
189
  const rawVal = args[maxFilesIdx + 1];
178
190
  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('');
191
+ printError(
192
+ 'Missing value',
193
+ 'The --max-files flag requires a numeric value.',
194
+ 'Example',
195
+ 'toren --max-files 100000'
196
+ );
183
197
  return { action: 'exit', code: 1 };
184
198
  }
185
199
  maxFiles = parseInt(rawVal, 10);
186
200
  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('');
201
+ printError(
202
+ 'Invalid value',
203
+ 'The --max-files flag must be a positive integer.',
204
+ 'Provided value',
205
+ rawVal
206
+ );
190
207
  return { action: 'exit', code: 1 };
191
208
  }
192
209
  consumedValues.add(rawVal);
@@ -213,8 +230,12 @@ function parseArgs() {
213
230
  );
214
231
 
215
232
  if (unknownFlag) {
216
- console.error(`\x1b[31m Unknown flag: ${unknownFlag}\x1b[0m`);
217
- console.error(` Run \x1b[36mtoren --help\x1b[0m for usage.\n`);
233
+ printError(
234
+ 'Unknown option',
235
+ 'An unrecognized flag was provided.',
236
+ 'Flag',
237
+ unknownFlag
238
+ );
218
239
  return { action: 'exit', code: 1 };
219
240
  }
220
241
 
@@ -234,16 +255,12 @@ function parseArgs() {
234
255
  function assertValidFormat(format) {
235
256
  if (renderers[format]) return;
236
257
 
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('');
258
+ printError(
259
+ 'Unsupported output format',
260
+ 'The requested output format is not supported.',
261
+ 'Format',
262
+ format
263
+ );
247
264
  process.exit(1);
248
265
  }
249
266
 
@@ -280,9 +297,11 @@ function assertValidFormat(format) {
280
297
  if (parsed.format === 'json') {
281
298
  console.error(JSON.stringify({ error: err.message }, null, 2));
282
299
  } else {
283
- console.error('');
284
- console.error(`\x1b[31m ❌ Error: ${err.message}\x1b[0m`);
285
- console.error('');
300
+ if (err.title) {
301
+ printError(err.title, err.message, err.detailLabel, err.detailValue);
302
+ } else {
303
+ printError('Scan failed', err.message);
304
+ }
286
305
  }
287
306
  process.exit(1);
288
307
  }
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.7",
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": {
@@ -1,9 +1,33 @@
1
1
  /**
2
- * @fileoverview Focused output modes handling for Toren CLI.
2
+ * @fileoverview Toren Focused Output Mode Handler
3
+ *
4
+ * Handles the six focused output flags:
5
+ * --project-type --frameworks --entry-points
6
+ * --structure --configs --scripts
7
+ *
8
+ * Design contract:
9
+ * - Each flag renders exactly one section to stdout using the console visual
10
+ * style (ANSI colours, section header + divider).
11
+ * - Empty-state messages are canonical strings declared in EMPTY below.
12
+ * They must end with a period and stay in sync with console-renderer.js.
13
+ * - No business logic. All data comes from the ScanResult passed in.
14
+ * - Stateless: renderFocusedMode() may be called multiple times safely.
3
15
  */
4
16
 
5
17
  import { renderStructure } from './renderers/console-renderer.js';
6
18
 
19
+ // ---------------------------------------------------------------------------
20
+ // Canonical empty-state messages
21
+ // Keep these in sync with the equivalent strings in console-renderer.js.
22
+ // ---------------------------------------------------------------------------
23
+
24
+ const EMPTY = {
25
+ frameworks: 'No frameworks detected.',
26
+ entryPoints: 'No entry points detected.',
27
+ configs: 'No configuration files detected.',
28
+ scripts: 'No package scripts detected.',
29
+ };
30
+
7
31
  export const FOCUSED_FLAGS = [
8
32
  '--project-type',
9
33
  '--frameworks',
@@ -25,8 +49,10 @@ export function getFocusedModeInfo(args) {
25
49
  if (activeFlags.length > 1) {
26
50
  return {
27
51
  error: true,
28
- message: '\x1b[31mError: focused output flags are mutually exclusive. Please use only one of:\x1b[0m\n' +
29
- FOCUSED_FLAGS.map(f => ` ${f}`).join('\n') + '\n'
52
+ title: 'Conflicting options',
53
+ message: 'Focused output flags are mutually exclusive.',
54
+ detailLabel: 'Provided flags',
55
+ detailValue: activeFlags.join(', ')
30
56
  };
31
57
  }
32
58
 
@@ -36,6 +62,37 @@ export function getFocusedModeInfo(args) {
36
62
  };
37
63
  }
38
64
 
65
+ /**
66
+ * Print a bold-white section title followed by a dim matched-length divider
67
+ * and a blank line — same visual pattern as console-renderer.js's section().
68
+ *
69
+ * @param {string} title
70
+ */
71
+ function printSectionHeader(title) {
72
+ console.log(`\x1b[1m\x1b[97m${title}\x1b[0m`);
73
+ console.log(`\x1b[2m${'─'.repeat(title.length)}\x1b[0m`);
74
+ console.log('');
75
+ }
76
+
77
+ /**
78
+ * Print a focused section: header, then items or an empty-state message.
79
+ *
80
+ * @param {string} title - Section heading
81
+ * @param {string[]} items - Pre-formatted lines to print
82
+ * @param {string} emptyMsg - Canonical empty-state message (ends with '.')
83
+ */
84
+ function section(title, items, emptyMsg) {
85
+ printSectionHeader(title);
86
+
87
+ if (!items || items.length === 0) {
88
+ console.log(`\x1b[2m${emptyMsg}\x1b[0m`);
89
+ } else {
90
+ for (const item of items) {
91
+ console.log(item);
92
+ }
93
+ }
94
+ }
95
+
39
96
  /**
40
97
  * Renders the scan result based on the active focused mode.
41
98
  *
@@ -44,53 +101,46 @@ export function getFocusedModeInfo(args) {
44
101
  */
45
102
  export function renderFocusedMode(mode, result) {
46
103
  switch (mode) {
47
- case '--project-type':
48
- console.log(`Project Type: ${result.projectType}`);
104
+ case '--project-type': {
105
+ // projectType is always a string; 'Unknown' when undetected.
106
+ section('Project Type', [result.projectType], 'Unknown');
49
107
  break;
50
-
51
- case '--frameworks':
52
- if (!result.projectType || result.projectType === 'Unknown') {
53
- console.log('Frameworks: None detected');
54
- } else {
55
- console.log('Frameworks:');
56
- console.log(`- ${result.projectType}`);
57
- }
108
+ }
109
+
110
+ case '--frameworks': {
111
+ const frameworks = (!result.projectType || result.projectType === 'Unknown')
112
+ ? []
113
+ : [result.projectType];
114
+ section('Frameworks', frameworks, EMPTY.frameworks);
58
115
  break;
59
-
60
- case '--entry-points':
61
- if (!result.entryPoints || result.entryPoints.length === 0) {
62
- console.log('Entry Points: None detected');
63
- } else {
64
- console.log('Entry Points:');
65
- result.entryPoints.forEach(ep => console.log(`- ${ep}`));
66
- }
116
+ }
117
+
118
+ case '--entry-points': {
119
+ section('Entry Points', result.entryPoints || [], EMPTY.entryPoints);
67
120
  break;
68
-
69
- case '--structure':
121
+ }
122
+
123
+ case '--structure': {
70
124
  renderStructure(result);
71
125
  break;
72
-
73
- case '--configs':
74
- console.log('Configuration Files');
75
- console.log('───────────────────\n');
76
- if (!result.configs || result.configs.length === 0) {
77
- console.log('No configuration files detected.');
78
- } else {
79
- result.configs.forEach(c => console.log(`✓ ${c}`));
80
- }
126
+ }
127
+
128
+ case '--configs': {
129
+ section('Configuration Files', result.configs || [], EMPTY.configs);
81
130
  break;
131
+ }
82
132
 
83
- case '--scripts':
84
- console.log('Available Scripts\n');
85
- if (!result.scripts || result.scripts.length === 0) {
86
- console.log('No scripts found');
87
- } else {
133
+ case '--scripts': {
134
+ const scriptsList = [];
135
+ if (result.scripts && result.scripts.length > 0) {
88
136
  const maxNameLen = Math.max(...result.scripts.map(s => s.name.length));
89
- result.scripts.forEach(s => {
90
- const paddedName = s.name.padEnd(maxNameLen + 6, ' ');
91
- console.log(`${paddedName}${s.command}`);
92
- });
137
+ for (const s of result.scripts) {
138
+ const paddedName = s.name.padEnd(maxNameLen, ' ');
139
+ scriptsList.push(`\x1b[97m${paddedName}\x1b[0m \x1b[2m${s.command}\x1b[0m`);
140
+ }
93
141
  }
142
+ section('Package Scripts', scriptsList, EMPTY.scripts);
94
143
  break;
144
+ }
95
145
  }
96
146
  }