llmview 0.2.0 → 0.3.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 CHANGED
@@ -1,13 +1,15 @@
1
1
  # llmview
2
2
 
3
- `llmview` is a small command-line tool for producing repeatable views of code for LLMs. You can configure views for different aspects of a large project and reuse them as the code evolves.
3
+ Generate LLM-friendly context from codebases using repeatable view files.
4
+
5
+ Designed for a middle ground in AI-assisted coding. Rather than copying files manually or relying on an LLM agent to search your codebase, define views once and reuse them as your code evolves. This can often "one-shot" problems by preparing the right context.
4
6
 
5
7
  ## Quick start
6
8
 
7
9
  Install from npm
8
10
 
9
11
  ```bash
10
- npm install -g llmview
12
+ npm i -g llmview
11
13
  ```
12
14
 
13
15
  Create any number of view files in your project. These can be saved anywhere. For example, a full-stack monorepo:
@@ -30,23 +32,20 @@ Run `llmview --help` for all options.
30
32
 
31
33
  ## How it works
32
34
 
33
- A view is a list of glob patterns to select. It's the same format as `.gitignore`, but it says which patterns to select rather than ignore.
35
+ A view is a list of glob patterns to select files. Use `**` for recursive matching.
34
36
 
35
37
  ```gitignore
36
38
  # Code
37
- backend/**/**
39
+ backend/**
38
40
  !backend/migrations/**
39
41
 
40
42
  # Docs
41
43
  docs/style_guide.md
42
44
  ```
43
45
 
44
- It will find all files that satisfy the glob patterns. It also respects existing `.gitignore` files (even nested ones) if your project is version controlled. If any files are ignored by git, they are also ignored here even if the view file would have selected it.
45
-
46
- After selecting, it serializes the contents of each file into a LLM-friendly format and prints to stdout.
46
+ After selecting, it outputs the contents of each file into a LLM-friendly format and prints to stdout.
47
47
 
48
- ````
49
- ```
48
+ ```xml
50
49
  <file path="backend/main.py">
51
50
  from flask import Flask
52
51
 
@@ -60,9 +59,6 @@ if __name__ == "__main__":
60
59
  app.run(debug=True)
61
60
 
62
61
  </file>
63
- ```
64
-
65
- ```
66
62
  <file path="docs/style_guide.md">
67
63
  # Style guide
68
64
 
@@ -70,14 +66,12 @@ Make no mistakes
70
66
 
71
67
  </file>
72
68
  ```
73
- ````
74
69
 
75
- ### Including the project directory
70
+ ### Include the project directory
76
71
 
77
- The `-t` argument includes the file system hierarchy for all selected files at the beginning of the result.
72
+ The `-t` argument includes the file system directory for all selected files at the beginning of the result.
78
73
 
79
- ````
80
- ```
74
+ ```xml
81
75
  <directory>
82
76
  my_project/
83
77
  backend/
@@ -85,16 +79,38 @@ my_project/
85
79
  docs/
86
80
  style_guide.md
87
81
  </directory>
82
+ <file path="backend/main.py">
83
+ ...
88
84
  ```
89
85
 
86
+ ### Include line numbers
87
+
88
+ The `-n` argument includes line numbers in each file, similar to `cat -n`. This uses more tokens, but can also be useful context.
89
+
90
+ ### JSON output
91
+
92
+ The `-j` argument outputs the result as JSON instead of XML tags. This can be useful for piping into other tools like `jq`.
93
+
94
+ ```bash
95
+ llmview -j .views/backend.llmview | jq '.files[].path'
90
96
  ```
91
- <file path="my_project/backend/main.py">
92
- ...
93
- ````
94
97
 
95
- ### Including line numbers
98
+ The JSON structure:
99
+
100
+ ```json
101
+ {
102
+ "directory": null,
103
+ "files": [
104
+ {
105
+ "path": "backend/main.py",
106
+ "size": 245,
107
+ "content": "..."
108
+ }
109
+ ]
110
+ }
111
+ ```
96
112
 
97
- The `-n` argument includes line numbers in each file, similar to `cat -n`. This uses more tokens, but can also be useful context.
113
+ The `directory` field is populated when using `-t`.
98
114
 
99
115
  ### Only list selected files
100
116
 
@@ -104,9 +120,9 @@ The `-l` argument lets you use selected files for something else besides renderi
104
120
  llmview .views/backend.llmview -l | zip context.zip -@
105
121
  ```
106
122
 
107
- ### Using `llmview` as a filter
123
+ ### Using as a filter
108
124
 
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:
125
+ Instead of reading from a view file, you can use it as a filter by reading from stdin. For example, to render all the unstaged changes in your repo:
110
126
 
111
127
  ```bash
112
128
  git diff --name-only | llmview -
@@ -121,9 +137,9 @@ This tool comes with a set of opinionated file renderers based on the file exten
121
137
 
122
138
  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.)
123
139
 
124
- ## Dry run to get statistics
140
+ ## Verbose mode
125
141
 
126
- To see what files will be included and an estimate of tokens used, try a command like this:
142
+ To see what files will be included and an estimate of tokens used, use a verbose `-v` command like this:
127
143
 
128
144
  ```bash
129
145
  llmview -v .views/backend.llmview > /dev/null
package/dist/cli.js CHANGED
@@ -1,82 +1,15 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
- if (k2 === undefined) k2 = k;
5
- var desc = Object.getOwnPropertyDescriptor(m, k);
6
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
- desc = { enumerable: true, get: function() { return m[k]; } };
8
- }
9
- Object.defineProperty(o, k2, desc);
10
- }) : (function(o, m, k, k2) {
11
- if (k2 === undefined) k2 = k;
12
- o[k2] = m[k];
13
- }));
14
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
- Object.defineProperty(o, "default", { enumerable: true, value: v });
16
- }) : function(o, v) {
17
- o["default"] = v;
18
- });
19
- var __importStar = (this && this.__importStar) || (function () {
20
- var ownKeys = function(o) {
21
- ownKeys = Object.getOwnPropertyNames || function (o) {
22
- var ar = [];
23
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
- return ar;
25
- };
26
- return ownKeys(o);
27
- };
28
- return function (mod) {
29
- if (mod && mod.__esModule) return mod;
30
- var result = {};
31
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
- __setModuleDefault(result, mod);
33
- return result;
34
- };
35
- })();
36
- Object.defineProperty(exports, "__esModule", { value: true });
37
- const fs = __importStar(require("fs/promises"));
38
- const path = __importStar(require("path"));
39
- const build_1 = require("./build");
40
- const render_1 = require("./render");
41
- const select_1 = require("./select");
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
- }
74
- return content
75
- .split('\n')
76
- .map((line) => line.trim())
77
- .filter((line) => line !== '' && !line.startsWith('#'));
78
- };
79
- const HELP_TEXT = `llmview - Generate LLM context from codebases using gitignore-style patterns
2
+ import*as V from"fs/promises";import*as H from"path";import{text as ne}from"node:stream/consumers";import*as N from"fs/promises";import*as h from"path";import*as E from"fs/promises";import I from"ignore";var w=async e=>{try{let t=await E.readFile(e,"utf8");return I().add(t)}catch{return}},v=e=>I().add(e),_=(e,t,r)=>{let n=t?`${e}/`:e,o=!1;for(let{ig:s,scope:i}of r){let l=i===""?n:n.startsWith(i+"/")?n.slice(i.length+1):null;if(l===null)continue;let d=s.test(l);d.ignored?o=!0:d.unignored&&(o=!1)}return o};var R=`.git
3
+ .DS_Store
4
+ __pycache__/
5
+ node_modules/`;var $=" ";var b=async e=>{let t=[{ig:v(R),scope:""}],r=await w(h.join(e,".gitignore"));r&&t.push({ig:r,scope:""});let n={name:h.basename(e),relativePath:"",type:"directory",children:[]};return n.children=await L(e,n,t),n},L=async(e,t,r)=>{let n=h.join(e,t.relativePath),o=await N.readdir(n),s=[];for(let i of o){let l=h.join(n,i),d=await N.lstat(l);if(d.isSymbolicLink())continue;let g=d.isDirectory(),m=t.relativePath?`${t.relativePath}/${i}`:i;if(_(m,g,r))continue;let f={name:i,relativePath:m};if(g){let p={...f,type:"directory",children:[]},a=await w(h.join(l,".gitignore")),c=a?[...r,{ig:a,scope:m}]:r;p.children=await L(e,p,c),s.push(p)}else{let p={...f,type:"file",size:d.size};s.push(p)}}return s.sort((i,l)=>i.type!==l.type?i.type==="directory"?-1:1:i.name.localeCompare(l.name))};import ee from"p-limit";import*as O from"fs";import*as j from"fs/promises";import*as x from"path";import*as T from"readline";var q=async(e,t)=>{let r=[],n=O.createReadStream(e,{encoding:"utf-8"}),o=T.createInterface({input:n,crlfDelay:1/0});try{for await(let s of o)if(r.length<t)r.push(s);else return{lines:r,hasMore:!0};return{lines:r,hasMore:!1}}finally{o.close(),n.destroy()}},C=e=>e.map((t,r)=>`${(r+1).toString().padStart(6," ")} ${t}`).join(`
6
+ `),M=async(e,t,r)=>{let n=x.join(e,t.relativePath);if(t.size>250*1024)return`(File contents excluded: size ${(t.size/1024).toFixed(2)}KB exceeds ${250}KB limit)`;let o=await j.readFile(n,"utf-8");if(r.lineNumbers){let s=o.split(`
7
+ `);return C(s)}return o},F=(...e)=>t=>e.some(r=>t.toLowerCase().endsWith(r)),A=[{matcher:F(".csv"),renderer:async(e,t,r)=>{let n=x.join(e,t.relativePath),{lines:o,hasMore:s}=await q(n,10),i=r.lineNumbers?C(o):o.join(`
8
+ `);return s?`${i}
9
+ ... (more rows)`:i}},{matcher:F(".xls",".xlsx"),renderer:async()=>"(Contents excluded)"},{matcher:F(".ico",".png",".jpg",".jpeg",".gif",".webp",".mp4"),renderer:async()=>"(Contents excluded)"},{matcher:F(".pdf",".zip"),renderer:async()=>"(Contents excluded)"}];var z=(e,t)=>B(e,t,0),B=(e,t,r)=>{let n=$.repeat(r);if(e.type==="file")return`${n}${e.name}`;let o=e.children.filter(i=>i.relativePath===""||t.has(i.relativePath)).map(i=>B(i,t,r+1)).join(`
10
+ `),s=`${n}${e.name}/`;return o?`${s}
11
+ ${o}`:s},W=async(e,t,r)=>{let n=ee(64);return await Promise.all(t.map(s=>n(async()=>{let i=await te(e,s,r);return{file:s,content:i}})))},te=async(e,t,r)=>await re(t)(e,t,r),re=e=>{for(let{matcher:t,renderer:r}of A)if(t(e.name))return r;return M};import{minimatch as X}from"minimatch";var G=(e,t)=>J(e).filter(o=>{let s=!1;for(let i of t)i.startsWith("!")?X(o.relativePath,i.slice(1),{dot:!0})&&(s=!1):X(o.relativePath,i,{dot:!0})&&(s=!0);return s}),J=e=>e.children.flatMap(t=>t.type==="file"?[t]:J(t)),K=e=>{let t=new Set;for(let r of e){t.add(r.relativePath);let n=r.relativePath.split("/"),o="";for(let s=0;s<n.length-1;s++)o=o?`${o}/${n[s]}`:n[s],t.add(o)}return t};var oe=()=>ne(process.stdin),se=async e=>{let t;if(!e||e==="-")process.stdin.isTTY&&(console.log(U),process.exit(1)),t=await oe();else{let r=H.resolve(e);try{t=await V.readFile(r,"utf-8")}catch(n){throw n.code==="ENOENT"&&(console.error(`File not found: ${r}`),process.exit(1)),n}}return t.split(`
12
+ `).map(r=>r.trim()).filter(r=>r!==""&&!r.startsWith("#"))},U=`llmview - Generate LLM context from codebases
80
13
 
81
14
  Usage: llmview [options] <view-file>
82
15
 
@@ -84,51 +17,19 @@ Arguments:
84
17
  <view-file> Path to a .llmview file, or - to read patterns from stdin
85
18
 
86
19
  Options:
20
+ -j, --json Output as JSON instead of XML tags
87
21
  -l, --list List selected files only (no content)
88
22
  -n, --number Include line numbers in output
89
23
  -t, --tree Include directory tree of selected files
90
24
  -v, --verbose Print file statistics to stderr
91
25
  -h, --help Show this help message
92
- `;
93
- const main = async () => {
94
- const args = process.argv.slice(2);
95
- if (args.includes('-h') || args.includes('--help')) {
96
- console.log(HELP_TEXT);
97
- process.exit(0);
98
- }
99
- const verbose = args.includes('-v') || args.includes('--verbose');
100
- const includeTree = args.includes('-t') || args.includes('--tree');
101
- const lineNumbers = args.includes('-n') || args.includes('--number');
102
- const listOnly = args.includes('-l') || args.includes('--list');
103
- const positionalArgs = args.filter((arg) => !arg.startsWith('-'));
104
- const patterns = await readPatterns(positionalArgs[0]);
105
- if (patterns.length === 0) {
106
- console.error('No patterns found in input');
107
- process.exit(1);
108
- }
109
- const rootNode = await (0, build_1.buildDirectory)(process.cwd());
110
- const { selectedFiles, visiblePaths } = (0, select_1.selectFiles)(rootNode, patterns, {
111
- verbose,
112
- });
113
- if (listOnly) {
114
- selectedFiles.forEach((file) => console.log(file.relativePath));
115
- process.exit(0);
116
- }
117
- let output = '';
118
- if (includeTree) {
119
- output += (0, render_1.renderDirectory)(rootNode, { verbose }, visiblePaths);
120
- }
121
- output += await (0, render_1.renderFiles)(rootNode.rootPath, selectedFiles, {
122
- verbose,
123
- lineNumbers,
124
- });
125
- const estimatedTokens = Math.ceil(output.length / constants_1.CHARS_PER_TOKEN_ESTIMATE);
126
- if (verbose) {
127
- console.warn(`Estimated tokens: ${estimatedTokens}`);
128
- }
129
- console.log(output);
130
- };
131
- main().catch((err) => {
132
- console.error(err);
133
- process.exit(1);
134
- });
26
+ `,ie=async()=>{let e=process.argv.slice(2);(e.includes("-h")||e.includes("--help"))&&(console.log(U),process.exit(0));let t=e.includes("-v")||e.includes("--verbose"),r=e.includes("-t")||e.includes("--tree"),n=e.includes("-n")||e.includes("--number"),o=e.includes("-l")||e.includes("--list"),s=e.includes("-j")||e.includes("--json"),i=e.filter(c=>!c.startsWith("-")),l=await se(i[0]);l.length===0&&(console.error("No patterns found in input"),process.exit(1));let d=process.cwd(),g=await b(d),m=G(g,l);o&&(m.forEach(c=>console.log(c.relativePath)),process.exit(0));let f=null;if(r){let c=K(m);f=z(g,c)}let p=await W(d,m,{lineNumbers:n}),a="";if(s){let c={directory:f,files:p.map(({file:u,content:P})=>({path:u.relativePath,size:u.size,content:P}))};a=JSON.stringify(c,null,2)}else f&&(a+=`
27
+ <directory>
28
+ ${f}
29
+ </directory>
30
+ `),a+=p.map(({file:c,content:u})=>`<file path="${c.relativePath}">
31
+ ${u}
32
+ </file>`).join(`
33
+ `),a=`\`\`\`
34
+ ${a}
35
+ \`\`\``;if(t){let c=l.join(", ");if(m.length===0)console.warn(`0 files matched (${c})`);else{console.warn(`${m.length} files matched (${c})`),console.warn(`Total characters: ${a.length}`),console.warn(""),console.warn("Files by size:");let u=[...p].sort((y,S)=>S.content.length-y.content.length),P=Math.max(...u.map(y=>y.file.relativePath.length));for(let{file:y,content:S}of u){let k=y.relativePath.padEnd(P),Z=S.length.toLocaleString().padStart(8);console.warn(` ${k} ${Z} chars`)}}}console.log(a)};ie().catch(e=>{console.error(e),process.exit(1)});
package/package.json CHANGED
@@ -1,33 +1,41 @@
1
1
  {
2
2
  "name": "llmview",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
+ "description": "Generate LLM-friendly context from codebases using repeatable view files",
5
+ "keywords": [],
6
+ "author": "",
7
+ "license": "ISC",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/noahtren/llmview.git"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/noahtren/llmview/issues"
14
+ },
15
+ "type": "module",
4
16
  "bin": {
5
17
  "llmview": "dist/cli.js"
6
18
  },
19
+ "files": ["dist", "README.md"],
20
+ "engines": {
21
+ "node": ">=18"
22
+ },
7
23
  "scripts": {
8
- "build": "tsc",
9
- "prepublishOnly": "npm run build",
10
- "test": "node --test --experimental-strip-types test/**/*.test.ts"
24
+ "build": "esbuild src/cli.ts --bundle --platform=node --format=esm --minify --outfile=dist/cli.js --banner:js='#!/usr/bin/env node' --packages=external",
25
+ "typecheck": "tsc --noEmit",
26
+ "test": "vitest run",
27
+ "prepublishOnly": "npm run build"
11
28
  },
12
- "keywords": [],
13
- "author": "",
14
- "license": "ISC",
15
- "description": "Generate LLM context from codebases using gitignore-style patterns",
16
29
  "dependencies": {
17
30
  "ignore": "^7.0.5",
18
- "minimatch": "^10.0.1"
31
+ "minimatch": "^10.0.1",
32
+ "p-limit": "^7.2.0"
19
33
  },
20
34
  "devDependencies": {
35
+ "@types/node": "^25.2.0",
36
+ "esbuild": "^0.27.2",
21
37
  "prettier": "^3.8.1",
22
- "ts-node": "^10.9.2",
23
- "typescript": "^5.8.2"
24
- },
25
- "files": ["dist", "README.md"],
26
- "repository": {
27
- "type": "git",
28
- "url": "git+https://github.com/noahtren/llmview.git"
29
- },
30
- "bugs": {
31
- "url": "https://github.com/noahtren/llmview/issues"
38
+ "typescript": "^5.8.2",
39
+ "vitest": "^4.0.18"
32
40
  }
33
41
  }
package/dist/build.js DELETED
@@ -1,116 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.buildDirectory = void 0;
37
- const fs = __importStar(require("fs/promises"));
38
- const path = __importStar(require("path"));
39
- const ignore_1 = require("./ignore");
40
- const constants_1 = require("./constants");
41
- const buildDirectory = async (projectPath) => {
42
- const ignores = [
43
- { ig: (0, ignore_1.createIgnore)(constants_1.BASE_IGNORE_CONTENT), scope: '' },
44
- ];
45
- const rootGitignore = await (0, ignore_1.createIgnoreFromFile)(path.join(projectPath, '.gitignore'));
46
- if (rootGitignore) {
47
- ignores.push({ ig: rootGitignore, scope: '' });
48
- }
49
- const stat = await fs.stat(projectPath);
50
- const rootNode = {
51
- ino: stat.ino,
52
- name: path.basename(projectPath),
53
- relativePath: '',
54
- createdAt: stat.birthtime,
55
- updatedAt: stat.mtime,
56
- type: 'directory',
57
- rootPath: projectPath,
58
- children: [],
59
- };
60
- rootNode.children = await buildChildrenNodes(projectPath, rootNode, ignores);
61
- return rootNode;
62
- };
63
- exports.buildDirectory = buildDirectory;
64
- const buildChildrenNodes = async (rootPath, parentNode, ignores) => {
65
- const currentPath = path.join(rootPath, parentNode.relativePath);
66
- const entries = await fs.readdir(currentPath);
67
- const nodes = [];
68
- for (const entry of entries) {
69
- const entryFullPath = path.join(currentPath, entry);
70
- const lstat = await fs.lstat(entryFullPath);
71
- if (lstat.isSymbolicLink()) {
72
- continue;
73
- }
74
- const isDirectory = lstat.isDirectory();
75
- const nodeRelativePath = parentNode.relativePath
76
- ? `${parentNode.relativePath}/${entry}`
77
- : entry;
78
- if ((0, ignore_1.isPathIgnored)(nodeRelativePath, isDirectory, ignores)) {
79
- continue;
80
- }
81
- const nodeBase = {
82
- ino: lstat.ino,
83
- name: entry,
84
- relativePath: nodeRelativePath,
85
- createdAt: lstat.birthtime,
86
- updatedAt: lstat.mtime,
87
- };
88
- if (isDirectory) {
89
- const dirNode = {
90
- ...nodeBase,
91
- type: 'directory',
92
- children: [],
93
- };
94
- const nestedIg = await (0, ignore_1.createIgnoreFromFile)(path.join(entryFullPath, '.gitignore'));
95
- const childIgnores = nestedIg
96
- ? [...ignores, { ig: nestedIg, scope: nodeRelativePath }]
97
- : ignores;
98
- dirNode.children = await buildChildrenNodes(rootPath, dirNode, childIgnores);
99
- nodes.push(dirNode);
100
- }
101
- else {
102
- const fileNode = {
103
- ...nodeBase,
104
- type: 'file',
105
- size: lstat.size,
106
- };
107
- nodes.push(fileNode);
108
- }
109
- }
110
- return nodes.sort((a, b) => {
111
- if (a.type !== b.type) {
112
- return a.type === 'directory' ? -1 : 1;
113
- }
114
- return a.name.localeCompare(b.name);
115
- });
116
- };
package/dist/constants.js DELETED
@@ -1,10 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CHARS_PER_TOKEN_ESTIMATE = exports.CSV_PREVIEW_LINES = exports.MAX_FILE_SIZE_KB = exports.BASE_IGNORE_CONTENT = void 0;
4
- exports.BASE_IGNORE_CONTENT = `.git
5
- .DS_Store
6
- __pycache__/
7
- node_modules/`;
8
- exports.MAX_FILE_SIZE_KB = 250;
9
- exports.CSV_PREVIEW_LINES = 10;
10
- exports.CHARS_PER_TOKEN_ESTIMATE = 4;
package/dist/ignore.js DELETED
@@ -1,77 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- var __importDefault = (this && this.__importDefault) || function (mod) {
36
- return (mod && mod.__esModule) ? mod : { "default": mod };
37
- };
38
- Object.defineProperty(exports, "__esModule", { value: true });
39
- exports.isPathIgnored = exports.createIgnore = exports.createIgnoreFromFile = void 0;
40
- const fs = __importStar(require("fs/promises"));
41
- const ignore_1 = __importDefault(require("ignore"));
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 {
48
- return undefined;
49
- }
50
- };
51
- exports.createIgnoreFromFile = createIgnoreFromFile;
52
- const createIgnore = (content) => {
53
- return (0, ignore_1.default)().add(content);
54
- };
55
- exports.createIgnore = createIgnore;
56
- const isPathIgnored = (relativePath, isDirectory, ignores) => {
57
- const pathToCheck = isDirectory ? `${relativePath}/` : relativePath;
58
- let ignored = false;
59
- for (const { ig, scope } of ignores) {
60
- const localPath = scope === ''
61
- ? pathToCheck
62
- : pathToCheck.startsWith(scope + '/')
63
- ? pathToCheck.slice(scope.length + 1)
64
- : null;
65
- if (localPath === null)
66
- continue;
67
- const result = ig.test(localPath);
68
- if (result.ignored) {
69
- ignored = true;
70
- }
71
- else if (result.unignored) {
72
- ignored = false;
73
- }
74
- }
75
- return ignored;
76
- };
77
- exports.isPathIgnored = isPathIgnored;
@@ -1,111 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.RENDER_RULES = exports.defaultRenderer = void 0;
37
- const fs = __importStar(require("fs"));
38
- const fsPromises = __importStar(require("fs/promises"));
39
- const path = __importStar(require("path"));
40
- const readline = __importStar(require("readline"));
41
- const constants_1 = require("./constants");
42
- const readFirstNLines = async (filePath, n) => {
43
- const lines = [];
44
- const stream = fs.createReadStream(filePath, { encoding: 'utf-8' });
45
- const rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
46
- try {
47
- for await (const line of rl) {
48
- if (lines.length < n) {
49
- lines.push(line);
50
- }
51
- else {
52
- return { lines, hasMore: true };
53
- }
54
- }
55
- return { lines, hasMore: false };
56
- }
57
- finally {
58
- rl.close();
59
- stream.destroy();
60
- }
61
- };
62
- const numberLines = (lines) => {
63
- return lines
64
- .map((line, index) => {
65
- const lineNumber = (index + 1).toString().padStart(6, ' ');
66
- return `${lineNumber}\t${line}`;
67
- })
68
- .join('\n');
69
- };
70
- const defaultRenderer = async (rootPath, file, options) => {
71
- const fullPath = path.join(rootPath, file.relativePath);
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)`;
74
- }
75
- const content = await fsPromises.readFile(fullPath, 'utf-8');
76
- if (options.lineNumbers) {
77
- const lines = content.split('\n');
78
- return numberLines(lines);
79
- }
80
- return content;
81
- };
82
- exports.defaultRenderer = defaultRenderer;
83
- exports.RENDER_RULES = [
84
- // csv
85
- {
86
- matcher: (name) => ['.csv'].some((ext) => name.toLowerCase().endsWith(ext)),
87
- renderer: async (rootPath, file, options) => {
88
- const fullPath = path.join(rootPath, file.relativePath);
89
- const { lines, hasMore } = await readFirstNLines(fullPath, constants_1.CSV_PREVIEW_LINES);
90
- const preview = options.lineNumbers
91
- ? numberLines(lines)
92
- : lines.join('\n');
93
- return hasMore ? `${preview}\n... (more rows)` : preview;
94
- },
95
- },
96
- // excel
97
- {
98
- matcher: (name) => ['.xls', '.xlsx'].some((ext) => name.toLowerCase().endsWith(ext)),
99
- renderer: async () => '(Contents excluded)',
100
- },
101
- // media
102
- {
103
- matcher: (name) => ['.ico', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.mp4'].some((ext) => name.toLowerCase().endsWith(ext)),
104
- renderer: async () => '(Contents excluded)',
105
- },
106
- // misc
107
- {
108
- matcher: (name) => ['.pdf', '.zip'].some((ext) => name.toLowerCase().endsWith(ext)),
109
- renderer: async () => '(Contents excluded)',
110
- },
111
- ];
package/dist/render.js DELETED
@@ -1,51 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
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) => {
10
- const { indentChar = ' ' } = options;
11
- const indent = indentChar.repeat(currentDepth);
12
- if (node.type === 'file') {
13
- return `${indent}${node.name}`;
14
- }
15
- const childrenOutput = node.children
16
- .filter((child) => !visiblePaths ||
17
- !child.relativePath ||
18
- visiblePaths.has(child.relativePath))
19
- .map((child) => renderDirectoryNode(child, options, visiblePaths, currentDepth + 1))
20
- .join('\n');
21
- const result = `${indent}${node.name}/`;
22
- if (currentDepth === 0) {
23
- return `\`\`\`\n<directory>\n${result}\n${childrenOutput}\n</directory>\n\`\`\`\n\n`;
24
- }
25
- return childrenOutput ? `${result}\n${childrenOutput}` : result;
26
- };
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');
30
- };
31
- exports.renderFiles = renderFiles;
32
- const renderFile = async (rootPath, file, options) => {
33
- const renderer = getRenderer(file);
34
- const rendered = await renderer(rootPath, file, options);
35
- if (options.verbose) {
36
- console.warn({ path: file.relativePath, length: rendered.length });
37
- }
38
- return `\`\`\`
39
- <file path="${file.relativePath}">
40
- ${rendered}
41
- </file>
42
- \`\`\``;
43
- };
44
- const getRenderer = (file) => {
45
- for (const { matcher, renderer } of render_rules_1.RENDER_RULES) {
46
- if (matcher(file.name)) {
47
- return renderer;
48
- }
49
- }
50
- return render_rules_1.defaultRenderer;
51
- };
package/dist/select.js DELETED
@@ -1,52 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getVisiblePaths = exports.selectFiles = void 0;
4
- const minimatch_1 = require("minimatch");
5
- const selectFiles = (node, globPatterns, options) => {
6
- const allFiles = listAllFiles(node);
7
- const selectedFiles = allFiles.filter((file) => {
8
- let included = false;
9
- for (const pattern of globPatterns) {
10
- if (pattern.startsWith('!')) {
11
- if ((0, minimatch_1.minimatch)(file.relativePath, pattern.slice(1), { dot: true })) {
12
- included = false;
13
- }
14
- }
15
- else {
16
- if ((0, minimatch_1.minimatch)(file.relativePath, pattern, { dot: true })) {
17
- included = true;
18
- }
19
- }
20
- }
21
- return included;
22
- });
23
- const visiblePaths = (0, exports.getVisiblePaths)(selectedFiles);
24
- if (options.verbose) {
25
- if (selectedFiles.length === 0) {
26
- console.warn(`No file(s) found that satisfy these patterns: ${globPatterns.join(', ')}`);
27
- }
28
- else {
29
- console.warn(`${selectedFiles.length} file(s) found satisfying these patterns: ${globPatterns.join(', ')}`);
30
- console.warn({ visiblePaths });
31
- }
32
- }
33
- return { selectedFiles, visiblePaths };
34
- };
35
- exports.selectFiles = selectFiles;
36
- const listAllFiles = (node) => {
37
- return node.children.flatMap((child) => child.type === 'file' ? [child] : listAllFiles(child));
38
- };
39
- const getVisiblePaths = (selectedFiles) => {
40
- const visible = new Set();
41
- for (const file of selectedFiles) {
42
- visible.add(file.relativePath);
43
- const parts = file.relativePath.split('/');
44
- let current = '';
45
- for (let i = 0; i < parts.length - 1; i++) {
46
- current = current ? `${current}/${parts[i]}` : parts[i];
47
- visible.add(current);
48
- }
49
- }
50
- return visible;
51
- };
52
- exports.getVisiblePaths = getVisiblePaths;
package/dist/types.js DELETED
@@ -1,2 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });