repomeld 2.0.0 โ 2.0.2
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 +215 -124
- package/bin/cli.js +314 -172
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
> Meld your entire repo into a single file โ perfect for AI context, code reviews & sharing.
|
|
4
4
|
|
|
5
|
+
> ๐ผ **Author [`susheelhbti@gmail.com`](mailto:susheelhbti@gmail.com) is open to freelance & full-time work.**
|
|
6
|
+
|
|
5
7
|
---
|
|
6
8
|
|
|
7
9
|
## Install
|
|
@@ -19,88 +21,183 @@ cd your-project
|
|
|
19
21
|
repomeld
|
|
20
22
|
```
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
**No flags needed.** repomeld will walk you through 7 quick questions and configure itself:
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
28
|
+
โ repomeld v1.2.0 โ
|
|
29
|
+
โ Meld your repo into one file ๐ฅ โ
|
|
30
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
31
|
+
|
|
32
|
+
๐ผ Author susheelhbti@gmail.com is open to freelance & full-time work
|
|
33
|
+
|
|
34
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
35
|
+
โ Interactive setup โ answer 7 quick questions โ
|
|
36
|
+
โ Press Enter to accept the [default] value โ
|
|
37
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
38
|
+
|
|
39
|
+
๐ 1/7 Output filename? [repomeld_output.txt]
|
|
40
|
+
>
|
|
41
|
+
|
|
42
|
+
๐จ 2/7 Header style?
|
|
43
|
+
1) banner โ clear dividers with file info (default)
|
|
44
|
+
2) markdown โ great for pasting into AI prompts
|
|
45
|
+
3) minimal โ filename only, no extra formatting
|
|
46
|
+
>
|
|
47
|
+
|
|
48
|
+
๐ 3/7 Filter to specific file extensions?
|
|
49
|
+
e.g. js ts php py (leave blank = include ALL files)
|
|
50
|
+
>
|
|
51
|
+
|
|
52
|
+
๐๏ธ 4/7 Enable structure-only mode? (y/n) [n]
|
|
53
|
+
Strips function bodies โ keeps class names + signatures only.
|
|
54
|
+
Cuts a 400-line file to ~30 lines. Perfect for AI architecture review.
|
|
55
|
+
>
|
|
56
|
+
|
|
57
|
+
๐ 5/7 Generate a code review graph? (y/n) [n]
|
|
58
|
+
Adds a Mermaid call graph at the top of the output.
|
|
59
|
+
AI models read Mermaid natively โ shows which files connect to which.
|
|
60
|
+
>
|
|
61
|
+
|
|
62
|
+
๐ 6/7 Include a table of contents? (y/n) [y]
|
|
63
|
+
>
|
|
64
|
+
|
|
65
|
+
๐งช 7/7 Dry run only? (y/n) [n]
|
|
66
|
+
Preview which files would be included without writing anything.
|
|
67
|
+
>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
After answering, it shows a summary and runs:
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
74
|
+
โ Your configuration โ
|
|
75
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
|
|
76
|
+
โ Output : repomeld_output.txt โ
|
|
77
|
+
โ Style : markdown โ
|
|
78
|
+
โ Extensions : php ts โ
|
|
79
|
+
โ Structure only : yes โ
โ
|
|
80
|
+
โ Review graph : yes โ
โ
|
|
81
|
+
โ Table of cont. : yes โ
|
|
82
|
+
โ Dry run : no โ
|
|
83
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
84
|
+
```
|
|
23
85
|
|
|
24
86
|
---
|
|
25
87
|
|
|
26
|
-
##
|
|
88
|
+
## Skip the Wizard (Power Users)
|
|
89
|
+
|
|
90
|
+
If you pass any flags, repomeld skips the wizard and runs directly:
|
|
27
91
|
|
|
28
|
-
|
|
92
|
+
```bash
|
|
93
|
+
repomeld --ext ts tsx --style markdown --output context.md
|
|
94
|
+
```
|
|
29
95
|
|
|
30
|
-
|
|
96
|
+
Force the wizard at any time with:
|
|
31
97
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
| Build output | `dist/`, `build/`, `.next/`, `.nuxt/`, `coverage/` |
|
|
36
|
-
| Vendor folders | `vendor/`, `libs/`, `plugins/`, `assets/vendor/`, `wwwroot/lib/` |
|
|
37
|
-
| Bootstrap | `bootstrap.min.css`, `bootstrap.bundle.min.js`, `bootstrap-icons` |
|
|
38
|
-
| jQuery | `jquery.min.js`, `jquery-ui.min.js`, `jquery.validate.min.js` |
|
|
39
|
-
| Font Awesome | `all.min.css`, `fa-solid-900.woff2`, `fontawesome-free/` |
|
|
40
|
-
| Tailwind CSS | `tailwind.min.css`, `tailwindcss/` |
|
|
41
|
-
| Materialize | `materialize.min.css`, `materialize.min.js` |
|
|
42
|
-
| Bulma | `bulma.min.css` |
|
|
43
|
-
| Semantic UI | `semantic.min.css`, `semantic.min.js` |
|
|
44
|
-
| Foundation | `foundation.min.css`, `foundation.min.js` |
|
|
45
|
-
| UIkit | `uikit.min.css`, `uikit.min.js` |
|
|
46
|
-
| Animate.css | `animate.min.css`, `animate.css` |
|
|
47
|
-
| GSAP | `gsap.min.js`, `ScrollTrigger.min.js`, `TweenMax.min.js` |
|
|
48
|
-
| Swiper / Slick / Owl | `swiper-bundle.min.js`, `slick.min.js`, `owl.carousel.min.js` |
|
|
49
|
-
| Chart.js / D3 / Three.js | `chart.min.js`, `d3.min.js`, `three.min.js` |
|
|
50
|
-
| Axios / Lodash / Moment | `axios.min.js`, `lodash.min.js`, `moment.min.js` |
|
|
51
|
-
| Select2 / Flatpickr | `select2.min.js`, `flatpickr.min.js` |
|
|
52
|
-
| DataTables | `datatables.min.js`, `jquery.dataTables.min.js` |
|
|
53
|
-
| Toastr / SweetAlert | `toastr.min.js`, `sweetalert2.min.js` |
|
|
54
|
-
| Lightbox / Fancybox | `lightbox.min.js`, `glightbox.min.js` |
|
|
55
|
-
| Rich text editors | `quill.min.js`, `tinymce/`, `ckeditor/`, `codemirror/` |
|
|
56
|
-
| Syntax highlighting | `prism.min.js`, `highlight.min.js` |
|
|
57
|
-
| Video / Audio players | `video.js`, `plyr.min.js` |
|
|
58
|
-
| Maps | `leaflet.min.js`, `mapbox-gl.min.js` |
|
|
59
|
-
| Icon sets | `remixicon.css`, `boxicons.css`, `ionicons.css`, `lucide.min.js` |
|
|
60
|
-
| Socket.IO | `socket.io.min.js` |
|
|
61
|
-
| Misc utilities | `lazysizes.js`, `lottie.min.js`, `particles.js`, `typed.js`, `sortable.min.js`, `masonry.js` |
|
|
62
|
-
| Admin templates | `AdminLTE/`, `metronic/`, `coreui/`, `gentelella/` |
|
|
63
|
-
| Env / Secrets | `.env`, `.env.local`, `.env.production` |
|
|
98
|
+
```bash
|
|
99
|
+
repomeld --wizard
|
|
100
|
+
```
|
|
64
101
|
|
|
65
102
|
---
|
|
66
103
|
|
|
67
|
-
##
|
|
104
|
+
## New Features in v1.2.0
|
|
105
|
+
|
|
106
|
+
### `--structure-only` โ Skeleton mode
|
|
68
107
|
|
|
69
|
-
|
|
108
|
+
Extracts **class names + function signatures** from every file. Strips all body code.
|
|
70
109
|
|
|
71
110
|
```bash
|
|
72
|
-
|
|
73
|
-
|
|
111
|
+
repomeld --structure-only
|
|
112
|
+
```
|
|
74
113
|
|
|
75
|
-
|
|
76
|
-
|
|
114
|
+
**Before** (400 lines of PHP):
|
|
115
|
+
```php
|
|
116
|
+
public function available(Request $request)
|
|
117
|
+
{
|
|
118
|
+
$validated = $request->validate([...]);
|
|
119
|
+
$guards = SecurityGuard::with('user')
|
|
120
|
+
->where('is_available', true)
|
|
121
|
+
...
|
|
122
|
+
// 50 more lines
|
|
123
|
+
}
|
|
124
|
+
```
|
|
77
125
|
|
|
78
|
-
|
|
79
|
-
|
|
126
|
+
**After** (1 line):
|
|
127
|
+
```php
|
|
128
|
+
public function available(Request $request) { ... }
|
|
80
129
|
```
|
|
81
130
|
|
|
82
|
-
|
|
131
|
+
Supports **PHP, JavaScript, TypeScript, Python**. Other languages fall back to the first 30 lines.
|
|
132
|
+
|
|
133
|
+
Perfect for:
|
|
134
|
+
- Giving AI a full architecture overview without hitting context limits
|
|
135
|
+
- Code review focusing on structure, not implementation
|
|
136
|
+
- Onboarding โ understand a codebase in seconds
|
|
83
137
|
|
|
84
138
|
---
|
|
85
139
|
|
|
86
|
-
|
|
140
|
+
### `--review-graph` โ Mermaid call graph
|
|
87
141
|
|
|
88
|
-
|
|
142
|
+
Generates a **Mermaid flowchart** at the top of your output showing which files import/call which.
|
|
89
143
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
144
|
+
```bash
|
|
145
|
+
repomeld --review-graph
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
Output (inside the file, readable by AI natively):
|
|
149
|
+
|
|
150
|
+
````
|
|
151
|
+
```mermaid
|
|
152
|
+
flowchart TD
|
|
153
|
+
%% Code Review Graph โ generated by repomeld
|
|
154
|
+
|
|
155
|
+
src_index_js["๐ index.js\n[repomeld, getAllFiles, buildHeader]"]
|
|
156
|
+
src_utils_js["๐ utils.js\n[formatSize, getLanguage]"]
|
|
157
|
+
src_graph_js["๐ graph.js\n[buildReviewGraph]"]
|
|
158
|
+
|
|
159
|
+
src_index_js --> src_utils_js
|
|
160
|
+
src_index_js --> src_graph_js
|
|
161
|
+
```
|
|
162
|
+
````
|
|
163
|
+
|
|
164
|
+
AI models like Claude, GPT-4, and Gemini read Mermaid natively โ no image needed.
|
|
165
|
+
|
|
166
|
+
---
|
|
167
|
+
|
|
168
|
+
### Power combo: both together
|
|
169
|
+
|
|
170
|
+
```bash
|
|
171
|
+
repomeld --structure-only --review-graph
|
|
98
172
|
```
|
|
99
173
|
|
|
100
|
-
|
|
101
|
-
1.
|
|
102
|
-
2.
|
|
103
|
-
3.
|
|
174
|
+
AI gets:
|
|
175
|
+
1. **The map** โ which files connect to which (graph)
|
|
176
|
+
2. **The skeleton** โ every class and function signature (structure)
|
|
177
|
+
3. **Zero noise** โ no implementation details
|
|
178
|
+
|
|
179
|
+
This is the best way to give AI a complete understanding of a large codebase without wasting context.
|
|
180
|
+
|
|
181
|
+
---
|
|
182
|
+
|
|
183
|
+
## Smart Auto-Ignore
|
|
184
|
+
|
|
185
|
+
repomeld automatically skips common public libraries and vendor files.
|
|
186
|
+
|
|
187
|
+
| Category | Examples |
|
|
188
|
+
|---|---|
|
|
189
|
+
| Project meta | `package.json`, `README.md`, `yarn.lock`, `.gitignore` |
|
|
190
|
+
| Build output | `dist/`, `build/`, `.next/`, `.nuxt/`, `coverage/` |
|
|
191
|
+
| Vendor folders | `vendor/`, `libs/`, `plugins/`, `assets/vendor/` |
|
|
192
|
+
| Bootstrap | `bootstrap.min.css`, `bootstrap.bundle.min.js` |
|
|
193
|
+
| jQuery | `jquery.min.js`, `jquery-ui.min.js` |
|
|
194
|
+
| Font Awesome | `all.min.css`, `fontawesome-free/` |
|
|
195
|
+
| Tailwind | `tailwind.min.css`, `tailwindcss/` |
|
|
196
|
+
| Chart / D3 / Three | `chart.min.js`, `d3.min.js`, `three.min.js` |
|
|
197
|
+
| Axios / Lodash | `axios.min.js`, `lodash.min.js` |
|
|
198
|
+
| Rich text editors | `tinymce/`, `ckeditor/`, `codemirror/` |
|
|
199
|
+
| Admin templates | `AdminLTE/`, `metronic/`, `coreui/` |
|
|
200
|
+
| Env / Secrets | `.env`, `.env.local`, `.env.production` |
|
|
104
201
|
|
|
105
202
|
---
|
|
106
203
|
|
|
@@ -112,6 +209,7 @@ Usage: repomeld [options]
|
|
|
112
209
|
Options:
|
|
113
210
|
-V, --version Show version
|
|
114
211
|
-h, --help Show help
|
|
212
|
+
--wizard Interactive step-by-step setup
|
|
115
213
|
|
|
116
214
|
Output:
|
|
117
215
|
-o, --output <filename> Output file name (default: "repomeld_output.txt")
|
|
@@ -124,14 +222,11 @@ Options:
|
|
|
124
222
|
--exclude <patterns...> Exclude files matching patterns
|
|
125
223
|
e.g. --exclude test spec
|
|
126
224
|
-i, --ignore <names...> Extra folders/files to ignore
|
|
127
|
-
e.g. --ignore dist .next
|
|
128
225
|
--force-include <names...> Force-include files ignored by default
|
|
129
|
-
e.g. --force-include vendor bootstrap jquery
|
|
130
226
|
--max-size <kb> Skip files larger than N KB (default: 500)
|
|
131
227
|
|
|
132
228
|
Formatting:
|
|
133
229
|
-s, --style <style> Header style: banner | markdown | minimal
|
|
134
|
-
(default: banner)
|
|
135
230
|
--no-toc Disable table of contents
|
|
136
231
|
--no-meta Hide file metadata (lines, size, lang)
|
|
137
232
|
--trim Trim leading/trailing whitespace per file
|
|
@@ -139,7 +234,11 @@ Options:
|
|
|
139
234
|
Advanced:
|
|
140
235
|
--lines-before <n> Skip first N lines of each file
|
|
141
236
|
--lines-after <n> Skip last N lines of each file
|
|
142
|
-
--dry-run Preview
|
|
237
|
+
--dry-run Preview without writing output
|
|
238
|
+
|
|
239
|
+
AI / Code Review:
|
|
240
|
+
--structure-only Extract class + function signatures only (no body)
|
|
241
|
+
--review-graph Prepend a Mermaid call graph to the output
|
|
143
242
|
```
|
|
144
243
|
|
|
145
244
|
---
|
|
@@ -147,99 +246,87 @@ Options:
|
|
|
147
246
|
## Examples
|
|
148
247
|
|
|
149
248
|
```bash
|
|
150
|
-
#
|
|
249
|
+
# Wizard mode (auto when no flags given)
|
|
151
250
|
repomeld
|
|
152
251
|
|
|
153
|
-
#
|
|
154
|
-
repomeld --
|
|
155
|
-
|
|
156
|
-
# Exclude test files
|
|
157
|
-
repomeld --exclude test spec __tests__
|
|
158
|
-
|
|
159
|
-
# Force-include your customized jQuery even though it is ignored by default
|
|
160
|
-
repomeld --force-include jquery
|
|
161
|
-
|
|
162
|
-
# Force-include multiple libs
|
|
163
|
-
repomeld --force-include vendor bootstrap select2
|
|
252
|
+
# Force wizard even with flags
|
|
253
|
+
repomeld --wizard
|
|
164
254
|
|
|
165
|
-
#
|
|
166
|
-
repomeld --
|
|
255
|
+
# Only PHP controllers, structure only
|
|
256
|
+
repomeld --ext php --include Controllers --structure-only
|
|
167
257
|
|
|
168
|
-
#
|
|
169
|
-
repomeld --
|
|
258
|
+
# Full code + call graph
|
|
259
|
+
repomeld --review-graph
|
|
170
260
|
|
|
171
|
-
#
|
|
172
|
-
repomeld --output
|
|
261
|
+
# Best for AI: skeleton + graph, markdown style
|
|
262
|
+
repomeld --structure-only --review-graph --style markdown --output ai_context.md
|
|
173
263
|
|
|
174
|
-
#
|
|
175
|
-
repomeld --
|
|
264
|
+
# Only TypeScript inside src/
|
|
265
|
+
repomeld --ext ts tsx --include src/
|
|
176
266
|
|
|
177
|
-
#
|
|
178
|
-
repomeld --
|
|
267
|
+
# Exclude tests
|
|
268
|
+
repomeld --exclude test spec __tests__
|
|
179
269
|
|
|
180
|
-
# Dry run โ preview
|
|
270
|
+
# Dry run โ preview only
|
|
181
271
|
repomeld --dry-run
|
|
182
272
|
|
|
183
|
-
#
|
|
184
|
-
repomeld --
|
|
185
|
-
|
|
186
|
-
# Combine options
|
|
187
|
-
repomeld --ext ts tsx --include src/ --style markdown --output ai_context.md
|
|
273
|
+
# Large repo: structure only, skip files over 100 KB
|
|
274
|
+
repomeld --structure-only --max-size 100
|
|
188
275
|
```
|
|
189
276
|
|
|
190
277
|
---
|
|
191
278
|
|
|
192
|
-
## Output Format
|
|
279
|
+
## Output Format
|
|
280
|
+
|
|
281
|
+
### With `--review-graph`
|
|
193
282
|
|
|
194
283
|
```
|
|
195
|
-
# Generated by repomeld v1.
|
|
284
|
+
# Generated by repomeld v1.2.0
|
|
196
285
|
# Date : 2024-01-01T00:00:00.000Z
|
|
197
286
|
# Source : /your/project
|
|
198
|
-
# Files :
|
|
199
|
-
# Lines :
|
|
287
|
+
# Files : 8
|
|
288
|
+
# Lines : 312
|
|
289
|
+
# Graph : review-graph enabled
|
|
290
|
+
# Author : susheelhbti@gmail.com (open to work)
|
|
200
291
|
|
|
201
|
-
TABLE OF CONTENTS
|
|
202
292
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
203
|
-
|
|
204
|
-
2. src/utils/helper.ts
|
|
205
|
-
...
|
|
293
|
+
CODE REVIEW GRAPH
|
|
206
294
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
207
295
|
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
<file contents>
|
|
296
|
+
```mermaid
|
|
297
|
+
flowchart TD
|
|
298
|
+
...
|
|
213
299
|
```
|
|
214
300
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
## Output Format (markdown style)
|
|
218
|
-
|
|
219
|
-
````
|
|
220
|
-
## ๐ src/index.ts [42 lines | 1.2 KB | typescript]
|
|
221
|
-
|
|
222
|
-
```typescript
|
|
223
|
-
<file contents>
|
|
301
|
+
TABLE OF CONTENTS
|
|
302
|
+
...
|
|
224
303
|
```
|
|
225
|
-
````
|
|
226
304
|
|
|
227
|
-
|
|
305
|
+
### With `--structure-only` (PHP example)
|
|
228
306
|
|
|
229
|
-
|
|
307
|
+
```php
|
|
308
|
+
namespace App\Http\Controllers;
|
|
309
|
+
use App\Models\SecurityGuard;
|
|
310
|
+
use App\Models\SecurityRequest;
|
|
230
311
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
312
|
+
class GuardController extends Controller
|
|
313
|
+
{
|
|
314
|
+
/** Get guard's own profile */
|
|
315
|
+
public function profile() { ... }
|
|
316
|
+
public function updateProfile(Request $request) { ... }
|
|
317
|
+
public function bookings() { ... }
|
|
318
|
+
public function available(Request $request) { ... }
|
|
319
|
+
public function toggleAvailability() { ... }
|
|
320
|
+
public function rate(Request $request, $id) { ... }
|
|
321
|
+
}
|
|
322
|
+
```
|
|
236
323
|
|
|
237
324
|
---
|
|
238
325
|
|
|
239
326
|
## Publish / Update
|
|
240
327
|
|
|
241
328
|
```bash
|
|
242
|
-
npm version patch # 1.
|
|
329
|
+
npm version patch # 1.2.0 โ 1.2.1
|
|
243
330
|
npm publish
|
|
244
331
|
```
|
|
245
332
|
|
|
@@ -247,10 +334,14 @@ npm publish
|
|
|
247
334
|
|
|
248
335
|
## Contributing
|
|
249
336
|
|
|
250
|
-
Found a popular library that should be in the ignore list? Open a PR
|
|
337
|
+
Found a popular library that should be in the ignore list? Open a PR!
|
|
251
338
|
|
|
252
339
|
---
|
|
253
340
|
|
|
254
341
|
## License
|
|
255
342
|
|
|
256
|
-
MIT
|
|
343
|
+
MIT
|
|
344
|
+
|
|
345
|
+
---
|
|
346
|
+
|
|
347
|
+
> ๐ผ **Looking to hire?** The author [`susheelhbti@gmail.com`](mailto:susheelhbti@gmail.com) is available for freelance and full-time work.
|
package/bin/cli.js
CHANGED
|
@@ -2,28 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
const fs = require("fs");
|
|
4
4
|
const path = require("path");
|
|
5
|
+
const readline = require("readline");
|
|
5
6
|
const { program } = require("commander");
|
|
6
7
|
|
|
7
|
-
const VERSION = "1.
|
|
8
|
+
const VERSION = "1.2.0";
|
|
9
|
+
|
|
10
|
+
const AUTHOR_NOTE = " ๐ผ Author susheelhbti@gmail.com is open to freelance & full-time work";
|
|
8
11
|
|
|
9
12
|
const DEFAULT_IGNORE = [
|
|
10
|
-
"node_modules",
|
|
11
|
-
".
|
|
12
|
-
".
|
|
13
|
-
".
|
|
14
|
-
".env.production",
|
|
15
|
-
".DS_Store",
|
|
16
|
-
"package-lock.json",
|
|
17
|
-
"yarn.lock",
|
|
18
|
-
"pnpm-lock.yaml",
|
|
19
|
-
".next",
|
|
20
|
-
".nuxt",
|
|
21
|
-
"dist",
|
|
22
|
-
"build",
|
|
23
|
-
".cache",
|
|
24
|
-
"package.json",
|
|
25
|
-
"README.md",
|
|
26
|
-
"repomeld_output.txt",
|
|
13
|
+
"node_modules", ".git", ".env", ".env.local", ".env.production",
|
|
14
|
+
".DS_Store", "package-lock.json", "yarn.lock", "pnpm-lock.yaml",
|
|
15
|
+
".next", ".nuxt", "dist", "build", ".cache", "package.json",
|
|
16
|
+
"README.md", "repomeld_output.txt",
|
|
27
17
|
];
|
|
28
18
|
|
|
29
19
|
function loadIgnoreConfig() {
|
|
@@ -33,9 +23,7 @@ function loadIgnoreConfig() {
|
|
|
33
23
|
if (fs.existsSync(loc)) {
|
|
34
24
|
try {
|
|
35
25
|
const data = JSON.parse(fs.readFileSync(loc, "utf8"));
|
|
36
|
-
if (Array.isArray(data.ignore))
|
|
37
|
-
return data.ignore;
|
|
38
|
-
}
|
|
26
|
+
if (Array.isArray(data.ignore)) return data.ignore;
|
|
39
27
|
} catch {
|
|
40
28
|
console.warn(` โ ๏ธ Could not parse ${loc}, using defaults.`);
|
|
41
29
|
}
|
|
@@ -62,31 +50,17 @@ function getLanguage(filePath) {
|
|
|
62
50
|
|
|
63
51
|
function getAllFiles(dirPath, ignoreList, fileList = []) {
|
|
64
52
|
let entries;
|
|
65
|
-
try {
|
|
66
|
-
|
|
67
|
-
} catch {
|
|
68
|
-
return fileList;
|
|
69
|
-
}
|
|
70
|
-
|
|
53
|
+
try { entries = fs.readdirSync(dirPath, { withFileTypes: true }); }
|
|
54
|
+
catch { return fileList; }
|
|
71
55
|
for (const entry of entries) {
|
|
72
56
|
const fullPath = path.join(dirPath, entry.name);
|
|
73
57
|
const relativePath = path.relative(process.cwd(), fullPath);
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
) {
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
if (entry.isDirectory()) {
|
|
84
|
-
getAllFiles(fullPath, ignoreList, fileList);
|
|
85
|
-
} else if (entry.isFile()) {
|
|
86
|
-
fileList.push(fullPath);
|
|
87
|
-
}
|
|
58
|
+
if (ignoreList.some(
|
|
59
|
+
(ig) => entry.name === ig || relativePath.startsWith(ig + path.sep) || relativePath === ig
|
|
60
|
+
)) continue;
|
|
61
|
+
if (entry.isDirectory()) getAllFiles(fullPath, ignoreList, fileList);
|
|
62
|
+
else if (entry.isFile()) fileList.push(fullPath);
|
|
88
63
|
}
|
|
89
|
-
|
|
90
64
|
return fileList;
|
|
91
65
|
}
|
|
92
66
|
|
|
@@ -96,13 +70,9 @@ function isBinaryFile(filePath) {
|
|
|
96
70
|
const fd = fs.openSync(filePath, "r");
|
|
97
71
|
const bytesRead = fs.readSync(fd, buffer, 0, 512, 0);
|
|
98
72
|
fs.closeSync(fd);
|
|
99
|
-
for (let i = 0; i < bytesRead; i++)
|
|
100
|
-
if (buffer[i] === 0) return true;
|
|
101
|
-
}
|
|
73
|
+
for (let i = 0; i < bytesRead; i++) if (buffer[i] === 0) return true;
|
|
102
74
|
return false;
|
|
103
|
-
} catch {
|
|
104
|
-
return true;
|
|
105
|
-
}
|
|
75
|
+
} catch { return true; }
|
|
106
76
|
}
|
|
107
77
|
|
|
108
78
|
function matchesExtensions(filePath, exts) {
|
|
@@ -125,145 +95,308 @@ function formatSize(bytes) {
|
|
|
125
95
|
|
|
126
96
|
function printBanner() {
|
|
127
97
|
console.log(`
|
|
128
|
-
|
|
129
|
-
โ
|
|
130
|
-
โ Meld your repo into one file โ
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
console.log(
|
|
98
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
99
|
+
โ repomeld v${VERSION} โ
|
|
100
|
+
โ Meld your repo into one file ๐ฅ โ
|
|
101
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ`);
|
|
102
|
+
console.log(AUTHOR_NOTE);
|
|
103
|
+
console.log();
|
|
134
104
|
}
|
|
135
105
|
|
|
136
106
|
function buildHeader(style, relativePath, filePath, lineCount, showMeta) {
|
|
137
107
|
const lang = getLanguage(filePath);
|
|
138
108
|
const size = formatSize(fs.statSync(filePath).size);
|
|
139
109
|
const meta = showMeta ? ` [${lineCount} lines | ${size}${lang ? " | " + lang : ""}]` : "";
|
|
140
|
-
|
|
141
|
-
if (style === "
|
|
142
|
-
return `\n## ๐ ${relativePath}${meta}\n\n\`\`\`${lang}\n`;
|
|
143
|
-
}
|
|
144
|
-
if (style === "minimal") {
|
|
145
|
-
return `\n# ${relativePath}\n`;
|
|
146
|
-
}
|
|
147
|
-
// default: banner style
|
|
110
|
+
if (style === "markdown") return `\n## ๐ ${relativePath}${meta}\n\n\`\`\`${lang}\n`;
|
|
111
|
+
if (style === "minimal") return `\n# ${relativePath}\n`;
|
|
148
112
|
const divider = "โ".repeat(60);
|
|
149
113
|
return `\n${divider}\n FILE: ${relativePath}${meta}\n${divider}\n\n`;
|
|
150
114
|
}
|
|
151
115
|
|
|
152
116
|
function buildFooter(style) {
|
|
153
|
-
|
|
154
|
-
return "\n";
|
|
117
|
+
return style === "markdown" ? "\n```\n" : "\n";
|
|
155
118
|
}
|
|
156
119
|
|
|
157
120
|
function buildTableOfContents(files, cwd) {
|
|
158
121
|
let toc = "TABLE OF CONTENTS\n" + "โ".repeat(60) + "\n";
|
|
159
122
|
files.forEach((f, i) => {
|
|
160
|
-
|
|
161
|
-
toc += ` ${String(i + 1).padStart(3, " ")}. ${rel}\n`;
|
|
123
|
+
toc += ` ${String(i + 1).padStart(3, " ")}. ${path.relative(cwd, f)}\n`;
|
|
162
124
|
});
|
|
163
125
|
toc += "โ".repeat(60) + "\n\n";
|
|
164
126
|
return toc;
|
|
165
127
|
}
|
|
166
128
|
|
|
167
|
-
|
|
168
|
-
|
|
129
|
+
// โโโ STRUCTURE EXTRACTION โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
130
|
+
|
|
131
|
+
function extractStructure(content, filePath) {
|
|
132
|
+
const lang = getLanguage(filePath);
|
|
133
|
+
const lines = content.split("\n");
|
|
134
|
+
const output = [];
|
|
135
|
+
|
|
136
|
+
if (lang === "php") {
|
|
137
|
+
let insideClass = false;
|
|
138
|
+
let braceDepth = 0;
|
|
139
|
+
let classStartDepth = 0;
|
|
140
|
+
for (const line of lines) {
|
|
141
|
+
const trimmed = line.trim();
|
|
142
|
+
if (trimmed.startsWith("namespace ") || trimmed.startsWith("use ")) { output.push(line); continue; }
|
|
143
|
+
const classMatch = trimmed.match(/^(abstract\s+class|class|interface|trait)\s+(\w+)(\s+extends\s+\w+)?(\s+implements\s+[\w,\s]+)?/);
|
|
144
|
+
if (classMatch) {
|
|
145
|
+
if (output.length && output[output.length - 1] !== "") output.push("");
|
|
146
|
+
output.push(line); insideClass = true; classStartDepth = braceDepth;
|
|
147
|
+
}
|
|
148
|
+
braceDepth += (line.match(/\{/g) || []).length;
|
|
149
|
+
braceDepth -= (line.match(/\}/g) || []).length;
|
|
150
|
+
if (insideClass && braceDepth <= classStartDepth && trimmed === "}") {
|
|
151
|
+
output.push("}"); insideClass = false; output.push(""); continue;
|
|
152
|
+
}
|
|
153
|
+
if (trimmed.startsWith("/**") || trimmed.startsWith("*") || trimmed.startsWith("*/")) {
|
|
154
|
+
if (insideClass) output.push(line); continue;
|
|
155
|
+
}
|
|
156
|
+
if (insideClass) {
|
|
157
|
+
const methodMatch = trimmed.match(/^(public|protected|private|static|abstract|final|\s)*(function\s+\w+\s*\([^)]*\)(\s*:\s*[\w\?\|\\]+)?)/);
|
|
158
|
+
if (methodMatch) output.push(` ${methodMatch[0].trim()} { ... }`);
|
|
159
|
+
if (trimmed.match(/^(public|protected|private|const)\s+/) && trimmed.endsWith(";") && !trimmed.includes("function"))
|
|
160
|
+
output.push(` ${trimmed}`);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
return output.join("\n");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (["javascript", "typescript"].includes(lang)) {
|
|
167
|
+
for (const line of lines) {
|
|
168
|
+
const trimmed = line.trim();
|
|
169
|
+
if (trimmed.match(/^(export\s+)?(abstract\s+)?class\s+\w+/)) { output.push(line); continue; }
|
|
170
|
+
if (trimmed.match(/^(export\s+)?(async\s+)?function\s+\w+\s*\(/)) {
|
|
171
|
+
output.push(`${line.match(/^\s*/)[0]}${trimmed.replace(/\{[\s\S]*$/, "{ ... }")}`); continue;
|
|
172
|
+
}
|
|
173
|
+
if (trimmed.match(/^(export\s+)?(const|let|var)\s+\w+\s*=\s*(async\s+)?\(/)) {
|
|
174
|
+
output.push(`${line.match(/^\s*/)[0]}${trimmed.split("=>")[0].trim()} => { ... }`); continue;
|
|
175
|
+
}
|
|
176
|
+
if (trimmed.match(/^(async\s+)?(\w+)\s*\([^)]*\)\s*(\:\s*[\w<>\[\]]+)?\s*\{/) &&
|
|
177
|
+
!["if","while","for","switch"].some(k => trimmed.startsWith(k))) {
|
|
178
|
+
output.push(`${line.match(/^\s*/)[0]}${trimmed.replace(/\{[\s\S]*$/, "{ ... }")}`); continue;
|
|
179
|
+
}
|
|
180
|
+
if (trimmed.startsWith("import ")) output.push(line);
|
|
181
|
+
if (trimmed === "}") output.push(line);
|
|
182
|
+
}
|
|
183
|
+
return output.join("\n");
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (lang === "python") {
|
|
187
|
+
for (const line of lines) {
|
|
188
|
+
const t = line.trim();
|
|
189
|
+
if (t.startsWith("class ") || t.startsWith("def ") || t.startsWith("async def "))
|
|
190
|
+
output.push(line.replace(/:[\s\S]*$/, ":"));
|
|
191
|
+
if (t.startsWith("import ") || t.startsWith("from ")) output.push(line);
|
|
192
|
+
}
|
|
193
|
+
return output.join("\n");
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return lines.slice(0, 30).join("\n") + "\n... (structure extraction not supported for this language)";
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// โโโ REVIEW GRAPH โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
200
|
+
|
|
201
|
+
function buildReviewGraph(files, cwd) {
|
|
202
|
+
const fileFunctions = {};
|
|
203
|
+
const fileImports = {};
|
|
204
|
+
|
|
205
|
+
for (const filePath of files.slice(0, 25)) {
|
|
206
|
+
const lang = getLanguage(filePath);
|
|
207
|
+
if (!["javascript", "typescript", "php", "python"].includes(lang)) continue;
|
|
208
|
+
let content;
|
|
209
|
+
try { content = fs.readFileSync(filePath, "utf8"); } catch { continue; }
|
|
210
|
+
const rel = path.relative(cwd, filePath);
|
|
211
|
+
const nodeId = rel.replace(/[^a-zA-Z0-9]/g, "_");
|
|
212
|
+
fileFunctions[rel] = { nodeId, functions: [] };
|
|
213
|
+
fileImports[rel] = [];
|
|
214
|
+
for (const line of content.split("\n")) {
|
|
215
|
+
const t = line.trim();
|
|
216
|
+
const fnMatch = t.match(/^(export\s+)?(async\s+)?function\s+(\w+)/) ||
|
|
217
|
+
t.match(/^(public|protected|private|static)?\s*(function\s+(\w+))/) ||
|
|
218
|
+
t.match(/^def\s+(\w+)/);
|
|
219
|
+
if (fnMatch) {
|
|
220
|
+
const name = fnMatch[3] || fnMatch[2] || fnMatch[1];
|
|
221
|
+
if (name && name.length > 2 && !["if","for","while","switch"].includes(name))
|
|
222
|
+
fileFunctions[rel].functions.push(name);
|
|
223
|
+
}
|
|
224
|
+
const imp = t.match(/require\(['"]([^'"]+)['"]\)/) ||
|
|
225
|
+
t.match(/^import\s+.*from\s+['"]([^'"]+)['"]/) ||
|
|
226
|
+
t.match(/^use\s+(App\\[^;]+)/);
|
|
227
|
+
if (imp) fileImports[rel].push(imp[1]);
|
|
228
|
+
}
|
|
229
|
+
fileFunctions[rel].functions = [...new Set(fileFunctions[rel].functions)].slice(0, 5);
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const fileList = Object.keys(fileFunctions);
|
|
233
|
+
if (fileList.length === 0) return null;
|
|
169
234
|
|
|
235
|
+
let mermaid = "```mermaid\nflowchart TD\n %% Code Review Graph โ generated by repomeld\n\n";
|
|
236
|
+
for (const rel of fileList) {
|
|
237
|
+
const { nodeId, functions } = fileFunctions[rel];
|
|
238
|
+
const label = path.basename(rel);
|
|
239
|
+
const fnList = functions.length ? `\\n[${functions.join(", ")}]` : "";
|
|
240
|
+
mermaid += ` ${nodeId}["๐ ${label}${fnList}"]\n`;
|
|
241
|
+
}
|
|
242
|
+
mermaid += "\n";
|
|
243
|
+
let edgeCount = 0;
|
|
244
|
+
for (const rel of fileList) {
|
|
245
|
+
for (const imp of fileImports[rel]) {
|
|
246
|
+
const matched = fileList.find(f => {
|
|
247
|
+
const base = path.basename(f, path.extname(f));
|
|
248
|
+
return imp.endsWith(base) || imp.includes(base);
|
|
249
|
+
});
|
|
250
|
+
if (matched && matched !== rel && edgeCount < 40) {
|
|
251
|
+
mermaid += ` ${fileFunctions[rel].nodeId} --> ${fileFunctions[matched].nodeId}\n`;
|
|
252
|
+
edgeCount++;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return mermaid + "```\n";
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// โโโ INTERACTIVE WIZARD โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
260
|
+
|
|
261
|
+
function ask(rl, question) {
|
|
262
|
+
return new Promise((resolve) => rl.question(question, resolve));
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
async function runWizard() {
|
|
266
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
267
|
+
|
|
268
|
+
console.log(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
|
|
269
|
+
console.log(" โ Interactive setup โ answer 7 quick questions โ");
|
|
270
|
+
console.log(" โ Press Enter to accept the [default] value โ");
|
|
271
|
+
console.log(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n");
|
|
272
|
+
|
|
273
|
+
const opts = {};
|
|
274
|
+
|
|
275
|
+
// 1 โ Output file
|
|
276
|
+
const outRaw = await ask(rl, " ๐ 1/7 Output filename? [repomeld_output.txt]\n > ");
|
|
277
|
+
opts.output = outRaw.trim() || "repomeld_output.txt";
|
|
278
|
+
|
|
279
|
+
// 2 โ Style
|
|
280
|
+
console.log("\n ๐จ 2/7 Header style?");
|
|
281
|
+
console.log(" 1) banner โ clear dividers with file info (default)");
|
|
282
|
+
console.log(" 2) markdown โ great for pasting into AI prompts");
|
|
283
|
+
console.log(" 3) minimal โ filename only, no extra formatting");
|
|
284
|
+
const styleRaw = await ask(rl, " > ");
|
|
285
|
+
opts.style = ({ "2": "markdown", "3": "minimal" })[styleRaw.trim()] || "banner";
|
|
286
|
+
|
|
287
|
+
// 3 โ Extensions
|
|
288
|
+
console.log("\n ๐ 3/7 Filter to specific file extensions?");
|
|
289
|
+
console.log(" e.g. js ts php py (leave blank = include ALL files)");
|
|
290
|
+
const extRaw = await ask(rl, " > ");
|
|
291
|
+
opts.ext = extRaw.trim() ? extRaw.trim().split(/\s+/) : [];
|
|
292
|
+
|
|
293
|
+
// 4 โ Structure only
|
|
294
|
+
console.log("\n ๐๏ธ 4/7 Enable structure-only mode? (y/n) [n]");
|
|
295
|
+
console.log(" Strips function bodies โ keeps class names + signatures only.");
|
|
296
|
+
console.log(" Cuts a 400-line file to ~30 lines. Perfect for AI architecture review.");
|
|
297
|
+
const structRaw = await ask(rl, " > ");
|
|
298
|
+
opts.structureOnly = structRaw.trim().toLowerCase() === "y";
|
|
299
|
+
|
|
300
|
+
// 5 โ Review graph
|
|
301
|
+
console.log("\n ๐ 5/7 Generate a code review graph? (y/n) [n]");
|
|
302
|
+
console.log(" Adds a Mermaid call graph at the top of the output.");
|
|
303
|
+
console.log(" AI models read Mermaid natively โ shows which files connect to which.");
|
|
304
|
+
const graphRaw = await ask(rl, " > ");
|
|
305
|
+
opts.reviewGraph = graphRaw.trim().toLowerCase() === "y";
|
|
306
|
+
|
|
307
|
+
// 6 โ Table of contents
|
|
308
|
+
console.log("\n ๐ 6/7 Include a table of contents? (y/n) [y]");
|
|
309
|
+
console.log(" Lists every included file at the top โ useful for large repos.");
|
|
310
|
+
const tocRaw = await ask(rl, " > ");
|
|
311
|
+
opts.noToc = tocRaw.trim().toLowerCase() === "n";
|
|
312
|
+
|
|
313
|
+
// 7 โ Dry run
|
|
314
|
+
console.log("\n ๐งช 7/7 Dry run only? (y/n) [n]");
|
|
315
|
+
console.log(" Preview which files would be included without writing anything.");
|
|
316
|
+
const dryRaw = await ask(rl, " > ");
|
|
317
|
+
opts.dryRun = dryRaw.trim().toLowerCase() === "y";
|
|
318
|
+
|
|
319
|
+
rl.close();
|
|
320
|
+
|
|
321
|
+
// Summary
|
|
322
|
+
console.log("\n โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ");
|
|
323
|
+
console.log(" โ Your configuration โ");
|
|
324
|
+
console.log(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค");
|
|
325
|
+
console.log(` โ Output : ${opts.output.padEnd(29)}โ`);
|
|
326
|
+
console.log(` โ Style : ${opts.style.padEnd(29)}โ`);
|
|
327
|
+
console.log(` โ Extensions : ${(opts.ext.length ? opts.ext.join(", ") : "all").padEnd(29)}โ`);
|
|
328
|
+
console.log(` โ Structure only : ${(opts.structureOnly ? "yes โ
" : "no").padEnd(29)}โ`);
|
|
329
|
+
console.log(` โ Review graph : ${(opts.reviewGraph ? "yes โ
" : "no").padEnd(29)}โ`);
|
|
330
|
+
console.log(` โ Table of cont. : ${(opts.noToc ? "no" : "yes").padEnd(29)}โ`);
|
|
331
|
+
console.log(` โ Dry run : ${(opts.dryRun ? "yes ๐งช" : "no").padEnd(29)}โ`);
|
|
332
|
+
console.log(" โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ\n");
|
|
333
|
+
|
|
334
|
+
return opts;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// โโโ CORE โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
338
|
+
|
|
339
|
+
function repomeld(options) {
|
|
170
340
|
const cwd = process.cwd();
|
|
171
341
|
const outputFile = path.resolve(cwd, options.output);
|
|
172
342
|
const forceInclude = options.forceInclude || [];
|
|
173
343
|
const rawIgnore = [...DEFAULT_IGNORE, ...IGNORE_FROM_CONFIG, ...(options.ignore || [])];
|
|
174
344
|
const ignoreList = forceInclude.length
|
|
175
|
-
? rawIgnore.filter(
|
|
345
|
+
? rawIgnore.filter(ig => !forceInclude.some(fi => ig.includes(fi) || fi.includes(ig)))
|
|
176
346
|
: rawIgnore;
|
|
177
|
-
const filterExts
|
|
347
|
+
const filterExts = options.ext || [];
|
|
178
348
|
const maxFileSizeBytes = (parseFloat(options.maxSize) || 500) * 1024;
|
|
179
|
-
const headerStyle
|
|
180
|
-
const showMeta
|
|
181
|
-
const showToc
|
|
182
|
-
const dryRun
|
|
183
|
-
const include
|
|
184
|
-
const exclude
|
|
185
|
-
const linesBefore
|
|
186
|
-
const linesAfter
|
|
187
|
-
|
|
188
|
-
|
|
349
|
+
const headerStyle = options.style || "banner";
|
|
350
|
+
const showMeta = options.noMeta !== true;
|
|
351
|
+
const showToc = options.noToc !== true;
|
|
352
|
+
const dryRun = options.dryRun || false;
|
|
353
|
+
const include = options.include || [];
|
|
354
|
+
const exclude = options.exclude || [];
|
|
355
|
+
const linesBefore = parseInt(options.linesBefore) || 0;
|
|
356
|
+
const linesAfter = parseInt(options.linesAfter) || 0;
|
|
357
|
+
const structureOnly = options.structureOnly || false;
|
|
358
|
+
const reviewGraph = options.reviewGraph || false;
|
|
359
|
+
|
|
360
|
+
console.log(` ๐ Source : ${cwd}`);
|
|
189
361
|
console.log(` ๐ Output : ${path.relative(cwd, outputFile)}`);
|
|
190
362
|
console.log(` ๐จ Style : ${headerStyle}`);
|
|
191
363
|
if (filterExts.length) console.log(` ๐ Filter : .${filterExts.join(", .")}`);
|
|
192
|
-
if (
|
|
193
|
-
if (
|
|
364
|
+
if (structureOnly) console.log(` ๐๏ธ Mode : structure only`);
|
|
365
|
+
if (reviewGraph) console.log(` ๐ Graph : review graph enabled`);
|
|
366
|
+
if (dryRun) console.log(` ๐งช Dry run : no file will be written`);
|
|
194
367
|
console.log();
|
|
195
368
|
|
|
196
369
|
let allFiles = getAllFiles(cwd, ignoreList);
|
|
370
|
+
if (filterExts.length) allFiles = allFiles.filter(f => matchesExtensions(f, filterExts));
|
|
371
|
+
if (include.length) allFiles = allFiles.filter(f => matchesPattern(f, include));
|
|
372
|
+
if (exclude.length) allFiles = allFiles.filter(f => !matchesPattern(f, exclude));
|
|
373
|
+
allFiles = allFiles.filter(f => path.resolve(f) !== outputFile);
|
|
197
374
|
|
|
198
|
-
|
|
199
|
-
if (filterExts.length) {
|
|
200
|
-
allFiles = allFiles.filter((f) => matchesExtensions(f, filterExts));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Include pattern filter
|
|
204
|
-
if (include.length) {
|
|
205
|
-
allFiles = allFiles.filter((f) => matchesPattern(f, include));
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Exclude pattern filter
|
|
209
|
-
if (exclude.length) {
|
|
210
|
-
allFiles = allFiles.filter((f) => !matchesPattern(f, exclude));
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Remove output file from list
|
|
214
|
-
allFiles = allFiles.filter((f) => path.resolve(f) !== outputFile);
|
|
215
|
-
|
|
216
|
-
if (allFiles.length === 0) {
|
|
217
|
-
console.log(" โ ๏ธ No matching files found.\n");
|
|
218
|
-
return;
|
|
219
|
-
}
|
|
375
|
+
if (allFiles.length === 0) { console.log(" โ ๏ธ No matching files found.\n"); return; }
|
|
220
376
|
|
|
221
377
|
let combinedContent = "";
|
|
222
|
-
let skipped = 0;
|
|
223
|
-
let included = 0;
|
|
224
|
-
let totalLines = 0;
|
|
378
|
+
let skipped = 0, included = 0, totalLines = 0;
|
|
225
379
|
const includedFiles = [];
|
|
226
380
|
|
|
227
381
|
for (const filePath of allFiles) {
|
|
228
382
|
const relativePath = path.relative(cwd, filePath);
|
|
229
|
-
|
|
230
|
-
if (isBinaryFile(filePath)) {
|
|
231
|
-
console.log(` โญ Binary : ${relativePath}`);
|
|
232
|
-
skipped++;
|
|
233
|
-
continue;
|
|
234
|
-
}
|
|
235
|
-
|
|
383
|
+
if (isBinaryFile(filePath)) { console.log(` โญ Binary : ${relativePath}`); skipped++; continue; }
|
|
236
384
|
const stat = fs.statSync(filePath);
|
|
237
|
-
if (stat.size > maxFileSizeBytes) {
|
|
238
|
-
console.log(` โญ Too large: ${relativePath} (${formatSize(stat.size)})`);
|
|
239
|
-
skipped++;
|
|
240
|
-
continue;
|
|
241
|
-
}
|
|
242
|
-
|
|
385
|
+
if (stat.size > maxFileSizeBytes) { console.log(` โญ Too large: ${relativePath} (${formatSize(stat.size)})`); skipped++; continue; }
|
|
243
386
|
try {
|
|
244
387
|
let content = fs.readFileSync(filePath, "utf8");
|
|
245
|
-
|
|
246
|
-
// Trim leading/trailing blank lines if requested
|
|
247
|
-
if (options.trim) {
|
|
248
|
-
content = content.trim();
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// Slice specific lines
|
|
388
|
+
if (options.trim) content = content.trim();
|
|
252
389
|
if (linesBefore > 0 || linesAfter > 0) {
|
|
253
390
|
const lines = content.split("\n");
|
|
254
|
-
|
|
255
|
-
const end = linesAfter > 0 ? lines.length - linesAfter : lines.length;
|
|
256
|
-
content = lines.slice(start, end).join("\n");
|
|
391
|
+
content = lines.slice(linesBefore, linesAfter > 0 ? lines.length - linesAfter : lines.length).join("\n");
|
|
257
392
|
}
|
|
258
|
-
|
|
393
|
+
if (structureOnly) content = extractStructure(content, filePath);
|
|
259
394
|
const lineCount = content.split("\n").length;
|
|
260
395
|
totalLines += lineCount;
|
|
261
396
|
includedFiles.push(filePath);
|
|
262
|
-
|
|
263
397
|
combinedContent += buildHeader(headerStyle, relativePath, filePath, lineCount, showMeta);
|
|
264
398
|
combinedContent += content;
|
|
265
399
|
combinedContent += buildFooter(headerStyle);
|
|
266
|
-
|
|
267
400
|
console.log(` โ
${relativePath}`);
|
|
268
401
|
included++;
|
|
269
402
|
} catch (err) {
|
|
@@ -272,29 +405,30 @@ function repomeld(options) {
|
|
|
272
405
|
}
|
|
273
406
|
}
|
|
274
407
|
|
|
275
|
-
// Build final output
|
|
276
408
|
let finalOutput = "";
|
|
277
|
-
|
|
278
|
-
// Top-level comment
|
|
279
|
-
const timestamp = new Date().toISOString();
|
|
280
409
|
finalOutput += `# Generated by repomeld v${VERSION}\n`;
|
|
281
|
-
finalOutput += `# Date : ${
|
|
410
|
+
finalOutput += `# Date : ${new Date().toISOString()}\n`;
|
|
282
411
|
finalOutput += `# Source : ${cwd}\n`;
|
|
283
412
|
finalOutput += `# Files : ${included}\n`;
|
|
284
|
-
finalOutput += `# Lines : ${totalLines}\n
|
|
285
|
-
|
|
286
|
-
if (
|
|
287
|
-
|
|
413
|
+
finalOutput += `# Lines : ${totalLines}\n`;
|
|
414
|
+
if (structureOnly) finalOutput += `# Mode : structure-only (signatures)\n`;
|
|
415
|
+
if (reviewGraph) finalOutput += `# Graph : review-graph enabled\n`;
|
|
416
|
+
finalOutput += `# Author : susheelhbti@gmail.com (open to work)\n\n`;
|
|
417
|
+
|
|
418
|
+
if (reviewGraph) {
|
|
419
|
+
const graph = buildReviewGraph(includedFiles, cwd);
|
|
420
|
+
if (graph) {
|
|
421
|
+
finalOutput += "โ".repeat(60) + "\n CODE REVIEW GRAPH\n" + "โ".repeat(60) + "\n\n";
|
|
422
|
+
finalOutput += graph + "\n";
|
|
423
|
+
}
|
|
288
424
|
}
|
|
289
425
|
|
|
426
|
+
if (showToc) finalOutput += buildTableOfContents(includedFiles, cwd);
|
|
290
427
|
finalOutput += combinedContent;
|
|
291
428
|
|
|
292
|
-
if (!dryRun)
|
|
293
|
-
fs.writeFileSync(outputFile, finalOutput, "utf8");
|
|
294
|
-
}
|
|
429
|
+
if (!dryRun) fs.writeFileSync(outputFile, finalOutput, "utf8");
|
|
295
430
|
|
|
296
431
|
const outputSize = formatSize(Buffer.byteLength(finalOutput, "utf8"));
|
|
297
|
-
|
|
298
432
|
console.log(`
|
|
299
433
|
โจ repomeld complete!
|
|
300
434
|
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
@@ -304,39 +438,47 @@ function repomeld(options) {
|
|
|
304
438
|
๐พ Size : ${outputSize}
|
|
305
439
|
๐ Output : ${options.output}${dryRun ? " (dry run โ not written)" : ""}
|
|
306
440
|
`);
|
|
441
|
+
console.log(AUTHOR_NOTE + "\n");
|
|
307
442
|
}
|
|
308
443
|
|
|
309
|
-
// โโโ CLI
|
|
444
|
+
// โโโ CLI โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
310
445
|
|
|
311
446
|
program
|
|
312
447
|
.name("repomeld")
|
|
313
448
|
.description("Meld your entire repo into a single file โ perfect for AI context, code reviews & sharing")
|
|
314
449
|
.version(VERSION)
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
.option("
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
.option("-
|
|
321
|
-
.option("--
|
|
322
|
-
.option("--
|
|
323
|
-
.option("-
|
|
324
|
-
.option("--
|
|
325
|
-
.option("--
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
.option("-
|
|
329
|
-
.option("--
|
|
330
|
-
.option("--
|
|
331
|
-
.option("--
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
450
|
+
.option("-o, --output <filename>", "Output file name", "repomeld_output.txt")
|
|
451
|
+
.option("-e, --ext <exts...>", "Only include specific extensions")
|
|
452
|
+
.option("--include <patterns...>", "Only include files matching patterns")
|
|
453
|
+
.option("--exclude <patterns...>", "Exclude files matching patterns")
|
|
454
|
+
.option("-i, --ignore <names...>", "Extra folders/files to ignore")
|
|
455
|
+
.option("--force-include <names...>", "Force-include files ignored by default")
|
|
456
|
+
.option("--max-size <kb>", "Skip files larger than N KB", "500")
|
|
457
|
+
.option("-s, --style <style>", "Header style: banner | markdown | minimal","banner")
|
|
458
|
+
.option("--no-toc", "Disable table of contents")
|
|
459
|
+
.option("--no-meta", "Hide file metadata")
|
|
460
|
+
.option("--trim", "Trim leading/trailing whitespace per file")
|
|
461
|
+
.option("--lines-before <n>", "Skip first N lines of each file")
|
|
462
|
+
.option("--lines-after <n>", "Skip last N lines of each file")
|
|
463
|
+
.option("--dry-run", "Preview without writing output")
|
|
464
|
+
.option("--structure-only", "Extract signatures only โ no body code")
|
|
465
|
+
.option("--review-graph", "Prepend a Mermaid call graph to output")
|
|
466
|
+
.option("--wizard", "Interactive step-by-step setup (auto when no flags given)")
|
|
467
|
+
.action(async (options) => {
|
|
468
|
+
printBanner();
|
|
469
|
+
|
|
470
|
+
// Auto-wizard when user runs plain `repomeld` with no flags
|
|
471
|
+
const hasFlags = options.ext || options.include || options.exclude ||
|
|
472
|
+
options.structureOnly || options.reviewGraph ||
|
|
473
|
+
options.style !== "banner" || options.output !== "repomeld_output.txt" ||
|
|
474
|
+
options.dryRun;
|
|
475
|
+
|
|
476
|
+
if (options.wizard || !hasFlags) {
|
|
477
|
+
const wizardOpts = await runWizard();
|
|
478
|
+
repomeld({ ...options, ...wizardOpts });
|
|
479
|
+
} else {
|
|
480
|
+
repomeld(options);
|
|
481
|
+
}
|
|
340
482
|
});
|
|
341
483
|
|
|
342
484
|
program.parse(process.argv);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repomeld",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.2",
|
|
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": {
|
|
@@ -23,8 +23,8 @@
|
|
|
23
23
|
"author": "",
|
|
24
24
|
"license": "MIT",
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"commander": "^11.0.0"
|
|
27
|
-
|
|
26
|
+
"commander": "^11.0.0"
|
|
27
|
+
|
|
28
28
|
},
|
|
29
29
|
"engines": {
|
|
30
30
|
"node": ">=14.0.0"
|