tokenlean 0.1.0 → 0.2.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/bin/tl-component.mjs +109 -83
- package/bin/tl-context.mjs +147 -98
- package/bin/tl-diff.mjs +95 -62
- package/bin/tl-related.mjs +112 -71
- package/bin/tl-search.mjs +142 -27
- package/bin/tl-structure.mjs +152 -83
- package/package.json +1 -1
package/bin/tl-search.mjs
CHANGED
|
@@ -22,7 +22,35 @@ if (process.argv.includes('--prompt')) {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
import { spawn, execSync } from 'child_process';
|
|
25
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
createOutput,
|
|
27
|
+
parseCommonArgs,
|
|
28
|
+
COMMON_OPTIONS_HELP
|
|
29
|
+
} from '../src/output.mjs';
|
|
30
|
+
import { loadConfig, CONFIG_FILENAME } from '../src/config.mjs';
|
|
31
|
+
|
|
32
|
+
const HELP = `
|
|
33
|
+
tl-search - Run pre-defined search patterns
|
|
34
|
+
|
|
35
|
+
Usage: tl-search <pattern-name> [options]
|
|
36
|
+
${COMMON_OPTIONS_HELP}
|
|
37
|
+
|
|
38
|
+
Configure patterns in ${CONFIG_FILENAME}:
|
|
39
|
+
{
|
|
40
|
+
"searchPatterns": {
|
|
41
|
+
"hooks": {
|
|
42
|
+
"description": "Find lifecycle hooks",
|
|
43
|
+
"pattern": "use(Effect|State|Callback)",
|
|
44
|
+
"glob": "**/*.{ts,tsx}"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
Examples:
|
|
50
|
+
tl-search # List available patterns
|
|
51
|
+
tl-search hooks # Run the "hooks" pattern
|
|
52
|
+
tl-search todos -j # JSON output
|
|
53
|
+
`;
|
|
26
54
|
|
|
27
55
|
// Check for ripgrep
|
|
28
56
|
try {
|
|
@@ -32,13 +60,14 @@ try {
|
|
|
32
60
|
process.exit(1);
|
|
33
61
|
}
|
|
34
62
|
|
|
35
|
-
function
|
|
63
|
+
function showPatterns(patterns, out) {
|
|
36
64
|
const names = Object.keys(patterns);
|
|
37
65
|
|
|
38
66
|
if (names.length === 0) {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
67
|
+
out.header('No search patterns defined.');
|
|
68
|
+
out.blank();
|
|
69
|
+
out.add(`Add patterns to ${CONFIG_FILENAME}:`);
|
|
70
|
+
out.add(`
|
|
42
71
|
{
|
|
43
72
|
"searchPatterns": {
|
|
44
73
|
"hooks": {
|
|
@@ -48,25 +77,37 @@ function showHelp(patterns) {
|
|
|
48
77
|
}
|
|
49
78
|
}
|
|
50
79
|
}`);
|
|
51
|
-
|
|
80
|
+
return;
|
|
52
81
|
}
|
|
53
82
|
|
|
54
|
-
|
|
83
|
+
out.header('Available search patterns:');
|
|
84
|
+
out.blank();
|
|
55
85
|
|
|
56
86
|
const maxLen = Math.max(...names.map(k => k.length));
|
|
57
87
|
|
|
88
|
+
// For JSON output, set data
|
|
89
|
+
out.setData('patterns', Object.entries(patterns).map(([name, config]) => ({
|
|
90
|
+
name,
|
|
91
|
+
description: config.description || '',
|
|
92
|
+
pattern: config.pattern,
|
|
93
|
+
glob: config.glob
|
|
94
|
+
})));
|
|
95
|
+
|
|
58
96
|
for (const [name, config] of Object.entries(patterns)) {
|
|
59
97
|
const paddedName = name.padEnd(maxLen);
|
|
60
|
-
|
|
98
|
+
out.add(` ${paddedName} ${config.description || '(no description)'}`);
|
|
61
99
|
}
|
|
62
100
|
|
|
63
|
-
|
|
64
|
-
|
|
101
|
+
out.blank();
|
|
102
|
+
out.add('Usage: tl-search <pattern-name>');
|
|
103
|
+
out.add('Example: tl-search hooks');
|
|
65
104
|
}
|
|
66
105
|
|
|
67
|
-
function runSearch(name, config, rootDir) {
|
|
68
|
-
|
|
69
|
-
|
|
106
|
+
function runSearch(name, config, rootDir, jsonMode) {
|
|
107
|
+
if (!jsonMode) {
|
|
108
|
+
console.log(`\nSearching: ${config.description || name}`);
|
|
109
|
+
console.log(`Pattern: ${config.pattern}\n`);
|
|
110
|
+
}
|
|
70
111
|
|
|
71
112
|
if (config.type === 'glob-only') {
|
|
72
113
|
const args = ['--files', '-g', config.pattern];
|
|
@@ -76,12 +117,39 @@ function runSearch(name, config, rootDir) {
|
|
|
76
117
|
}
|
|
77
118
|
}
|
|
78
119
|
args.push(rootDir);
|
|
79
|
-
|
|
80
|
-
|
|
120
|
+
|
|
121
|
+
if (jsonMode) {
|
|
122
|
+
// Capture output for JSON
|
|
123
|
+
try {
|
|
124
|
+
const result = execSync(`rg ${args.map(a => `"${a}"`).join(' ')}`, {
|
|
125
|
+
encoding: 'utf-8',
|
|
126
|
+
cwd: rootDir
|
|
127
|
+
});
|
|
128
|
+
const files = result.trim().split('\n').filter(Boolean);
|
|
129
|
+
console.log(JSON.stringify({
|
|
130
|
+
pattern: name,
|
|
131
|
+
description: config.description,
|
|
132
|
+
type: 'glob-only',
|
|
133
|
+
files,
|
|
134
|
+
count: files.length
|
|
135
|
+
}, null, 2));
|
|
136
|
+
} catch {
|
|
137
|
+
console.log(JSON.stringify({
|
|
138
|
+
pattern: name,
|
|
139
|
+
files: [],
|
|
140
|
+
count: 0
|
|
141
|
+
}, null, 2));
|
|
142
|
+
}
|
|
143
|
+
} else {
|
|
144
|
+
const proc = spawn('rg', args, { stdio: 'inherit' });
|
|
145
|
+
proc.on('close', code => process.exit(code === 1 ? 0 : code));
|
|
146
|
+
}
|
|
81
147
|
return;
|
|
82
148
|
}
|
|
83
149
|
|
|
84
|
-
const args =
|
|
150
|
+
const args = jsonMode
|
|
151
|
+
? ['-n', '--json', '-e', config.pattern]
|
|
152
|
+
: ['--color=always', '-n', '-e', config.pattern];
|
|
85
153
|
|
|
86
154
|
if (config.glob) {
|
|
87
155
|
args.push('-g', config.glob);
|
|
@@ -95,29 +163,76 @@ function runSearch(name, config, rootDir) {
|
|
|
95
163
|
|
|
96
164
|
args.push(rootDir);
|
|
97
165
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
166
|
+
if (jsonMode) {
|
|
167
|
+
try {
|
|
168
|
+
const result = execSync(`rg ${args.map(a => `"${a}"`).join(' ')} 2>/dev/null || true`, {
|
|
169
|
+
encoding: 'utf-8',
|
|
170
|
+
maxBuffer: 10 * 1024 * 1024
|
|
171
|
+
});
|
|
172
|
+
const matches = result.trim().split('\n')
|
|
173
|
+
.filter(line => line.startsWith('{'))
|
|
174
|
+
.map(line => {
|
|
175
|
+
try { return JSON.parse(line); } catch { return null; }
|
|
176
|
+
})
|
|
177
|
+
.filter(Boolean)
|
|
178
|
+
.filter(m => m.type === 'match')
|
|
179
|
+
.map(m => ({
|
|
180
|
+
file: m.data.path.text,
|
|
181
|
+
line: m.data.line_number,
|
|
182
|
+
text: m.data.lines.text.trim()
|
|
183
|
+
}));
|
|
184
|
+
|
|
185
|
+
console.log(JSON.stringify({
|
|
186
|
+
pattern: name,
|
|
187
|
+
description: config.description,
|
|
188
|
+
searchPattern: config.pattern,
|
|
189
|
+
matches,
|
|
190
|
+
count: matches.length
|
|
191
|
+
}, null, 2));
|
|
192
|
+
} catch {
|
|
193
|
+
console.log(JSON.stringify({
|
|
194
|
+
pattern: name,
|
|
195
|
+
matches: [],
|
|
196
|
+
count: 0
|
|
197
|
+
}, null, 2));
|
|
102
198
|
}
|
|
103
|
-
|
|
104
|
-
|
|
199
|
+
} else {
|
|
200
|
+
const proc = spawn('rg', args, { stdio: 'inherit' });
|
|
201
|
+
proc.on('close', code => {
|
|
202
|
+
if (code === 1) {
|
|
203
|
+
console.log('No matches found.');
|
|
204
|
+
}
|
|
205
|
+
process.exit(code === 1 ? 0 : code);
|
|
206
|
+
});
|
|
207
|
+
}
|
|
105
208
|
}
|
|
106
209
|
|
|
107
210
|
// Main
|
|
211
|
+
const args = process.argv.slice(2);
|
|
212
|
+
const options = parseCommonArgs(args);
|
|
213
|
+
|
|
214
|
+
if (options.help) {
|
|
215
|
+
console.log(HELP);
|
|
216
|
+
process.exit(0);
|
|
217
|
+
}
|
|
218
|
+
|
|
108
219
|
const { config, projectRoot } = loadConfig();
|
|
109
220
|
const patterns = config.searchPatterns || {};
|
|
110
|
-
const patternName =
|
|
221
|
+
const patternName = options.remaining.find(a => !a.startsWith('-'));
|
|
111
222
|
|
|
112
|
-
if (!patternName
|
|
113
|
-
|
|
223
|
+
if (!patternName) {
|
|
224
|
+
const out = createOutput(options);
|
|
225
|
+
showPatterns(patterns, out);
|
|
226
|
+
out.print();
|
|
114
227
|
process.exit(0);
|
|
115
228
|
}
|
|
116
229
|
|
|
117
230
|
if (!patterns[patternName]) {
|
|
118
231
|
console.error(`\nUnknown pattern: "${patternName}"`);
|
|
119
|
-
|
|
232
|
+
const out = createOutput({ ...options, json: false });
|
|
233
|
+
showPatterns(patterns, out);
|
|
234
|
+
out.print();
|
|
120
235
|
process.exit(1);
|
|
121
236
|
}
|
|
122
237
|
|
|
123
|
-
runSearch(patternName, patterns[patternName], projectRoot);
|
|
238
|
+
runSearch(patternName, patterns[patternName], projectRoot, options.json);
|
package/bin/tl-structure.mjs
CHANGED
|
@@ -9,9 +9,26 @@
|
|
|
9
9
|
* Usage: tl-structure [path] [--depth N]
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
+
// Prompt info for tl-prompt
|
|
13
|
+
if (process.argv.includes('--prompt')) {
|
|
14
|
+
console.log(JSON.stringify({
|
|
15
|
+
name: 'tl-structure',
|
|
16
|
+
desc: 'Project overview with token estimates',
|
|
17
|
+
when: 'before-read',
|
|
18
|
+
example: 'tl-structure'
|
|
19
|
+
}));
|
|
20
|
+
process.exit(0);
|
|
21
|
+
}
|
|
22
|
+
|
|
12
23
|
import { readdirSync, readFileSync, existsSync } from 'fs';
|
|
13
24
|
import { join, basename } from 'path';
|
|
14
|
-
import {
|
|
25
|
+
import {
|
|
26
|
+
createOutput,
|
|
27
|
+
parseCommonArgs,
|
|
28
|
+
estimateTokens,
|
|
29
|
+
formatTokens,
|
|
30
|
+
COMMON_OPTIONS_HELP
|
|
31
|
+
} from '../src/output.mjs';
|
|
15
32
|
import {
|
|
16
33
|
getSkipDirs,
|
|
17
34
|
getImportantFiles,
|
|
@@ -19,6 +36,25 @@ import {
|
|
|
19
36
|
} from '../src/project.mjs';
|
|
20
37
|
import { getConfig } from '../src/config.mjs';
|
|
21
38
|
|
|
39
|
+
const HELP = `
|
|
40
|
+
tl-structure - Smart project overview with context estimates
|
|
41
|
+
|
|
42
|
+
Usage: tl-structure [path] [options]
|
|
43
|
+
|
|
44
|
+
Options:
|
|
45
|
+
--depth N, -d N Maximum depth to show (default: 3)
|
|
46
|
+
${COMMON_OPTIONS_HELP}
|
|
47
|
+
|
|
48
|
+
Configure defaults in .tokenleanrc.json:
|
|
49
|
+
"structure": { "depth": 3, "important": ["src", "lib"] }
|
|
50
|
+
|
|
51
|
+
Examples:
|
|
52
|
+
tl-structure # Current directory
|
|
53
|
+
tl-structure src/ -d 2 # Just src, 2 levels deep
|
|
54
|
+
tl-structure -j # JSON output
|
|
55
|
+
tl-structure -q # Quiet (no headers)
|
|
56
|
+
`;
|
|
57
|
+
|
|
22
58
|
function getDirStats(dirPath, skipDirs, importantDirs) {
|
|
23
59
|
let totalTokens = 0;
|
|
24
60
|
let fileCount = 0;
|
|
@@ -38,80 +74,104 @@ function getDirStats(dirPath, skipDirs, importantDirs) {
|
|
|
38
74
|
const content = readFileSync(fullPath, 'utf-8');
|
|
39
75
|
totalTokens += estimateTokens(content);
|
|
40
76
|
fileCount++;
|
|
41
|
-
} catch
|
|
77
|
+
} catch { /* skip binary */ }
|
|
42
78
|
}
|
|
43
79
|
}
|
|
44
|
-
} catch
|
|
80
|
+
} catch { /* permission error */ }
|
|
45
81
|
}
|
|
46
82
|
|
|
47
83
|
walk(dirPath);
|
|
48
84
|
return { totalTokens, fileCount };
|
|
49
85
|
}
|
|
50
86
|
|
|
51
|
-
function
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
87
|
+
function buildTree(dirPath, depth, maxDepth, skipDirs, importantDirs, importantFiles) {
|
|
88
|
+
const tree = [];
|
|
89
|
+
if (depth > maxDepth) return tree;
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const entries = readdirSync(dirPath, { withFileTypes: true });
|
|
93
|
+
|
|
94
|
+
// Sort: directories first, then by importance, then alphabetically
|
|
95
|
+
entries.sort((a, b) => {
|
|
96
|
+
if (a.isDirectory() !== b.isDirectory()) return a.isDirectory() ? -1 : 1;
|
|
97
|
+
const aImportant = importantDirs.has(a.name) || importantFiles.has(a.name);
|
|
98
|
+
const bImportant = importantDirs.has(b.name) || importantFiles.has(b.name);
|
|
99
|
+
if (aImportant !== bImportant) return aImportant ? -1 : 1;
|
|
100
|
+
return a.name.localeCompare(b.name);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const filtered = entries.filter(e => {
|
|
104
|
+
if (e.name.startsWith('.') && e.name !== '.claude') return false;
|
|
105
|
+
if (skipDirs.has(e.name)) return false;
|
|
106
|
+
return true;
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
for (const entry of filtered) {
|
|
110
|
+
const fullPath = join(dirPath, entry.name);
|
|
111
|
+
const isImportant = importantDirs.has(entry.name) || importantFiles.has(entry.name);
|
|
112
|
+
|
|
113
|
+
if (entry.isDirectory()) {
|
|
114
|
+
const stats = getDirStats(fullPath, skipDirs, importantDirs);
|
|
115
|
+
tree.push({
|
|
116
|
+
name: entry.name,
|
|
117
|
+
type: 'dir',
|
|
118
|
+
important: isImportant,
|
|
119
|
+
fileCount: stats.fileCount,
|
|
120
|
+
tokens: stats.totalTokens,
|
|
121
|
+
children: buildTree(fullPath, depth + 1, maxDepth, skipDirs, importantDirs, importantFiles)
|
|
122
|
+
});
|
|
123
|
+
} else {
|
|
124
|
+
try {
|
|
125
|
+
const content = readFileSync(fullPath, 'utf-8');
|
|
126
|
+
const tokens = estimateTokens(content);
|
|
127
|
+
const lines = content.split('\n').length;
|
|
128
|
+
tree.push({
|
|
129
|
+
name: entry.name,
|
|
130
|
+
type: 'file',
|
|
131
|
+
important: isImportant,
|
|
132
|
+
tokens,
|
|
133
|
+
lines
|
|
134
|
+
});
|
|
135
|
+
} catch {
|
|
136
|
+
tree.push({
|
|
137
|
+
name: entry.name,
|
|
138
|
+
type: 'file',
|
|
139
|
+
important: isImportant,
|
|
140
|
+
binary: true
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
} catch { /* permission error */ }
|
|
64
146
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (skipDirs.has(e.name)) return false;
|
|
68
|
-
return true;
|
|
69
|
-
});
|
|
147
|
+
return tree;
|
|
148
|
+
}
|
|
70
149
|
|
|
71
|
-
|
|
72
|
-
|
|
150
|
+
function printTree(tree, out, prefix = '') {
|
|
151
|
+
tree.forEach((entry, index) => {
|
|
152
|
+
const isLast = index === tree.length - 1;
|
|
73
153
|
const connector = isLast ? '└── ' : '├── ';
|
|
74
|
-
const
|
|
154
|
+
const marker = entry.important ? '*' : ' ';
|
|
75
155
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
if (entry.isDirectory()) {
|
|
80
|
-
const stats = getDirStats(fullPath, skipDirs, importantDirs);
|
|
81
|
-
const sizeInfo = stats.fileCount > 0
|
|
82
|
-
? ` (${stats.fileCount} files, ~${formatTokens(stats.totalTokens)})`
|
|
156
|
+
if (entry.type === 'dir') {
|
|
157
|
+
const sizeInfo = entry.fileCount > 0
|
|
158
|
+
? ` (${entry.fileCount} files, ~${formatTokens(entry.tokens)})`
|
|
83
159
|
: ' (empty)';
|
|
84
|
-
|
|
85
|
-
console.log(`${prefix}${connector}${marker}${entry.name}/${sizeInfo}`);
|
|
160
|
+
out.add(`${prefix}${connector}${marker}${entry.name}/${sizeInfo}`);
|
|
86
161
|
|
|
87
162
|
const newPrefix = prefix + (isLast ? ' ' : '│ ');
|
|
88
|
-
printTree(
|
|
163
|
+
printTree(entry.children, out, newPrefix);
|
|
164
|
+
} else if (entry.binary) {
|
|
165
|
+
out.add(`${prefix}${connector}${marker}${entry.name} (binary)`);
|
|
89
166
|
} else {
|
|
90
|
-
|
|
91
|
-
const content = readFileSync(fullPath, 'utf-8');
|
|
92
|
-
const tokens = estimateTokens(content);
|
|
93
|
-
const lines = content.split('\n').length;
|
|
94
|
-
console.log(`${prefix}${connector}${marker}${entry.name} (~${formatTokens(tokens)}, ${lines}L)`);
|
|
95
|
-
} catch (e) {
|
|
96
|
-
console.log(`${prefix}${connector}${marker}${entry.name} (binary)`);
|
|
97
|
-
}
|
|
167
|
+
out.add(`${prefix}${connector}${marker}${entry.name} (~${formatTokens(entry.tokens)}, ${entry.lines}L)`);
|
|
98
168
|
}
|
|
99
169
|
});
|
|
100
170
|
}
|
|
101
171
|
|
|
102
|
-
// Prompt info for tl-prompt
|
|
103
|
-
if (process.argv.includes('--prompt')) {
|
|
104
|
-
console.log(JSON.stringify({
|
|
105
|
-
name: 'tl-structure',
|
|
106
|
-
desc: 'Project overview with token estimates',
|
|
107
|
-
when: 'before-read',
|
|
108
|
-
example: 'tl-structure'
|
|
109
|
-
}));
|
|
110
|
-
process.exit(0);
|
|
111
|
-
}
|
|
112
|
-
|
|
113
172
|
// Main
|
|
114
173
|
const args = process.argv.slice(2);
|
|
174
|
+
const options = parseCommonArgs(args);
|
|
115
175
|
|
|
116
176
|
// Get config defaults
|
|
117
177
|
const structureConfig = getConfig('structure') || {};
|
|
@@ -119,43 +179,52 @@ const structureConfig = getConfig('structure') || {};
|
|
|
119
179
|
let targetPath = '.';
|
|
120
180
|
let maxDepth = structureConfig.depth || 3;
|
|
121
181
|
|
|
122
|
-
//
|
|
123
|
-
|
|
124
|
-
const
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
for (let i = 0; i < args.length; i++) {
|
|
128
|
-
if ((args[i] === '--depth' || args[i] === '-d') && args[i + 1]) {
|
|
129
|
-
maxDepth = parseInt(args[i + 1], 10);
|
|
182
|
+
// Parse tool-specific options
|
|
183
|
+
for (let i = 0; i < options.remaining.length; i++) {
|
|
184
|
+
const arg = options.remaining[i];
|
|
185
|
+
if ((arg === '--depth' || arg === '-d') && options.remaining[i + 1]) {
|
|
186
|
+
maxDepth = parseInt(options.remaining[i + 1], 10);
|
|
130
187
|
i++;
|
|
131
|
-
} else if (
|
|
132
|
-
|
|
133
|
-
tl-structure - Smart project overview with context estimates
|
|
134
|
-
|
|
135
|
-
Usage: tl-structure [path] [options]
|
|
136
|
-
|
|
137
|
-
Options:
|
|
138
|
-
--depth N, -d N Maximum depth to show (default: ${structureConfig.depth || 3})
|
|
139
|
-
--help, -h Show this help
|
|
140
|
-
|
|
141
|
-
Configure defaults in .tokenleanrc.json:
|
|
142
|
-
"structure": { "depth": 3, "important": ["src", "lib"] }
|
|
143
|
-
`);
|
|
144
|
-
process.exit(0);
|
|
145
|
-
} else if (!args[i].startsWith('-')) {
|
|
146
|
-
targetPath = args[i];
|
|
188
|
+
} else if (!arg.startsWith('-')) {
|
|
189
|
+
targetPath = arg;
|
|
147
190
|
}
|
|
148
191
|
}
|
|
149
192
|
|
|
193
|
+
if (options.help) {
|
|
194
|
+
console.log(HELP);
|
|
195
|
+
process.exit(0);
|
|
196
|
+
}
|
|
197
|
+
|
|
150
198
|
if (!existsSync(targetPath)) {
|
|
151
199
|
console.error(`Path not found: ${targetPath}`);
|
|
152
200
|
process.exit(1);
|
|
153
201
|
}
|
|
154
202
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
203
|
+
// Get combined sets (defaults + user config extensions)
|
|
204
|
+
const skipDirs = getSkipDirs();
|
|
205
|
+
const importantDirs = getImportantDirs();
|
|
206
|
+
const importantFiles = getImportantFiles();
|
|
159
207
|
|
|
160
|
-
|
|
161
|
-
|
|
208
|
+
const out = createOutput(options);
|
|
209
|
+
|
|
210
|
+
const rootStats = getDirStats(targetPath, skipDirs, importantDirs);
|
|
211
|
+
const tree = buildTree(targetPath, 0, maxDepth, skipDirs, importantDirs, importantFiles);
|
|
212
|
+
|
|
213
|
+
// Set JSON data
|
|
214
|
+
out.setData('path', targetPath);
|
|
215
|
+
out.setData('totalFiles', rootStats.fileCount);
|
|
216
|
+
out.setData('totalTokens', rootStats.totalTokens);
|
|
217
|
+
out.setData('depth', maxDepth);
|
|
218
|
+
out.setData('tree', tree);
|
|
219
|
+
|
|
220
|
+
// Headers
|
|
221
|
+
const rootName = targetPath === '.' ? basename(process.cwd()) : targetPath;
|
|
222
|
+
out.header(rootName);
|
|
223
|
+
out.header(`Total: ${rootStats.fileCount} files, ~${formatTokens(rootStats.totalTokens)} tokens`);
|
|
224
|
+
out.header(`(* = important for understanding project)`);
|
|
225
|
+
out.blank();
|
|
226
|
+
|
|
227
|
+
// Tree output
|
|
228
|
+
printTree(tree, out);
|
|
229
|
+
|
|
230
|
+
out.print();
|