llmview 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/README.md +14 -8
- package/dist/build.js +14 -18
- package/dist/cli.js +52 -22
- package/dist/constants.js +3 -1
- package/dist/{git.js → ignore.js} +7 -4
- package/dist/{renderers.js → render-rules.js} +21 -41
- package/dist/render.js +21 -32
- package/package.json +2 -5
package/README.md
CHANGED
|
@@ -20,9 +20,9 @@ Create any number of view files in your project. These can be saved anywhere. Fo
|
|
|
20
20
|
new_feature.llmview
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
And use
|
|
23
|
+
And use one:
|
|
24
24
|
|
|
25
|
-
```
|
|
25
|
+
```bash
|
|
26
26
|
llmview .views/backend.llmview
|
|
27
27
|
```
|
|
28
28
|
|
|
@@ -96,24 +96,30 @@ my_project/
|
|
|
96
96
|
|
|
97
97
|
The `-n` argument includes line numbers in each file, similar to `cat -n`. This uses more tokens, but can also be useful context.
|
|
98
98
|
|
|
99
|
-
|
|
99
|
+
### Only list selected files
|
|
100
|
+
|
|
101
|
+
The `-l` argument lets you use selected files for something else besides rendering the context to stdout. For example, to create a zip of selected files.
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
llmview .views/backend.llmview -l | zip context.zip -@
|
|
105
|
+
```
|
|
100
106
|
|
|
101
|
-
|
|
107
|
+
### Using `llmview` as a filter
|
|
102
108
|
|
|
103
|
-
|
|
109
|
+
Instead of reading from a view file, you can use it as a filter. For example, to render all the unstaged changes in your repo:
|
|
104
110
|
|
|
105
111
|
```bash
|
|
106
|
-
|
|
112
|
+
git diff --name-only | llmview -
|
|
107
113
|
```
|
|
108
114
|
|
|
109
115
|
## Renderers
|
|
110
116
|
|
|
111
|
-
This tool comes with a set of opinionated file renderers
|
|
117
|
+
This tool comes with a set of opinionated file renderers based on the file extension. Currently they are:
|
|
112
118
|
|
|
113
119
|
- CSV (truncated by default, preserving the header and the first 10 lines)
|
|
114
120
|
- Excel, media files, and other non-text formats (omitted)
|
|
115
121
|
|
|
116
|
-
There is also a max size of 250KB per file. If a file is larger than that, it is not rendered.
|
|
122
|
+
There is also a max size of 250KB per file. If a code file is larger than that, it is not rendered. (If a CSV file is larger, it's still rendered and just truncated as usual.)
|
|
117
123
|
|
|
118
124
|
## Dry run to get statistics
|
|
119
125
|
|
package/dist/build.js
CHANGED
|
@@ -34,24 +34,19 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.buildDirectory = void 0;
|
|
37
|
-
const
|
|
38
|
-
const fs = __importStar(require("fs"));
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
39
38
|
const path = __importStar(require("path"));
|
|
40
|
-
const
|
|
39
|
+
const ignore_1 = require("./ignore");
|
|
41
40
|
const constants_1 = require("./constants");
|
|
42
|
-
const
|
|
43
|
-
return inputPath.replace(/^~/, os.homedir());
|
|
44
|
-
};
|
|
45
|
-
const buildDirectory = (projectPath) => {
|
|
46
|
-
projectPath = expandUser(projectPath);
|
|
41
|
+
const buildDirectory = async (projectPath) => {
|
|
47
42
|
const ignores = [
|
|
48
|
-
{ ig: (0,
|
|
43
|
+
{ ig: (0, ignore_1.createIgnore)(constants_1.BASE_IGNORE_CONTENT), scope: '' },
|
|
49
44
|
];
|
|
50
|
-
const rootGitignore = (0,
|
|
45
|
+
const rootGitignore = await (0, ignore_1.createIgnoreFromFile)(path.join(projectPath, '.gitignore'));
|
|
51
46
|
if (rootGitignore) {
|
|
52
47
|
ignores.push({ ig: rootGitignore, scope: '' });
|
|
53
48
|
}
|
|
54
|
-
const stat = fs.
|
|
49
|
+
const stat = await fs.stat(projectPath);
|
|
55
50
|
const rootNode = {
|
|
56
51
|
ino: stat.ino,
|
|
57
52
|
name: path.basename(projectPath),
|
|
@@ -62,17 +57,17 @@ const buildDirectory = (projectPath) => {
|
|
|
62
57
|
rootPath: projectPath,
|
|
63
58
|
children: [],
|
|
64
59
|
};
|
|
65
|
-
rootNode.children = buildChildrenNodes(projectPath, rootNode, ignores);
|
|
60
|
+
rootNode.children = await buildChildrenNodes(projectPath, rootNode, ignores);
|
|
66
61
|
return rootNode;
|
|
67
62
|
};
|
|
68
63
|
exports.buildDirectory = buildDirectory;
|
|
69
|
-
const buildChildrenNodes = (rootPath, parentNode, ignores) => {
|
|
64
|
+
const buildChildrenNodes = async (rootPath, parentNode, ignores) => {
|
|
70
65
|
const currentPath = path.join(rootPath, parentNode.relativePath);
|
|
71
|
-
const entries = fs.
|
|
66
|
+
const entries = await fs.readdir(currentPath);
|
|
72
67
|
const nodes = [];
|
|
73
68
|
for (const entry of entries) {
|
|
74
69
|
const entryFullPath = path.join(currentPath, entry);
|
|
75
|
-
const lstat = fs.
|
|
70
|
+
const lstat = await fs.lstat(entryFullPath);
|
|
76
71
|
if (lstat.isSymbolicLink()) {
|
|
77
72
|
continue;
|
|
78
73
|
}
|
|
@@ -80,7 +75,7 @@ const buildChildrenNodes = (rootPath, parentNode, ignores) => {
|
|
|
80
75
|
const nodeRelativePath = parentNode.relativePath
|
|
81
76
|
? `${parentNode.relativePath}/${entry}`
|
|
82
77
|
: entry;
|
|
83
|
-
if ((0,
|
|
78
|
+
if ((0, ignore_1.isPathIgnored)(nodeRelativePath, isDirectory, ignores)) {
|
|
84
79
|
continue;
|
|
85
80
|
}
|
|
86
81
|
const nodeBase = {
|
|
@@ -96,17 +91,18 @@ const buildChildrenNodes = (rootPath, parentNode, ignores) => {
|
|
|
96
91
|
type: 'directory',
|
|
97
92
|
children: [],
|
|
98
93
|
};
|
|
99
|
-
const nestedIg = (0,
|
|
94
|
+
const nestedIg = await (0, ignore_1.createIgnoreFromFile)(path.join(entryFullPath, '.gitignore'));
|
|
100
95
|
const childIgnores = nestedIg
|
|
101
96
|
? [...ignores, { ig: nestedIg, scope: nodeRelativePath }]
|
|
102
97
|
: ignores;
|
|
103
|
-
dirNode.children = buildChildrenNodes(rootPath, dirNode, childIgnores);
|
|
98
|
+
dirNode.children = await buildChildrenNodes(rootPath, dirNode, childIgnores);
|
|
104
99
|
nodes.push(dirNode);
|
|
105
100
|
}
|
|
106
101
|
else {
|
|
107
102
|
const fileNode = {
|
|
108
103
|
...nodeBase,
|
|
109
104
|
type: 'file',
|
|
105
|
+
size: lstat.size,
|
|
110
106
|
};
|
|
111
107
|
nodes.push(fileNode);
|
|
112
108
|
}
|
package/dist/cli.js
CHANGED
|
@@ -34,13 +34,43 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
};
|
|
35
35
|
})();
|
|
36
36
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
37
|
-
const fs = __importStar(require("fs"));
|
|
37
|
+
const fs = __importStar(require("fs/promises"));
|
|
38
38
|
const path = __importStar(require("path"));
|
|
39
39
|
const build_1 = require("./build");
|
|
40
40
|
const render_1 = require("./render");
|
|
41
41
|
const select_1 = require("./select");
|
|
42
|
-
const
|
|
43
|
-
|
|
42
|
+
const constants_1 = require("./constants");
|
|
43
|
+
const readStdin = () => {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
const chunks = [];
|
|
46
|
+
process.stdin.on('data', (chunk) => chunks.push(chunk));
|
|
47
|
+
process.stdin.on('end', () => resolve(Buffer.concat(chunks).toString('utf-8')));
|
|
48
|
+
process.stdin.on('error', reject);
|
|
49
|
+
});
|
|
50
|
+
};
|
|
51
|
+
const readPatterns = async (pathArg) => {
|
|
52
|
+
let content;
|
|
53
|
+
if (!pathArg || pathArg === '-') {
|
|
54
|
+
if (process.stdin.isTTY) {
|
|
55
|
+
console.error('Error: No input file specified and stdin is a terminal');
|
|
56
|
+
console.error('Usage: llmview <view-file> or pipe patterns via stdin');
|
|
57
|
+
process.exit(1);
|
|
58
|
+
}
|
|
59
|
+
content = await readStdin();
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
const resolvedPath = path.resolve(pathArg);
|
|
63
|
+
try {
|
|
64
|
+
content = await fs.readFile(resolvedPath, 'utf-8');
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
if (err.code === 'ENOENT') {
|
|
68
|
+
console.error(`File not found: ${resolvedPath}`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
}
|
|
71
|
+
throw err;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
44
74
|
return content
|
|
45
75
|
.split('\n')
|
|
46
76
|
.map((line) => line.trim())
|
|
@@ -51,15 +81,16 @@ const HELP_TEXT = `llmview - Generate LLM context from codebases using gitignore
|
|
|
51
81
|
Usage: llmview [options] <view-file>
|
|
52
82
|
|
|
53
83
|
Arguments:
|
|
54
|
-
<view-file> Path to a .llmview file
|
|
84
|
+
<view-file> Path to a .llmview file, or - to read patterns from stdin
|
|
55
85
|
|
|
56
86
|
Options:
|
|
87
|
+
-l, --list List selected files only (no content)
|
|
57
88
|
-n, --number Include line numbers in output
|
|
58
89
|
-t, --tree Include directory tree of selected files
|
|
59
90
|
-v, --verbose Print file statistics to stderr
|
|
60
91
|
-h, --help Show this help message
|
|
61
92
|
`;
|
|
62
|
-
const main = () => {
|
|
93
|
+
const main = async () => {
|
|
63
94
|
const args = process.argv.slice(2);
|
|
64
95
|
if (args.includes('-h') || args.includes('--help')) {
|
|
65
96
|
console.log(HELP_TEXT);
|
|
@@ -68,37 +99,36 @@ const main = () => {
|
|
|
68
99
|
const verbose = args.includes('-v') || args.includes('--verbose');
|
|
69
100
|
const includeTree = args.includes('-t') || args.includes('--tree');
|
|
70
101
|
const lineNumbers = args.includes('-n') || args.includes('--number');
|
|
71
|
-
const
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
process.exit(1);
|
|
75
|
-
}
|
|
76
|
-
const resolvedPath = path.resolve(llmviewPath);
|
|
77
|
-
if (!fs.existsSync(resolvedPath)) {
|
|
78
|
-
console.error(`File not found: ${resolvedPath}`);
|
|
79
|
-
process.exit(1);
|
|
80
|
-
}
|
|
81
|
-
const patterns = parseLlmviewFile(resolvedPath);
|
|
102
|
+
const listOnly = args.includes('-l') || args.includes('--list');
|
|
103
|
+
const positionalArgs = args.filter((arg) => !arg.startsWith('-'));
|
|
104
|
+
const patterns = await readPatterns(positionalArgs[0]);
|
|
82
105
|
if (patterns.length === 0) {
|
|
83
|
-
console.error('No patterns found in
|
|
106
|
+
console.error('No patterns found in input');
|
|
84
107
|
process.exit(1);
|
|
85
108
|
}
|
|
86
|
-
const rootNode = (0, build_1.buildDirectory)(process.cwd());
|
|
109
|
+
const rootNode = await (0, build_1.buildDirectory)(process.cwd());
|
|
87
110
|
const { selectedFiles, visiblePaths } = (0, select_1.selectFiles)(rootNode, patterns, {
|
|
88
111
|
verbose,
|
|
89
112
|
});
|
|
113
|
+
if (listOnly) {
|
|
114
|
+
selectedFiles.forEach((file) => console.log(file.relativePath));
|
|
115
|
+
process.exit(0);
|
|
116
|
+
}
|
|
90
117
|
let output = '';
|
|
91
118
|
if (includeTree) {
|
|
92
|
-
output += (0, render_1.
|
|
119
|
+
output += (0, render_1.renderDirectory)(rootNode, { verbose }, visiblePaths);
|
|
93
120
|
}
|
|
94
|
-
output += (0, render_1.renderFiles)(rootNode.rootPath, selectedFiles, {
|
|
121
|
+
output += await (0, render_1.renderFiles)(rootNode.rootPath, selectedFiles, {
|
|
95
122
|
verbose,
|
|
96
123
|
lineNumbers,
|
|
97
124
|
});
|
|
98
|
-
const estimatedTokens = Math.ceil(output.length /
|
|
125
|
+
const estimatedTokens = Math.ceil(output.length / constants_1.CHARS_PER_TOKEN_ESTIMATE);
|
|
99
126
|
if (verbose) {
|
|
100
127
|
console.warn(`Estimated tokens: ${estimatedTokens}`);
|
|
101
128
|
}
|
|
102
129
|
console.log(output);
|
|
103
130
|
};
|
|
104
|
-
main()
|
|
131
|
+
main().catch((err) => {
|
|
132
|
+
console.error(err);
|
|
133
|
+
process.exit(1);
|
|
134
|
+
});
|
package/dist/constants.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.MAX_FILE_SIZE_KB = exports.BASE_IGNORE_CONTENT = void 0;
|
|
3
|
+
exports.CHARS_PER_TOKEN_ESTIMATE = exports.CSV_PREVIEW_LINES = exports.MAX_FILE_SIZE_KB = exports.BASE_IGNORE_CONTENT = void 0;
|
|
4
4
|
exports.BASE_IGNORE_CONTENT = `.git
|
|
5
5
|
.DS_Store
|
|
6
6
|
__pycache__/
|
|
7
7
|
node_modules/`;
|
|
8
8
|
exports.MAX_FILE_SIZE_KB = 250;
|
|
9
|
+
exports.CSV_PREVIEW_LINES = 10;
|
|
10
|
+
exports.CHARS_PER_TOKEN_ESTIMATE = 4;
|
|
@@ -37,13 +37,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.isPathIgnored = exports.createIgnore = exports.createIgnoreFromFile = void 0;
|
|
40
|
-
const fs = __importStar(require("fs"));
|
|
40
|
+
const fs = __importStar(require("fs/promises"));
|
|
41
41
|
const ignore_1 = __importDefault(require("ignore"));
|
|
42
|
-
const createIgnoreFromFile = (gitignorePath) => {
|
|
43
|
-
|
|
42
|
+
const createIgnoreFromFile = async (gitignorePath) => {
|
|
43
|
+
try {
|
|
44
|
+
const content = await fs.readFile(gitignorePath, 'utf8');
|
|
45
|
+
return (0, ignore_1.default)().add(content);
|
|
46
|
+
}
|
|
47
|
+
catch {
|
|
44
48
|
return undefined;
|
|
45
49
|
}
|
|
46
|
-
return (0, ignore_1.default)().add(fs.readFileSync(gitignorePath, 'utf8'));
|
|
47
50
|
};
|
|
48
51
|
exports.createIgnoreFromFile = createIgnoreFromFile;
|
|
49
52
|
const createIgnore = (content) => {
|
|
@@ -35,47 +35,28 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.RENDER_RULES = exports.defaultRenderer = void 0;
|
|
37
37
|
const fs = __importStar(require("fs"));
|
|
38
|
+
const fsPromises = __importStar(require("fs/promises"));
|
|
38
39
|
const path = __importStar(require("path"));
|
|
40
|
+
const readline = __importStar(require("readline"));
|
|
39
41
|
const constants_1 = require("./constants");
|
|
40
|
-
const readFirstNLines = (filePath, n) => {
|
|
41
|
-
const fd = fs.openSync(filePath, 'r');
|
|
42
|
-
const bufferSize = 64 * 1024;
|
|
43
|
-
const buffer = Buffer.alloc(bufferSize);
|
|
42
|
+
const readFirstNLines = async (filePath, n) => {
|
|
44
43
|
const lines = [];
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
|
|
45
|
+
const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
47
46
|
try {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
break; // EOF
|
|
52
|
-
const chunk = leftover + buffer.toString('utf-8', 0, bytesRead);
|
|
53
|
-
const chunkLines = chunk.split('\n');
|
|
54
|
-
leftover = chunkLines.pop() || '';
|
|
55
|
-
for (const line of chunkLines) {
|
|
56
|
-
if (lines.length < n) {
|
|
57
|
-
lines.push(line);
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
60
|
-
hasMore = true;
|
|
61
|
-
break;
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
if (hasMore)
|
|
65
|
-
break;
|
|
66
|
-
}
|
|
67
|
-
if (!hasMore && lines.length >= n) {
|
|
68
|
-
if (leftover !== '') {
|
|
69
|
-
hasMore = true;
|
|
47
|
+
for await (const line of rl) {
|
|
48
|
+
if (lines.length < n) {
|
|
49
|
+
lines.push(line);
|
|
70
50
|
}
|
|
71
51
|
else {
|
|
72
|
-
|
|
52
|
+
return { lines, hasMore: true };
|
|
73
53
|
}
|
|
74
54
|
}
|
|
75
|
-
return { lines, hasMore };
|
|
55
|
+
return { lines, hasMore: false };
|
|
76
56
|
}
|
|
77
57
|
finally {
|
|
78
|
-
|
|
58
|
+
rl.close();
|
|
59
|
+
stream.destroy();
|
|
79
60
|
}
|
|
80
61
|
};
|
|
81
62
|
const numberLines = (lines) => {
|
|
@@ -86,13 +67,12 @@ const numberLines = (lines) => {
|
|
|
86
67
|
})
|
|
87
68
|
.join('\n');
|
|
88
69
|
};
|
|
89
|
-
const defaultRenderer = (rootPath, file, options) => {
|
|
70
|
+
const defaultRenderer = async (rootPath, file, options) => {
|
|
90
71
|
const fullPath = path.join(rootPath, file.relativePath);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
return `(File contents excluded: size ${(stats.size / 1024).toFixed(2)}KB exceeds ${constants_1.MAX_FILE_SIZE_KB}KB limit)`;
|
|
72
|
+
if (file.size > constants_1.MAX_FILE_SIZE_KB * 1024) {
|
|
73
|
+
return `(File contents excluded: size ${(file.size / 1024).toFixed(2)}KB exceeds ${constants_1.MAX_FILE_SIZE_KB}KB limit)`;
|
|
94
74
|
}
|
|
95
|
-
const content =
|
|
75
|
+
const content = await fsPromises.readFile(fullPath, 'utf-8');
|
|
96
76
|
if (options.lineNumbers) {
|
|
97
77
|
const lines = content.split('\n');
|
|
98
78
|
return numberLines(lines);
|
|
@@ -104,9 +84,9 @@ exports.RENDER_RULES = [
|
|
|
104
84
|
// csv
|
|
105
85
|
{
|
|
106
86
|
matcher: (name) => ['.csv'].some((ext) => name.toLowerCase().endsWith(ext)),
|
|
107
|
-
renderer: (rootPath, file, options) => {
|
|
87
|
+
renderer: async (rootPath, file, options) => {
|
|
108
88
|
const fullPath = path.join(rootPath, file.relativePath);
|
|
109
|
-
const { lines, hasMore } = readFirstNLines(fullPath,
|
|
89
|
+
const { lines, hasMore } = await readFirstNLines(fullPath, constants_1.CSV_PREVIEW_LINES);
|
|
110
90
|
const preview = options.lineNumbers
|
|
111
91
|
? numberLines(lines)
|
|
112
92
|
: lines.join('\n');
|
|
@@ -116,16 +96,16 @@ exports.RENDER_RULES = [
|
|
|
116
96
|
// excel
|
|
117
97
|
{
|
|
118
98
|
matcher: (name) => ['.xls', '.xlsx'].some((ext) => name.toLowerCase().endsWith(ext)),
|
|
119
|
-
renderer: (
|
|
99
|
+
renderer: async () => '(Contents excluded)',
|
|
120
100
|
},
|
|
121
101
|
// media
|
|
122
102
|
{
|
|
123
103
|
matcher: (name) => ['.ico', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.mp4'].some((ext) => name.toLowerCase().endsWith(ext)),
|
|
124
|
-
renderer: (
|
|
104
|
+
renderer: async () => '(Contents excluded)',
|
|
125
105
|
},
|
|
126
106
|
// misc
|
|
127
107
|
{
|
|
128
108
|
matcher: (name) => ['.pdf', '.zip'].some((ext) => name.toLowerCase().endsWith(ext)),
|
|
129
|
-
renderer: (
|
|
109
|
+
renderer: async () => '(Contents excluded)',
|
|
130
110
|
},
|
|
131
111
|
];
|
package/dist/render.js
CHANGED
|
@@ -1,48 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.renderFiles = exports.
|
|
4
|
-
const
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
3
|
+
exports.renderFiles = exports.renderDirectory = void 0;
|
|
4
|
+
const render_rules_1 = require("./render-rules");
|
|
5
|
+
const renderDirectory = (rootNode, options, visiblePaths) => {
|
|
6
|
+
return renderDirectoryNode(rootNode, options, visiblePaths, 0);
|
|
7
|
+
};
|
|
8
|
+
exports.renderDirectory = renderDirectory;
|
|
9
|
+
const renderDirectoryNode = (node, options, visiblePaths, currentDepth) => {
|
|
11
10
|
const { indentChar = ' ' } = options;
|
|
12
11
|
const indent = indentChar.repeat(currentDepth);
|
|
13
12
|
if (node.type === 'file') {
|
|
14
13
|
return `${indent}${node.name}`;
|
|
15
14
|
}
|
|
16
|
-
let result = `${indent}${node.name}/`;
|
|
17
|
-
if (node.children.length === 0) {
|
|
18
|
-
return result;
|
|
19
|
-
}
|
|
20
15
|
const childrenOutput = node.children
|
|
21
|
-
.
|
|
22
|
-
.
|
|
16
|
+
.filter((child) => !visiblePaths ||
|
|
17
|
+
!child.relativePath ||
|
|
18
|
+
visiblePaths.has(child.relativePath))
|
|
19
|
+
.map((child) => renderDirectoryNode(child, options, visiblePaths, currentDepth + 1))
|
|
23
20
|
.join('\n');
|
|
21
|
+
const result = `${indent}${node.name}/`;
|
|
24
22
|
if (currentDepth === 0) {
|
|
25
|
-
return
|
|
26
|
-
<directory>
|
|
27
|
-
${result}
|
|
28
|
-
${childrenOutput}
|
|
29
|
-
</directory>
|
|
30
|
-
\`\`\`\n\n`;
|
|
31
|
-
}
|
|
32
|
-
else {
|
|
33
|
-
return `${result}\n${childrenOutput}`;
|
|
23
|
+
return `\`\`\`\n<directory>\n${result}\n${childrenOutput}\n</directory>\n\`\`\`\n\n`;
|
|
34
24
|
}
|
|
25
|
+
return childrenOutput ? `${result}\n${childrenOutput}` : result;
|
|
35
26
|
};
|
|
36
|
-
|
|
37
|
-
const
|
|
38
|
-
return
|
|
39
|
-
.map((file) => renderFileBlock(rootPath, file, options))
|
|
40
|
-
.join('\n\n');
|
|
27
|
+
const renderFiles = async (rootPath, files, options) => {
|
|
28
|
+
const renderedBlocks = await Promise.all(files.map((file) => renderFile(rootPath, file, options)));
|
|
29
|
+
return renderedBlocks.join('\n\n');
|
|
41
30
|
};
|
|
42
31
|
exports.renderFiles = renderFiles;
|
|
43
|
-
const
|
|
32
|
+
const renderFile = async (rootPath, file, options) => {
|
|
44
33
|
const renderer = getRenderer(file);
|
|
45
|
-
const rendered = renderer(rootPath, file, options);
|
|
34
|
+
const rendered = await renderer(rootPath, file, options);
|
|
46
35
|
if (options.verbose) {
|
|
47
36
|
console.warn({ path: file.relativePath, length: rendered.length });
|
|
48
37
|
}
|
|
@@ -53,10 +42,10 @@ ${rendered}
|
|
|
53
42
|
\`\`\``;
|
|
54
43
|
};
|
|
55
44
|
const getRenderer = (file) => {
|
|
56
|
-
for (const { matcher, renderer } of
|
|
45
|
+
for (const { matcher, renderer } of render_rules_1.RENDER_RULES) {
|
|
57
46
|
if (matcher(file.name)) {
|
|
58
47
|
return renderer;
|
|
59
48
|
}
|
|
60
49
|
}
|
|
61
|
-
return
|
|
50
|
+
return render_rules_1.defaultRenderer;
|
|
62
51
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "llmview",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"bin": {
|
|
5
5
|
"llmview": "dist/cli.js"
|
|
6
6
|
},
|
|
@@ -22,10 +22,7 @@
|
|
|
22
22
|
"ts-node": "^10.9.2",
|
|
23
23
|
"typescript": "^5.8.2"
|
|
24
24
|
},
|
|
25
|
-
"files": [
|
|
26
|
-
"dist",
|
|
27
|
-
"README.md"
|
|
28
|
-
],
|
|
25
|
+
"files": ["dist", "README.md"],
|
|
29
26
|
"repository": {
|
|
30
27
|
"type": "git",
|
|
31
28
|
"url": "git+https://github.com/noahtren/llmview.git"
|