repomeld 2.0.4 ā 2.0.5
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 +253 -9
- package/bin/cli.js +288 -168
- package/package.json +32 -6
package/README.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
> Meld your entire repo into a single file ā perfect for AI context, code reviews & sharing.
|
|
4
4
|
|
|
5
|
+
[](https://www.npmjs.com/package/repomeld)
|
|
6
|
+
[](https://opensource.org/licenses/MIT)
|
|
7
|
+
|
|
5
8
|
---
|
|
6
9
|
|
|
7
10
|
> ## š¼ Open to Work
|
|
@@ -10,12 +13,33 @@
|
|
|
10
13
|
|
|
11
14
|
---
|
|
12
15
|
|
|
16
|
+
## ⨠Features
|
|
17
|
+
|
|
18
|
+
- š **Fast & Efficient** - Async scanning with real-time progress
|
|
19
|
+
- šØ **Multiple Styles** - Banner, Markdown, or Minimal output
|
|
20
|
+
- š **Smart Filtering** - Extension, pattern, and size-based filtering
|
|
21
|
+
- š **Gitignore Support** - Respects your .gitignore rules automatically
|
|
22
|
+
- š¾ **Binary Detection** - Intelligently skips binary files
|
|
23
|
+
- š¦ **Single File Output** - Perfect for AI context windows
|
|
24
|
+
- š **Auto-Numbering** - Never overwrites existing files
|
|
25
|
+
- šæ **Zip Backup** - Creates timestamped backups of all included files
|
|
26
|
+
- š **Update Notifications** - Know when new versions are available
|
|
27
|
+
- šÆ **Force Include** - Override ignore rules when needed
|
|
28
|
+
|
|
29
|
+
---
|
|
30
|
+
|
|
13
31
|
## Install
|
|
14
32
|
|
|
15
33
|
```bash
|
|
16
34
|
npm install -g repomeld
|
|
17
35
|
```
|
|
18
36
|
|
|
37
|
+
Or use without installing:
|
|
38
|
+
|
|
39
|
+
```bash
|
|
40
|
+
npx repomeld
|
|
41
|
+
```
|
|
42
|
+
|
|
19
43
|
---
|
|
20
44
|
|
|
21
45
|
## Quick Start
|
|
@@ -25,7 +49,7 @@ cd your-project
|
|
|
25
49
|
repomeld
|
|
26
50
|
```
|
|
27
51
|
|
|
28
|
-
That's it. repomeld walks your project,
|
|
52
|
+
That's it. repomeld walks your project, respects `.gitignore`, skips binary files, and writes everything into one readable file.
|
|
29
53
|
|
|
30
54
|
---
|
|
31
55
|
|
|
@@ -76,6 +100,8 @@ Filtering:
|
|
|
76
100
|
--max-size <kb> Skip files larger than N kilobytes
|
|
77
101
|
Default: 500
|
|
78
102
|
|
|
103
|
+
--no-gitignore Ignore .gitignore file (include everything)
|
|
104
|
+
|
|
79
105
|
Formatting:
|
|
80
106
|
-s, --style <style> Header style for each file block:
|
|
81
107
|
banner ā clear dividers with file info (default)
|
|
@@ -92,6 +118,8 @@ Advanced:
|
|
|
92
118
|
--lines-before <n> Skip the first N lines of every file
|
|
93
119
|
--lines-after <n> Skip the last N lines of every file
|
|
94
120
|
--dry-run Preview which files would be included ā nothing is written
|
|
121
|
+
--no-backup Skip creating backup zip file
|
|
122
|
+
--no-update-check Skip checking for updates
|
|
95
123
|
```
|
|
96
124
|
|
|
97
125
|
---
|
|
@@ -120,6 +148,9 @@ repomeld --dry-run
|
|
|
120
148
|
# Ignore extra folders on top of defaults
|
|
121
149
|
repomeld --ignore coverage logs tmp
|
|
122
150
|
|
|
151
|
+
# Respect gitignore (default) or ignore it
|
|
152
|
+
repomeld --no-gitignore # include everything
|
|
153
|
+
|
|
123
154
|
# Only small files ā skip anything over 100 KB
|
|
124
155
|
repomeld --max-size 100
|
|
125
156
|
|
|
@@ -131,6 +162,9 @@ repomeld --no-toc --no-meta
|
|
|
131
162
|
|
|
132
163
|
# Combine filters
|
|
133
164
|
repomeld --ext php --include Controllers --exclude test --style markdown
|
|
165
|
+
|
|
166
|
+
# Skip backup creation
|
|
167
|
+
repomeld --no-backup
|
|
134
168
|
```
|
|
135
169
|
|
|
136
170
|
---
|
|
@@ -147,14 +181,15 @@ repomeld automatically skips these so your output stays clean:
|
|
|
147
181
|
| Lock files | `package-lock.json`, `yarn.lock`, `pnpm-lock.yaml` |
|
|
148
182
|
| Build output | `dist/`, `build/`, `.next/`, `.nuxt/`, `.cache/` |
|
|
149
183
|
| OS files | `.DS_Store` |
|
|
150
|
-
| Project meta | `package.json`, `README.md` |
|
|
151
184
|
| repomeld output | `repomeld_output.txt` and all `repomeld_output__N.txt` files |
|
|
152
185
|
|
|
153
|
-
|
|
186
|
+
**Note:** `package.json` and `README.md` are **NOT** ignored by default ā they contain important context for AI tools and code reviews.
|
|
154
187
|
|
|
155
188
|
---
|
|
156
189
|
|
|
157
|
-
## Custom Ignore
|
|
190
|
+
## Custom Ignore Rules
|
|
191
|
+
|
|
192
|
+
### Method 1: repomeld.ignore.json
|
|
158
193
|
|
|
159
194
|
Create a `repomeld.ignore.json` in your project root:
|
|
160
195
|
|
|
@@ -164,13 +199,52 @@ Create a `repomeld.ignore.json` in your project root:
|
|
|
164
199
|
"coverage",
|
|
165
200
|
"logs",
|
|
166
201
|
"tmp",
|
|
167
|
-
"*.min.js"
|
|
202
|
+
"*.min.js",
|
|
203
|
+
"**/generated/**"
|
|
168
204
|
]
|
|
169
205
|
}
|
|
170
206
|
```
|
|
171
207
|
|
|
172
208
|
These are merged with the defaults every time repomeld runs.
|
|
173
209
|
|
|
210
|
+
### Method 2: .gitignore
|
|
211
|
+
|
|
212
|
+
repomeld automatically respects your `.gitignore` file. Use `--no-gitignore` to override.
|
|
213
|
+
|
|
214
|
+
### Method 3: CLI --ignore
|
|
215
|
+
|
|
216
|
+
Override on the command line:
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
repomeld --ignore temp logs "*.tmp"
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## Backup Zip Files
|
|
225
|
+
|
|
226
|
+
When repomeld runs, it automatically creates a backup zip file in the `repomeld_repomeld/` folder:
|
|
227
|
+
|
|
228
|
+
```
|
|
229
|
+
repomeld_output.txt
|
|
230
|
+
repomeld_repomeld/
|
|
231
|
+
āāā repomeld_output.zip ā contains all included files + output
|
|
232
|
+
|
|
233
|
+
repomeld_output__2.txt
|
|
234
|
+
repomeld_repomeld/
|
|
235
|
+
āāā repomeld_output__2.zip ā corresponding backup
|
|
236
|
+
|
|
237
|
+
repomeld_output__3.txt
|
|
238
|
+
repomeld_repomeld/
|
|
239
|
+
āāā repomeld_output__3.zip ā and so on...
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The zip file contains:
|
|
243
|
+
- All source files included in the run (preserving folder structure)
|
|
244
|
+
- The repomeld output file itself
|
|
245
|
+
|
|
246
|
+
To disable backups: `repomeld --no-backup`
|
|
247
|
+
|
|
174
248
|
---
|
|
175
249
|
|
|
176
250
|
## Output Format
|
|
@@ -178,8 +252,8 @@ These are merged with the defaults every time repomeld runs.
|
|
|
178
252
|
Each run produces a file like this:
|
|
179
253
|
|
|
180
254
|
```
|
|
181
|
-
# Generated by repomeld
|
|
182
|
-
# Date : 2025-04-
|
|
255
|
+
# Generated by repomeld v2.0.4
|
|
256
|
+
# Date : 2025-04-23T10:00:00.000Z
|
|
183
257
|
# Source : /your/project
|
|
184
258
|
# Files : 12
|
|
185
259
|
# Lines : 847
|
|
@@ -199,16 +273,186 @@ TABLE OF CONTENTS
|
|
|
199
273
|
... file contents ...
|
|
200
274
|
```
|
|
201
275
|
|
|
202
|
-
|
|
276
|
+
### Markdown Style Example
|
|
277
|
+
|
|
278
|
+
With `--style markdown` each file becomes a fenced code block:
|
|
279
|
+
|
|
280
|
+
```markdown
|
|
281
|
+
## š src/index.js [120 lines | 3.2 KB | javascript]
|
|
282
|
+
|
|
283
|
+
```javascript
|
|
284
|
+
// Your code here
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
## š src/utils.js [45 lines | 1.1 KB | javascript]
|
|
288
|
+
|
|
289
|
+
```javascript
|
|
290
|
+
// More code here
|
|
291
|
+
```
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
Perfect for pasting directly into Claude, ChatGPT, Cursor, or any AI tool!
|
|
295
|
+
|
|
296
|
+
### Minimal Style Example
|
|
297
|
+
|
|
298
|
+
With `--style minimal`:
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
# src/index.js
|
|
302
|
+
your code here
|
|
303
|
+
|
|
304
|
+
# src/utils.js
|
|
305
|
+
more code here
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Performance
|
|
311
|
+
|
|
312
|
+
repomeld is optimized for large codebases:
|
|
313
|
+
- Async file scanning (non-blocking)
|
|
314
|
+
- Real-time progress indicator with ETA
|
|
315
|
+
- Memory-efficient streaming
|
|
316
|
+
- Handles repos with 10,000+ files easily
|
|
317
|
+
|
|
318
|
+
Example output:
|
|
319
|
+
```
|
|
320
|
+
š Scanning files...
|
|
321
|
+
ā
Found 2453 files in 1.2s
|
|
322
|
+
|
|
323
|
+
š Processing 2453 files...
|
|
324
|
+
|
|
325
|
+
[1245/2453] files (50.7%) | 2.3s elapsed | ETA: 2s
|
|
326
|
+
ā
Completed 2453/2453 files in 4.7s
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Use Cases
|
|
332
|
+
|
|
333
|
+
### š¤ AI Context Preparation
|
|
334
|
+
```bash
|
|
335
|
+
repomeld --ext js ts jsx py --style markdown --max-size 200
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### š Code Review
|
|
339
|
+
```bash
|
|
340
|
+
repomeld --include src/ --exclude test --style minimal --no-meta
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### š¾ Full Project Backup
|
|
344
|
+
```bash
|
|
345
|
+
repomeld --force-include . --max-size 10000 --no-toc --no-meta
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
### š Documentation Generation
|
|
349
|
+
```bash
|
|
350
|
+
repomeld --ext md --include docs --style markdown --output documentation.md
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
### š Debug Specific Feature
|
|
354
|
+
```bash
|
|
355
|
+
repomeld --include feature-name --ext js css --output feature-context.txt
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## FAQ
|
|
361
|
+
|
|
362
|
+
**Q: Why are package.json and README.md included now?**
|
|
363
|
+
A: They were removed in early versions but added back because they provide essential context for AI tools and code reviewers.
|
|
364
|
+
|
|
365
|
+
**Q: How do I ignore package.json?**
|
|
366
|
+
A: Add it to `repomeld.ignore.json` or use `--ignore package.json`
|
|
367
|
+
|
|
368
|
+
**Q: Can I use this in CI/CD?**
|
|
369
|
+
A: Yes! Use `--no-update-check` and `--no-backup` for automated environments.
|
|
370
|
+
|
|
371
|
+
**Q: Does it work on Windows?**
|
|
372
|
+
A: Yes! Paths are normalized for cross-platform compatibility.
|
|
373
|
+
|
|
374
|
+
**Q: How do I get just the file list without content?**
|
|
375
|
+
A: Use `--dry-run` to preview without writing.
|
|
376
|
+
|
|
377
|
+
**Q: My binary files are being included?**
|
|
378
|
+
A: repomeld uses intelligent binary detection. If something slips through, use `--ext` to filter specific extensions.
|
|
379
|
+
|
|
380
|
+
---
|
|
381
|
+
|
|
382
|
+
## Development
|
|
383
|
+
|
|
384
|
+
```bash
|
|
385
|
+
# Clone the repo
|
|
386
|
+
git clone https://github.com/susheel/repomeld.git
|
|
387
|
+
cd repomeld
|
|
388
|
+
|
|
389
|
+
# Install dependencies
|
|
390
|
+
npm install
|
|
391
|
+
|
|
392
|
+
# Run locally
|
|
393
|
+
npm start -- --dry-run
|
|
394
|
+
|
|
395
|
+
# Link for global testing
|
|
396
|
+
npm link
|
|
397
|
+
repomeld --help
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
---
|
|
401
|
+
|
|
402
|
+
## Contributing
|
|
403
|
+
|
|
404
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
405
|
+
|
|
406
|
+
1. Fork the repository
|
|
407
|
+
2. Create your feature branch (`git checkout -b feature/amazing`)
|
|
408
|
+
3. Commit your changes (`git commit -m 'Add some amazing feature'`)
|
|
409
|
+
4. Push to the branch (`git push origin feature/amazing`)
|
|
410
|
+
5. Open a Pull Request
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
## Changelog
|
|
415
|
+
|
|
416
|
+
### v2.0.4 (Current)
|
|
417
|
+
- ā
Added gitignore support with `ignore` package
|
|
418
|
+
- ā
Added zip backup feature (`repomeld_repomeld/` folder)
|
|
419
|
+
- ā
Added update notifications (non-intrusive)
|
|
420
|
+
- ā
Improved performance with async operations
|
|
421
|
+
- ā
Added progress indicator with ETA
|
|
422
|
+
- ā
Fixed Windows path compatibility
|
|
423
|
+
- ā
Improved binary detection
|
|
424
|
+
- ā
Added force-include for override scenarios
|
|
425
|
+
- ā
Removed `package.json` and `README.md` from default ignores
|
|
426
|
+
|
|
427
|
+
### v1.0.0
|
|
428
|
+
- Initial release with basic functionality
|
|
203
429
|
|
|
204
430
|
---
|
|
205
431
|
|
|
206
432
|
## License
|
|
207
433
|
|
|
208
|
-
MIT
|
|
434
|
+
MIT Ā© [Susheel](mailto:susheelhbti@gmail.com)
|
|
435
|
+
|
|
436
|
+
---
|
|
437
|
+
|
|
438
|
+
## Support & Contact
|
|
439
|
+
|
|
440
|
+
- š **Issues**: [GitHub Issues](https://github.com/susheel/repomeld/issues)
|
|
441
|
+
- š§ **Email**: [susheelhbti@gmail.com](mailto:susheelhbti@gmail.com)
|
|
442
|
+
- š¼ **Hire Me**: Available for freelance and full-time opportunities
|
|
209
443
|
|
|
210
444
|
---
|
|
211
445
|
|
|
212
446
|
> ## š¼ Hire the Author
|
|
213
447
|
> Built by a developer available for **freelance and full-time opportunities**.
|
|
214
448
|
> Got a project? Let's talk ā š§ **[susheelhbti@gmail.com](mailto:susheelhbti@gmail.com)**
|
|
449
|
+
|
|
450
|
+
---
|
|
451
|
+
|
|
452
|
+
## Star History
|
|
453
|
+
|
|
454
|
+
[](https://star-history.com/#susheel/repomeld&Date)
|
|
455
|
+
|
|
456
|
+
---
|
|
457
|
+
|
|
458
|
+
**Made with ā¤ļø for developers who need better context for AI tools**
|
package/bin/cli.js
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const fs = require("fs");
|
|
3
|
+
const fs = require("fs").promises;
|
|
4
|
+
const fsSync = require("fs");
|
|
4
5
|
const path = require("path");
|
|
5
6
|
const { program } = require("commander");
|
|
7
|
+
const ignore = require("ignore");
|
|
8
|
+
const { isBinaryFile } = require("isbinaryfile");
|
|
9
|
+
const readline = require("readline");
|
|
10
|
+
const os = require("os");
|
|
6
11
|
|
|
7
12
|
const VERSION = "1.0.0";
|
|
13
|
+
const PACKAGE_NAME = "repomeld";
|
|
14
|
+
|
|
15
|
+
// Normalize paths for cross-platform compatibility
|
|
16
|
+
const normalizePath = (p) => p.split(path.sep).join('/');
|
|
8
17
|
|
|
9
18
|
const DEFAULT_IGNORE = [
|
|
10
19
|
"node_modules",
|
|
11
20
|
".git",
|
|
12
21
|
".env",
|
|
13
|
-
".env.local",
|
|
22
|
+
".env.local",
|
|
14
23
|
".env.production",
|
|
15
24
|
".DS_Store",
|
|
16
25
|
"package-lock.json",
|
|
@@ -20,31 +29,10 @@ const DEFAULT_IGNORE = [
|
|
|
20
29
|
".nuxt",
|
|
21
30
|
"dist",
|
|
22
31
|
"build",
|
|
23
|
-
".cache"
|
|
24
|
-
|
|
25
|
-
"README.md",
|
|
32
|
+
".cache"
|
|
33
|
+
|
|
26
34
|
];
|
|
27
35
|
|
|
28
|
-
function loadIgnoreConfig() {
|
|
29
|
-
const configPath = path.resolve(process.cwd(), "repomeld.ignore.json");
|
|
30
|
-
const pkgDir = path.resolve(__dirname, "..", "repomeld.ignore.json");
|
|
31
|
-
for (const loc of [configPath, pkgDir]) {
|
|
32
|
-
if (fs.existsSync(loc)) {
|
|
33
|
-
try {
|
|
34
|
-
const data = JSON.parse(fs.readFileSync(loc, "utf8"));
|
|
35
|
-
if (Array.isArray(data.ignore)) {
|
|
36
|
-
return data.ignore;
|
|
37
|
-
}
|
|
38
|
-
} catch {
|
|
39
|
-
console.warn(` ā ļø Could not parse ${loc}, using defaults.`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
return [];
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const IGNORE_FROM_CONFIG = loadIgnoreConfig();
|
|
47
|
-
|
|
48
36
|
const LANGUAGE_MAP = {
|
|
49
37
|
js: "javascript", jsx: "javascript", ts: "typescript", tsx: "typescript",
|
|
50
38
|
py: "python", rb: "ruby", java: "java", cpp: "cpp", c: "c",
|
|
@@ -54,54 +42,92 @@ const LANGUAGE_MAP = {
|
|
|
54
42
|
toml: "toml", xml: "xml", sql: "sql", graphql: "graphql",
|
|
55
43
|
};
|
|
56
44
|
|
|
57
|
-
function
|
|
58
|
-
const
|
|
59
|
-
return LANGUAGE_MAP[ext] || "";
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
function getAllFiles(dirPath, ignoreList, fileList = []) {
|
|
63
|
-
let entries;
|
|
45
|
+
async function loadIgnoreConfig() {
|
|
46
|
+
const configPath = path.resolve(process.cwd(), "repomeld.ignore.json");
|
|
64
47
|
try {
|
|
65
|
-
|
|
48
|
+
const data = JSON.parse(await fs.readFile(configPath, "utf8"));
|
|
49
|
+
if (Array.isArray(data.ignore)) return data.ignore;
|
|
66
50
|
} catch {
|
|
67
|
-
|
|
51
|
+
// File doesn't exist or invalid JSON, use defaults
|
|
68
52
|
}
|
|
53
|
+
return [];
|
|
54
|
+
}
|
|
69
55
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
function getLanguage(filePath) {
|
|
57
|
+
const ext = path.extname(filePath).slice(1).toLowerCase();
|
|
58
|
+
return LANGUAGE_MAP[ext] || "";
|
|
59
|
+
}
|
|
73
60
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
61
|
+
async function getAllFilesWithIgnore(dirPath, ig, forceIncludePatterns, rootDir = process.cwd(), progress = null) {
|
|
62
|
+
const fileList = [];
|
|
63
|
+
const stack = [{ dirPath, relativePath: '.' }];
|
|
64
|
+
|
|
65
|
+
while (stack.length) {
|
|
66
|
+
const { dirPath: currentDir, relativePath: currentRelative } = stack.pop();
|
|
67
|
+
|
|
68
|
+
let entries;
|
|
69
|
+
try {
|
|
70
|
+
entries = await fs.readdir(currentDir, { withFileTypes: true });
|
|
71
|
+
} catch (err) {
|
|
79
72
|
continue;
|
|
80
73
|
}
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
74
|
+
|
|
75
|
+
for (const entry of entries) {
|
|
76
|
+
const fullPath = path.join(currentDir, entry.name);
|
|
77
|
+
const relativePath = path.join(currentRelative, entry.name);
|
|
78
|
+
const normalizedPath = normalizePath(relativePath);
|
|
79
|
+
|
|
80
|
+
// Check force-include first - these always go through
|
|
81
|
+
const isForceIncluded = forceIncludePatterns && forceIncludePatterns.some(pattern =>
|
|
82
|
+
normalizedPath.includes(pattern) || entry.name.includes(pattern)
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
// Only check ignore if not force-included
|
|
86
|
+
if (!isForceIncluded && ig.ignores(normalizedPath)) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (entry.isDirectory()) {
|
|
91
|
+
stack.push({ dirPath: fullPath, relativePath });
|
|
92
|
+
} else if (entry.isFile()) {
|
|
93
|
+
fileList.push(fullPath);
|
|
94
|
+
if (progress && fileList.length % 100 === 0) {
|
|
95
|
+
progress.update(fileList.length);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
86
98
|
}
|
|
87
99
|
}
|
|
88
|
-
|
|
100
|
+
|
|
89
101
|
return fileList;
|
|
90
102
|
}
|
|
91
103
|
|
|
92
|
-
function
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
104
|
+
async function buildIgnoreFilter(options) {
|
|
105
|
+
const ig = ignore();
|
|
106
|
+
|
|
107
|
+
// Add default ignores
|
|
108
|
+
ig.add(DEFAULT_IGNORE);
|
|
109
|
+
|
|
110
|
+
// Add custom config ignores
|
|
111
|
+
const customIgnores = await loadIgnoreConfig();
|
|
112
|
+
ig.add(customIgnores);
|
|
113
|
+
|
|
114
|
+
// Add CLI ignores
|
|
115
|
+
if (options.ignore && options.ignore.length) {
|
|
116
|
+
ig.add(options.ignore);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Add .gitignore patterns if not disabled
|
|
120
|
+
if (!options.noGitignore) {
|
|
121
|
+
try {
|
|
122
|
+
const gitignorePath = path.join(process.cwd(), ".gitignore");
|
|
123
|
+
const gitignoreContent = await fs.readFile(gitignorePath, "utf8");
|
|
124
|
+
ig.add(gitignoreContent);
|
|
125
|
+
} catch {
|
|
126
|
+
// No .gitignore file, ignore silently
|
|
100
127
|
}
|
|
101
|
-
return false;
|
|
102
|
-
} catch {
|
|
103
|
-
return true;
|
|
104
128
|
}
|
|
129
|
+
|
|
130
|
+
return { ig, forceInclude: options.forceInclude || null };
|
|
105
131
|
}
|
|
106
132
|
|
|
107
133
|
function matchesExtensions(filePath, exts) {
|
|
@@ -119,7 +145,47 @@ function matchesPattern(filePath, patterns) {
|
|
|
119
145
|
function formatSize(bytes) {
|
|
120
146
|
if (bytes < 1024) return `${bytes} B`;
|
|
121
147
|
if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
|
|
122
|
-
return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
148
|
+
if (bytes < 1024 * 1024 * 1024) return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
|
|
149
|
+
return `${(bytes / (1024 * 1024 * 1024)).toFixed(2)} GB`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function formatDuration(ms) {
|
|
153
|
+
if (ms < 1000) return `${ms}ms`;
|
|
154
|
+
const seconds = (ms / 1000).toFixed(1);
|
|
155
|
+
return `${seconds}s`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
class ProgressIndicator {
|
|
159
|
+
constructor(total, prefix = '') {
|
|
160
|
+
this.total = total;
|
|
161
|
+
this.prefix = prefix;
|
|
162
|
+
this.current = 0;
|
|
163
|
+
this.startTime = Date.now();
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
update(current) {
|
|
167
|
+
this.current = current;
|
|
168
|
+
this.render();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
increment() {
|
|
172
|
+
this.current++;
|
|
173
|
+
this.render();
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
render() {
|
|
177
|
+
const percent = (this.current / this.total * 100).toFixed(1);
|
|
178
|
+
const elapsed = Date.now() - this.startTime;
|
|
179
|
+
const rate = this.current / (elapsed / 1000);
|
|
180
|
+
const eta = rate > 0 ? ((this.total - this.current) / rate).toFixed(0) : '?';
|
|
181
|
+
|
|
182
|
+
process.stdout.write(`\r${this.prefix} ${this.current}/${this.total} files (${percent}%) | ${formatDuration(elapsed)} elapsed | ETA: ${eta}s`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
finish() {
|
|
186
|
+
const elapsed = Date.now() - this.startTime;
|
|
187
|
+
console.log(`\r${this.prefix} ā
Completed ${this.current}/${this.total} files in ${formatDuration(elapsed)}`);
|
|
188
|
+
}
|
|
123
189
|
}
|
|
124
190
|
|
|
125
191
|
function printBanner() {
|
|
@@ -133,10 +199,9 @@ function printBanner() {
|
|
|
133
199
|
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`);
|
|
134
200
|
}
|
|
135
201
|
|
|
136
|
-
function buildHeader(style, relativePath, filePath, lineCount, showMeta) {
|
|
202
|
+
function buildHeader(style, relativePath, filePath, lineCount, showMeta, stats) {
|
|
137
203
|
const lang = getLanguage(filePath);
|
|
138
|
-
const
|
|
139
|
-
const meta = showMeta ? ` [${lineCount} lines | ${size}${lang ? " | " + lang : ""}]` : "";
|
|
204
|
+
const meta = showMeta ? ` [${lineCount} lines | ${formatSize(stats.size)}${lang ? " | " + lang : ""}]` : "";
|
|
140
205
|
|
|
141
206
|
if (style === "markdown") {
|
|
142
207
|
return `\n## š ${relativePath}${meta}\n\n\`\`\`${lang}\n`;
|
|
@@ -144,7 +209,6 @@ function buildHeader(style, relativePath, filePath, lineCount, showMeta) {
|
|
|
144
209
|
if (style === "minimal") {
|
|
145
210
|
return `\n# ${relativePath}\n`;
|
|
146
211
|
}
|
|
147
|
-
// default: banner style
|
|
148
212
|
const divider = "ā".repeat(60);
|
|
149
213
|
return `\n${divider}\n FILE: ${relativePath}${meta}\n${divider}\n\n`;
|
|
150
214
|
}
|
|
@@ -164,30 +228,27 @@ function buildTableOfContents(files, cwd) {
|
|
|
164
228
|
return toc;
|
|
165
229
|
}
|
|
166
230
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
231
|
+
async function resolveOutputPath(desiredPath) {
|
|
232
|
+
try {
|
|
233
|
+
await fs.access(desiredPath);
|
|
234
|
+
const ext = path.extname(desiredPath);
|
|
235
|
+
const base = desiredPath.slice(0, desiredPath.length - ext.length);
|
|
236
|
+
let counter = 2;
|
|
237
|
+
while (true) {
|
|
238
|
+
const candidate = `${base}__${counter}${ext}`;
|
|
239
|
+
try {
|
|
240
|
+
await fs.access(candidate);
|
|
241
|
+
counter++;
|
|
242
|
+
} catch {
|
|
243
|
+
return { path: candidate, number: counter };
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} catch {
|
|
247
|
+
return { path: desiredPath, number: null };
|
|
183
248
|
}
|
|
184
249
|
}
|
|
185
250
|
|
|
186
|
-
|
|
187
|
-
* Returns true if the given filePath looks like a repomeld output file
|
|
188
|
-
* (matches the base name pattern with optional __N suffix).
|
|
189
|
-
*/
|
|
190
|
-
function isRepomeldOutput(filePath, baseOutputName) {
|
|
251
|
+
async function isRepomeldOutput(filePath, baseOutputName) {
|
|
191
252
|
const fileName = path.basename(filePath);
|
|
192
253
|
const ext = path.extname(baseOutputName);
|
|
193
254
|
const base = path.basename(baseOutputName, ext);
|
|
@@ -199,21 +260,59 @@ function escapeRegex(str) {
|
|
|
199
260
|
return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
200
261
|
}
|
|
201
262
|
|
|
202
|
-
function
|
|
263
|
+
async function checkForUpdates() {
|
|
264
|
+
return new Promise((resolve) => {
|
|
265
|
+
const http = require('http');
|
|
266
|
+
const options = {
|
|
267
|
+
hostname: 'registry.npmjs.org',
|
|
268
|
+
path: `/${PACKAGE_NAME}/latest`,
|
|
269
|
+
method: 'GET',
|
|
270
|
+
timeout: 3000
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
const req = http.request(options, (res) => {
|
|
274
|
+
let data = '';
|
|
275
|
+
res.on('data', chunk => data += chunk);
|
|
276
|
+
res.on('end', () => {
|
|
277
|
+
try {
|
|
278
|
+
const json = JSON.parse(data);
|
|
279
|
+
if (json.version && json.version !== VERSION) {
|
|
280
|
+
resolve({ hasUpdate: true, latestVersion: json.version });
|
|
281
|
+
} else {
|
|
282
|
+
resolve({ hasUpdate: false });
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
resolve({ hasUpdate: false });
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
req.on('error', () => resolve({ hasUpdate: false }));
|
|
291
|
+
req.on('timeout', () => {
|
|
292
|
+
req.destroy();
|
|
293
|
+
resolve({ hasUpdate: false });
|
|
294
|
+
});
|
|
295
|
+
req.end();
|
|
296
|
+
});
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
function showUpdateMessage(currentVersion, latestVersion) {
|
|
300
|
+
console.log(`\n${'ā'.repeat(60)}`);
|
|
301
|
+
console.log(` ā New version available: ${currentVersion} ā ${latestVersion}`);
|
|
302
|
+
console.log(` š¦ Update with: npm install -g ${PACKAGE_NAME}@latest`);
|
|
303
|
+
console.log(`${'ā'.repeat(60)}\n`);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
async function repomeld(options) {
|
|
307
|
+
const startTime = Date.now();
|
|
203
308
|
printBanner();
|
|
204
309
|
|
|
205
310
|
const cwd = process.cwd();
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const desiredOutput = path.resolve(cwd, options.output);
|
|
209
|
-
const outputFile = resolveOutputPath(desiredOutput);
|
|
311
|
+
|
|
312
|
+
const { path: outputFile, number: outputNumber } = await resolveOutputPath(path.resolve(cwd, options.output));
|
|
210
313
|
const outputBaseName = path.basename(options.output);
|
|
211
|
-
|
|
212
|
-
const forceInclude = options
|
|
213
|
-
const rawIgnore = [...DEFAULT_IGNORE, ...IGNORE_FROM_CONFIG, ...(options.ignore || [])];
|
|
214
|
-
const ignoreList = forceInclude.length
|
|
215
|
-
? rawIgnore.filter((ig) => !forceInclude.some((fi) => ig.includes(fi) || fi.includes(ig)))
|
|
216
|
-
: rawIgnore;
|
|
314
|
+
|
|
315
|
+
const { ig, forceInclude } = await buildIgnoreFilter(options);
|
|
217
316
|
const filterExts = options.ext || [];
|
|
218
317
|
const maxFileSizeBytes = (parseFloat(options.maxSize) || 500) * 1024;
|
|
219
318
|
const headerStyle = options.style || "banner";
|
|
@@ -228,91 +327,101 @@ function repomeld(options) {
|
|
|
228
327
|
console.log(`\n š Source : ${cwd}`);
|
|
229
328
|
console.log(` š Output : ${path.relative(cwd, outputFile)}`);
|
|
230
329
|
console.log(` šØ Style : ${headerStyle}`);
|
|
330
|
+
if (!options.noGitignore) console.log(` š .gitignore respected`);
|
|
231
331
|
if (filterExts.length) console.log(` š Filter : .${filterExts.join(", .")}`);
|
|
232
|
-
if (forceInclude.length) console.log(` š Force : ${forceInclude.join(", ")}`);
|
|
332
|
+
if (forceInclude && forceInclude.length) console.log(` š Force : ${forceInclude.join(", ")}`);
|
|
233
333
|
if (dryRun) console.log(` š§Ŗ Dry run : no file will be written`);
|
|
234
334
|
console.log();
|
|
235
335
|
|
|
236
|
-
|
|
336
|
+
console.log(` š Scanning files...`);
|
|
337
|
+
const scanStartTime = Date.now();
|
|
338
|
+
let allFiles = await getAllFilesWithIgnore(cwd, ig, forceInclude, cwd);
|
|
339
|
+
console.log(` ā
Found ${allFiles.length} files in ${formatDuration(Date.now() - scanStartTime)}`);
|
|
237
340
|
|
|
238
|
-
//
|
|
341
|
+
// Apply additional filters
|
|
239
342
|
if (filterExts.length) {
|
|
240
|
-
allFiles = allFiles.filter(
|
|
343
|
+
allFiles = allFiles.filter(f => matchesExtensions(f, filterExts));
|
|
241
344
|
}
|
|
242
|
-
|
|
243
|
-
// Include pattern filter
|
|
244
345
|
if (include.length) {
|
|
245
|
-
allFiles = allFiles.filter(
|
|
346
|
+
allFiles = allFiles.filter(f => matchesPattern(f, include));
|
|
246
347
|
}
|
|
247
|
-
|
|
248
|
-
// Exclude pattern filter
|
|
249
348
|
if (exclude.length) {
|
|
250
|
-
allFiles = allFiles.filter(
|
|
349
|
+
allFiles = allFiles.filter(f => !matchesPattern(f, exclude));
|
|
251
350
|
}
|
|
252
|
-
|
|
253
|
-
// Remove
|
|
254
|
-
|
|
351
|
+
|
|
352
|
+
// Remove repomeld output files
|
|
353
|
+
const filteredFiles = [];
|
|
354
|
+
for (const file of allFiles) {
|
|
355
|
+
const isOutput = await isRepomeldOutput(file, outputBaseName);
|
|
356
|
+
if (!isOutput) filteredFiles.push(file);
|
|
357
|
+
}
|
|
358
|
+
allFiles = filteredFiles;
|
|
255
359
|
|
|
256
360
|
if (allFiles.length === 0) {
|
|
257
361
|
console.log(" ā ļø No matching files found.\n");
|
|
258
362
|
return;
|
|
259
363
|
}
|
|
260
364
|
|
|
365
|
+
console.log(` š Processing ${allFiles.length} files...\n`);
|
|
366
|
+
|
|
261
367
|
let combinedContent = "";
|
|
262
368
|
let skipped = 0;
|
|
263
369
|
let included = 0;
|
|
264
370
|
let totalLines = 0;
|
|
265
371
|
const includedFiles = [];
|
|
266
|
-
|
|
267
|
-
|
|
372
|
+
|
|
373
|
+
const progress = new ProgressIndicator(allFiles.length, ' ');
|
|
374
|
+
|
|
375
|
+
for (let i = 0; i < allFiles.length; i++) {
|
|
376
|
+
const filePath = allFiles[i];
|
|
268
377
|
const relativePath = path.relative(cwd, filePath);
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
378
|
+
|
|
379
|
+
progress.update(i + 1);
|
|
380
|
+
|
|
381
|
+
const isBinary = await isBinaryFile(filePath).catch(() => true);
|
|
382
|
+
if (isBinary) {
|
|
272
383
|
skipped++;
|
|
273
384
|
continue;
|
|
274
385
|
}
|
|
275
|
-
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
console.log(` ā Too large: ${relativePath} (${formatSize(stat.size)})`);
|
|
386
|
+
|
|
387
|
+
const stats = await fs.stat(filePath);
|
|
388
|
+
if (stats.size > maxFileSizeBytes) {
|
|
279
389
|
skipped++;
|
|
280
390
|
continue;
|
|
281
391
|
}
|
|
282
|
-
|
|
392
|
+
|
|
283
393
|
try {
|
|
284
|
-
let content = fs.
|
|
285
|
-
|
|
394
|
+
let content = await fs.readFile(filePath, "utf8");
|
|
395
|
+
|
|
286
396
|
if (options.trim) {
|
|
287
397
|
content = content.trim();
|
|
288
398
|
}
|
|
289
|
-
|
|
399
|
+
|
|
290
400
|
if (linesBefore > 0 || linesAfter > 0) {
|
|
291
401
|
const lines = content.split("\n");
|
|
292
|
-
const start = linesBefore;
|
|
293
|
-
const end = linesAfter > 0 ? lines.length - linesAfter : lines.length;
|
|
402
|
+
const start = Math.min(linesBefore, lines.length);
|
|
403
|
+
const end = linesAfter > 0 ? Math.max(0, lines.length - linesAfter) : lines.length;
|
|
294
404
|
content = lines.slice(start, end).join("\n");
|
|
295
405
|
}
|
|
296
|
-
|
|
406
|
+
|
|
297
407
|
const lineCount = content.split("\n").length;
|
|
298
408
|
totalLines += lineCount;
|
|
299
409
|
includedFiles.push(filePath);
|
|
300
|
-
|
|
301
|
-
combinedContent += buildHeader(headerStyle, relativePath, filePath, lineCount, showMeta);
|
|
410
|
+
|
|
411
|
+
combinedContent += buildHeader(headerStyle, relativePath, filePath, lineCount, showMeta, stats);
|
|
302
412
|
combinedContent += content;
|
|
303
413
|
combinedContent += buildFooter(headerStyle);
|
|
304
|
-
|
|
305
|
-
console.log(` ā
${relativePath}`);
|
|
414
|
+
|
|
306
415
|
included++;
|
|
307
416
|
} catch (err) {
|
|
308
|
-
console.log(` ā Error: ${relativePath} ā ${err.message}`);
|
|
309
417
|
skipped++;
|
|
310
418
|
}
|
|
311
419
|
}
|
|
312
|
-
|
|
420
|
+
|
|
421
|
+
progress.finish();
|
|
422
|
+
|
|
313
423
|
// Build final output
|
|
314
424
|
let finalOutput = "";
|
|
315
|
-
|
|
316
425
|
const timestamp = new Date().toISOString();
|
|
317
426
|
finalOutput += `# Generated by repomeld v${VERSION}\n`;
|
|
318
427
|
finalOutput += `# Date : ${timestamp}\n`;
|
|
@@ -320,19 +429,20 @@ function repomeld(options) {
|
|
|
320
429
|
finalOutput += `# Files : ${included}\n`;
|
|
321
430
|
finalOutput += `# Lines : ${totalLines}\n`;
|
|
322
431
|
finalOutput += `# Author : susheelhbti@gmail.com ā available for freelance & full-time work\n\n`;
|
|
323
|
-
|
|
324
|
-
if (showToc) {
|
|
432
|
+
|
|
433
|
+
if (showToc && includedFiles.length > 0) {
|
|
325
434
|
finalOutput += buildTableOfContents(includedFiles, cwd);
|
|
326
435
|
}
|
|
327
|
-
|
|
436
|
+
|
|
328
437
|
finalOutput += combinedContent;
|
|
329
|
-
|
|
330
|
-
if (!dryRun) {
|
|
331
|
-
fs.
|
|
438
|
+
|
|
439
|
+
if (!dryRun && includedFiles.length > 0) {
|
|
440
|
+
await fs.writeFile(outputFile, finalOutput, "utf8");
|
|
332
441
|
}
|
|
333
|
-
|
|
442
|
+
|
|
334
443
|
const outputSize = formatSize(Buffer.byteLength(finalOutput, "utf8"));
|
|
335
|
-
|
|
444
|
+
const totalTime = Date.now() - startTime;
|
|
445
|
+
|
|
336
446
|
console.log(`
|
|
337
447
|
⨠repomeld complete!
|
|
338
448
|
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
@@ -340,10 +450,19 @@ function repomeld(options) {
|
|
|
340
450
|
ā Skipped : ${skipped} files
|
|
341
451
|
š Lines : ${totalLines}
|
|
342
452
|
š¾ Size : ${outputSize}
|
|
453
|
+
ā±ļø Time : ${formatDuration(totalTime)}
|
|
343
454
|
š Output : ${path.relative(cwd, outputFile)}${dryRun ? " (dry run ā not written)" : ""}
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
`);
|
|
455
|
+
āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā`);
|
|
456
|
+
|
|
457
|
+
console.log(`\n š¼ Need a developer? susheelhbti@gmail.com`);
|
|
458
|
+
|
|
459
|
+
// Check for updates (non-blocking, no prompt)
|
|
460
|
+
if (!options.noUpdateCheck) {
|
|
461
|
+
const updateInfo = await checkForUpdates();
|
|
462
|
+
if (updateInfo.hasUpdate) {
|
|
463
|
+
showUpdateMessage(VERSION, updateInfo.latestVersion);
|
|
464
|
+
}
|
|
465
|
+
}
|
|
347
466
|
}
|
|
348
467
|
|
|
349
468
|
// āāā CLI Definition āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā
|
|
@@ -353,30 +472,31 @@ program
|
|
|
353
472
|
.description("Meld your entire repo into a single file ā perfect for AI context, code reviews & sharing")
|
|
354
473
|
.version(VERSION)
|
|
355
474
|
|
|
356
|
-
|
|
357
|
-
.option("-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
.option("-
|
|
361
|
-
.option("--include <
|
|
362
|
-
.option("--
|
|
363
|
-
.option("-
|
|
364
|
-
.option("
|
|
365
|
-
.option("--
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
.option("-
|
|
369
|
-
.option("--
|
|
370
|
-
.option("--
|
|
371
|
-
.option("--
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
475
|
+
.option("-o, --output <filename>", "Output file name", "repomeld_output.txt")
|
|
476
|
+
.option("-e, --ext <exts...>", "Only include specific extensions")
|
|
477
|
+
.option("--include <patterns...>", "Only include files matching patterns")
|
|
478
|
+
.option("--exclude <patterns...>", "Exclude files matching patterns")
|
|
479
|
+
.option("-i, --ignore <names...>", "Extra folders/files to ignore")
|
|
480
|
+
.option("--force-include <names...>", "Force-include files even if ignored")
|
|
481
|
+
.option("--max-size <kb>", "Skip files larger than N KB", "500")
|
|
482
|
+
.option("--no-gitignore", "Ignore .gitignore file")
|
|
483
|
+
.option("-s, --style <style>", "Header style: banner | markdown | minimal", "banner")
|
|
484
|
+
.option("--no-toc", "Disable table of contents")
|
|
485
|
+
.option("--no-meta", "Hide file metadata")
|
|
486
|
+
.option("--trim", "Trim leading/trailing whitespace per file")
|
|
487
|
+
.option("--lines-before <n>", "Skip first N lines of each file", parseInt)
|
|
488
|
+
.option("--lines-after <n>", "Skip last N lines of each file", parseInt)
|
|
489
|
+
.option("--dry-run", "Preview without writing output")
|
|
490
|
+
.option("--no-update-check", "Skip checking for updates")
|
|
491
|
+
|
|
492
|
+
.action(async (options) => {
|
|
493
|
+
try {
|
|
494
|
+
await repomeld(options);
|
|
495
|
+
} catch (error) {
|
|
496
|
+
console.error(`\n ā Error: ${error.message}`);
|
|
497
|
+
if (process.env.DEBUG) console.error(error);
|
|
498
|
+
process.exit(1);
|
|
499
|
+
}
|
|
380
500
|
});
|
|
381
501
|
|
|
382
|
-
program.parse(process.argv);
|
|
502
|
+
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repomeld",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.5",
|
|
4
4
|
"description": "Meld your entire repo into a single file ā perfect for AI context, code reviews & sharing",
|
|
5
5
|
"main": "bin/cli.js",
|
|
6
6
|
"bin": {
|
|
7
7
|
"repomeld": "bin/cli.js"
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
|
-
"start": "node bin/cli.js"
|
|
10
|
+
"start": "node bin/cli.js",
|
|
11
|
+
"test": "node bin/cli.js --dry-run",
|
|
12
|
+
"prepublishOnly": "npm run test",
|
|
13
|
+
"postinstall": "node -e \"console.log('\\nš¦ repomeld installed successfully! Run: repomeld --help\\n')\""
|
|
11
14
|
},
|
|
12
15
|
"keywords": [
|
|
13
16
|
"cli",
|
|
@@ -18,15 +21,38 @@
|
|
|
18
21
|
"concat",
|
|
19
22
|
"ai-context",
|
|
20
23
|
"code-review",
|
|
21
|
-
"repomeld"
|
|
24
|
+
"repomeld",
|
|
25
|
+
"git",
|
|
26
|
+
"repository"
|
|
22
27
|
],
|
|
23
|
-
"author": "",
|
|
28
|
+
"author": "Susheel <susheelhbti@gmail.com>",
|
|
24
29
|
"license": "MIT",
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/sakshsky/repomeld.git"
|
|
33
|
+
},
|
|
34
|
+
"bugs": {
|
|
35
|
+
"url": "https://github.com/sakshsky/repomeld/issues"
|
|
36
|
+
},
|
|
37
|
+
"homepage": "https://github.com/sakshsky/repomeld#readme",
|
|
25
38
|
"dependencies": {
|
|
26
|
-
"commander": "^11.
|
|
27
|
-
|
|
39
|
+
"commander": "^11.1.0",
|
|
40
|
+
"ignore": "^5.3.2",
|
|
41
|
+
"isbinaryfile": "^5.0.0"
|
|
42
|
+
},
|
|
43
|
+
"devDependencies": {
|
|
44
|
+
"eslint": "^8.56.0",
|
|
45
|
+
"prettier": "^3.1.1"
|
|
28
46
|
},
|
|
29
47
|
"engines": {
|
|
30
48
|
"node": ">=14.0.0"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"bin/",
|
|
52
|
+
"README.md",
|
|
53
|
+
"LICENSE"
|
|
54
|
+
],
|
|
55
|
+
"publishConfig": {
|
|
56
|
+
"access": "public"
|
|
31
57
|
}
|
|
32
58
|
}
|