llmview 0.1.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:
@@ -20,9 +22,9 @@ Create any number of view files in your project. These can be saved anywhere. Fo
20
22
  new_feature.llmview
21
23
  ```
22
24
 
23
- And use them selectively:
25
+ And use one:
24
26
 
25
- ```
27
+ ```bash
26
28
  llmview .views/backend.llmview
27
29
  ```
28
30
 
@@ -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.
46
+ After selecting, it outputs the contents of each file into a LLM-friendly format and prints to stdout.
45
47
 
46
- After selecting, it serializes the contents of each file into a LLM-friendly format and prints to stdout.
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,39 +79,67 @@ my_project/
85
79
  docs/
86
80
  style_guide.md
87
81
  </directory>
82
+ <file path="backend/main.py">
83
+ ...
84
+ ```
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'
88
96
  ```
89
97
 
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
+ }
90
111
  ```
91
- <file path="my_project/backend/main.py">
92
- ...
93
- ````
94
112
 
95
- ### Including line numbers
113
+ The `directory` field is populated when using `-t`.
96
114
 
97
- The `-n` argument includes line numbers in each file, similar to `cat -n`. This uses more tokens, but can also be useful context.
115
+ ### Only list selected files
98
116
 
99
- ## Integrating with your tools
117
+ 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.
118
+
119
+ ```bash
120
+ llmview .views/backend.llmview -l | zip context.zip -@
121
+ ```
100
122
 
101
- For agentic tools, you can ask an agent to use the `llmview` command itself with views you defined. This might save it some time getting up to speed on your project, especially across sessions.
123
+ ### Using as a filter
102
124
 
103
- For interactive use such as code reviews, you can pipe the results to an API or just copy the contents to your clipboard and paste into a LLM web app, like:
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:
104
126
 
105
127
  ```bash
106
- llmview .views/backend.llmview | pbcopy
128
+ git diff --name-only | llmview -
107
129
  ```
108
130
 
109
131
  ## Renderers
110
132
 
111
- This tool comes with a set of opinionated file renderers. They are informed by the file extension. Exceptions are made for:
133
+ This tool comes with a set of opinionated file renderers based on the file extension. Currently they are:
112
134
 
113
135
  - CSV (truncated by default, preserving the header and the first 10 lines)
114
136
  - Excel, media files, and other non-text formats (omitted)
115
137
 
116
- There is also a max size of 250KB per file. If a file is larger than that, it is not rendered.
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.)
117
139
 
118
- ## Dry run to get statistics
140
+ ## Verbose mode
119
141
 
120
- 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:
121
143
 
122
144
  ```bash
123
145
  llmview -v .views/backend.llmview > /dev/null
package/dist/cli.js CHANGED
@@ -1,104 +1,35 @@
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"));
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 parseLlmviewFile = (filePath) => {
43
- const content = fs.readFileSync(filePath, 'utf-8');
44
- return content
45
- .split('\n')
46
- .map((line) => line.trim())
47
- .filter((line) => line !== '' && !line.startsWith('#'));
48
- };
49
- 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
50
13
 
51
14
  Usage: llmview [options] <view-file>
52
15
 
53
16
  Arguments:
54
- <view-file> Path to a .llmview file containing glob patterns
17
+ <view-file> Path to a .llmview file, or - to read patterns from stdin
55
18
 
56
19
  Options:
20
+ -j, --json Output as JSON instead of XML tags
21
+ -l, --list List selected files only (no content)
57
22
  -n, --number Include line numbers in output
58
23
  -t, --tree Include directory tree of selected files
59
24
  -v, --verbose Print file statistics to stderr
60
25
  -h, --help Show this help message
61
- `;
62
- const main = () => {
63
- const args = process.argv.slice(2);
64
- if (args.includes('-h') || args.includes('--help')) {
65
- console.log(HELP_TEXT);
66
- process.exit(0);
67
- }
68
- const verbose = args.includes('-v') || args.includes('--verbose');
69
- const includeTree = args.includes('-t') || args.includes('--tree');
70
- const lineNumbers = args.includes('-n') || args.includes('--number');
71
- const llmviewPath = args.find((arg) => !arg.startsWith('-'));
72
- if (!llmviewPath) {
73
- console.log(HELP_TEXT);
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);
82
- if (patterns.length === 0) {
83
- console.error('No patterns found in llmview file');
84
- process.exit(1);
85
- }
86
- const rootNode = (0, build_1.buildDirectory)(process.cwd());
87
- const { selectedFiles, visiblePaths } = (0, select_1.selectFiles)(rootNode, patterns, {
88
- verbose,
89
- });
90
- let output = '';
91
- if (includeTree) {
92
- output += (0, render_1.renderHierarchy)(rootNode, { verbose }, visiblePaths);
93
- }
94
- output += (0, render_1.renderFiles)(rootNode.rootPath, selectedFiles, {
95
- verbose,
96
- lineNumbers,
97
- });
98
- const estimatedTokens = Math.ceil(output.length / 4);
99
- if (verbose) {
100
- console.warn(`Estimated tokens: ${estimatedTokens}`);
101
- }
102
- console.log(output);
103
- };
104
- main();
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,36 +1,41 @@
1
1
  {
2
2
  "name": "llmview",
3
- "version": "0.1.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": [
26
- "dist",
27
- "README.md"
28
- ],
29
- "repository": {
30
- "type": "git",
31
- "url": "git+https://github.com/noahtren/llmview.git"
32
- },
33
- "bugs": {
34
- "url": "https://github.com/noahtren/llmview/issues"
38
+ "typescript": "^5.8.2",
39
+ "vitest": "^4.0.18"
35
40
  }
36
41
  }
package/dist/build.js DELETED
@@ -1,120 +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 os = __importStar(require("os"));
38
- const fs = __importStar(require("fs"));
39
- const path = __importStar(require("path"));
40
- const git_1 = require("./git");
41
- const constants_1 = require("./constants");
42
- const expandUser = (inputPath) => {
43
- return inputPath.replace(/^~/, os.homedir());
44
- };
45
- const buildDirectory = (projectPath) => {
46
- projectPath = expandUser(projectPath);
47
- const ignores = [
48
- { ig: (0, git_1.createIgnore)(constants_1.BASE_IGNORE_CONTENT), scope: '' },
49
- ];
50
- const rootGitignore = (0, git_1.createIgnoreFromFile)(path.join(projectPath, '.gitignore'));
51
- if (rootGitignore) {
52
- ignores.push({ ig: rootGitignore, scope: '' });
53
- }
54
- const stat = fs.statSync(projectPath);
55
- const rootNode = {
56
- ino: stat.ino,
57
- name: path.basename(projectPath),
58
- relativePath: '',
59
- createdAt: stat.birthtime,
60
- updatedAt: stat.mtime,
61
- type: 'directory',
62
- rootPath: projectPath,
63
- children: [],
64
- };
65
- rootNode.children = buildChildrenNodes(projectPath, rootNode, ignores);
66
- return rootNode;
67
- };
68
- exports.buildDirectory = buildDirectory;
69
- const buildChildrenNodes = (rootPath, parentNode, ignores) => {
70
- const currentPath = path.join(rootPath, parentNode.relativePath);
71
- const entries = fs.readdirSync(currentPath);
72
- const nodes = [];
73
- for (const entry of entries) {
74
- const entryFullPath = path.join(currentPath, entry);
75
- const lstat = fs.lstatSync(entryFullPath);
76
- if (lstat.isSymbolicLink()) {
77
- continue;
78
- }
79
- const isDirectory = lstat.isDirectory();
80
- const nodeRelativePath = parentNode.relativePath
81
- ? `${parentNode.relativePath}/${entry}`
82
- : entry;
83
- if ((0, git_1.isPathIgnored)(nodeRelativePath, isDirectory, ignores)) {
84
- continue;
85
- }
86
- const nodeBase = {
87
- ino: lstat.ino,
88
- name: entry,
89
- relativePath: nodeRelativePath,
90
- createdAt: lstat.birthtime,
91
- updatedAt: lstat.mtime,
92
- };
93
- if (isDirectory) {
94
- const dirNode = {
95
- ...nodeBase,
96
- type: 'directory',
97
- children: [],
98
- };
99
- const nestedIg = (0, git_1.createIgnoreFromFile)(path.join(entryFullPath, '.gitignore'));
100
- const childIgnores = nestedIg
101
- ? [...ignores, { ig: nestedIg, scope: nodeRelativePath }]
102
- : ignores;
103
- dirNode.children = buildChildrenNodes(rootPath, dirNode, childIgnores);
104
- nodes.push(dirNode);
105
- }
106
- else {
107
- const fileNode = {
108
- ...nodeBase,
109
- type: 'file',
110
- };
111
- nodes.push(fileNode);
112
- }
113
- }
114
- return nodes.sort((a, b) => {
115
- if (a.type !== b.type) {
116
- return a.type === 'directory' ? -1 : 1;
117
- }
118
- return a.name.localeCompare(b.name);
119
- });
120
- };
package/dist/constants.js DELETED
@@ -1,8 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- 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;
package/dist/git.js DELETED
@@ -1,74 +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"));
41
- const ignore_1 = __importDefault(require("ignore"));
42
- const createIgnoreFromFile = (gitignorePath) => {
43
- if (!fs.existsSync(gitignorePath)) {
44
- return undefined;
45
- }
46
- return (0, ignore_1.default)().add(fs.readFileSync(gitignorePath, 'utf8'));
47
- };
48
- exports.createIgnoreFromFile = createIgnoreFromFile;
49
- const createIgnore = (content) => {
50
- return (0, ignore_1.default)().add(content);
51
- };
52
- exports.createIgnore = createIgnore;
53
- const isPathIgnored = (relativePath, isDirectory, ignores) => {
54
- const pathToCheck = isDirectory ? `${relativePath}/` : relativePath;
55
- let ignored = false;
56
- for (const { ig, scope } of ignores) {
57
- const localPath = scope === ''
58
- ? pathToCheck
59
- : pathToCheck.startsWith(scope + '/')
60
- ? pathToCheck.slice(scope.length + 1)
61
- : null;
62
- if (localPath === null)
63
- continue;
64
- const result = ig.test(localPath);
65
- if (result.ignored) {
66
- ignored = true;
67
- }
68
- else if (result.unignored) {
69
- ignored = false;
70
- }
71
- }
72
- return ignored;
73
- };
74
- exports.isPathIgnored = isPathIgnored;
package/dist/render.js DELETED
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.renderFiles = exports.renderHierarchy = void 0;
4
- const renderers_1 = require("./renderers");
5
- const renderHierarchy = (node, options, visiblePaths, currentDepth = 0) => {
6
- if (visiblePaths &&
7
- node.relativePath &&
8
- !visiblePaths.has(node.relativePath)) {
9
- return null;
10
- }
11
- const { indentChar = ' ' } = options;
12
- const indent = indentChar.repeat(currentDepth);
13
- if (node.type === 'file') {
14
- return `${indent}${node.name}`;
15
- }
16
- let result = `${indent}${node.name}/`;
17
- if (node.children.length === 0) {
18
- return result;
19
- }
20
- const childrenOutput = node.children
21
- .map((child) => (0, exports.renderHierarchy)(child, options, visiblePaths, currentDepth + 1))
22
- .filter((output) => output !== null)
23
- .join('\n');
24
- if (currentDepth === 0) {
25
- return `\`\`\`
26
- <directory>
27
- ${result}
28
- ${childrenOutput}
29
- </directory>
30
- \`\`\`\n\n`;
31
- }
32
- else {
33
- return `${result}\n${childrenOutput}`;
34
- }
35
- };
36
- exports.renderHierarchy = renderHierarchy;
37
- const renderFiles = (rootPath, files, options) => {
38
- return files
39
- .map((file) => renderFileBlock(rootPath, file, options))
40
- .join('\n\n');
41
- };
42
- exports.renderFiles = renderFiles;
43
- const renderFileBlock = (rootPath, file, options) => {
44
- const renderer = getRenderer(file);
45
- const rendered = renderer(rootPath, file, options);
46
- if (options.verbose) {
47
- console.warn({ path: file.relativePath, length: rendered.length });
48
- }
49
- return `\`\`\`
50
- <file path="${file.relativePath}">
51
- ${rendered}
52
- </file>
53
- \`\`\``;
54
- };
55
- const getRenderer = (file) => {
56
- for (const { matcher, renderer } of renderers_1.RENDER_RULES) {
57
- if (matcher(file.name)) {
58
- return renderer;
59
- }
60
- }
61
- return renderers_1.defaultRenderer;
62
- };
package/dist/renderers.js DELETED
@@ -1,131 +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 path = __importStar(require("path"));
39
- 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);
44
- const lines = [];
45
- let leftover = '';
46
- let hasMore = false;
47
- try {
48
- while (lines.length < n) {
49
- const bytesRead = fs.readSync(fd, buffer, 0, bufferSize, null);
50
- if (bytesRead === 0)
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;
70
- }
71
- else {
72
- hasMore = fs.readSync(fd, buffer, 0, 1, null) > 0;
73
- }
74
- }
75
- return { lines, hasMore };
76
- }
77
- finally {
78
- fs.closeSync(fd);
79
- }
80
- };
81
- const numberLines = (lines) => {
82
- return lines
83
- .map((line, index) => {
84
- const lineNumber = (index + 1).toString().padStart(6, ' ');
85
- return `${lineNumber}\t${line}`;
86
- })
87
- .join('\n');
88
- };
89
- const defaultRenderer = (rootPath, file, options) => {
90
- const fullPath = path.join(rootPath, file.relativePath);
91
- const stats = fs.statSync(fullPath);
92
- if (stats.size > constants_1.MAX_FILE_SIZE_KB * 1024) {
93
- return `(File contents excluded: size ${(stats.size / 1024).toFixed(2)}KB exceeds ${constants_1.MAX_FILE_SIZE_KB}KB limit)`;
94
- }
95
- const content = fs.readFileSync(fullPath, 'utf-8');
96
- if (options.lineNumbers) {
97
- const lines = content.split('\n');
98
- return numberLines(lines);
99
- }
100
- return content;
101
- };
102
- exports.defaultRenderer = defaultRenderer;
103
- exports.RENDER_RULES = [
104
- // csv
105
- {
106
- matcher: (name) => ['.csv'].some((ext) => name.toLowerCase().endsWith(ext)),
107
- renderer: (rootPath, file, options) => {
108
- const fullPath = path.join(rootPath, file.relativePath);
109
- const { lines, hasMore } = readFirstNLines(fullPath, 10);
110
- const preview = options.lineNumbers
111
- ? numberLines(lines)
112
- : lines.join('\n');
113
- return hasMore ? `${preview}\n... (more rows)` : preview;
114
- },
115
- },
116
- // excel
117
- {
118
- matcher: (name) => ['.xls', '.xlsx'].some((ext) => name.toLowerCase().endsWith(ext)),
119
- renderer: (rootPath, file, options) => '(Contents excluded)',
120
- },
121
- // media
122
- {
123
- matcher: (name) => ['.ico', '.png', '.jpg', '.jpeg', '.gif', '.webp', '.mp4'].some((ext) => name.toLowerCase().endsWith(ext)),
124
- renderer: (rootPath, file, options) => '(Contents excluded)',
125
- },
126
- // misc
127
- {
128
- matcher: (name) => ['.pdf', '.zip'].some((ext) => name.toLowerCase().endsWith(ext)),
129
- renderer: (rootPath, file, options) => '(Contents excluded)',
130
- },
131
- ];
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 });