make-folder-txt 2.0.0 → 2.1.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 +100 -6
- package/bin/make-folder-txt.js +545 -11
- package/completion/install-powershell-completion.ps1 +75 -0
- package/completion/make-folder-txt-completion.bash +19 -1
- package/completion/make-folder-txt-completion.ps1 +95 -0
- package/completion/make-folder-txt-completion.zsh +4 -0
- package/make-folder-txt.txt +52 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
|
|
12
12
|
Perfect for sharing your codebase with **AI tools**, **teammates**, or **code reviewers** — without zipping files or giving repo access.
|
|
13
13
|
|
|
14
|
-
[Installation](#-installation) · [Usage](#-usage) · [Help](#-get-help) · [Copy to Clipboard](#-copy-to-clipboard) · [Force Include Everything](#-force-include-everything) · [Shell Autocompletion](#-shell-autocompletion) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
|
|
14
|
+
[Installation](#-installation) · [Usage](#-usage) · [Help](#-get-help) · [Copy to Clipboard](#-copy-to-clipboard) · [Force Include Everything](#-force-include-everything) · [Shell Autocompletion](#-shell-autocompletion) · [File Size Control](#-file-size-control) · [Output Splitting](#-output-splitting) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
|
|
15
15
|
|
|
16
16
|
</div>
|
|
17
17
|
|
|
@@ -23,7 +23,9 @@ Ever needed to share your entire project with ChatGPT, Claude, or a teammate —
|
|
|
23
23
|
|
|
24
24
|
- ✅ Run it from any project directory — no arguments needed
|
|
25
25
|
- ✅ Built-in help system with `--help` flag
|
|
26
|
-
- ✅
|
|
26
|
+
- ✅ **Built-in shell autocompletion** (installs automatically)
|
|
27
|
+
- ✅ **File size control** with `--skip-large` and `--no-skip`
|
|
28
|
+
- ✅ **Output splitting** by folders, files, or size
|
|
27
29
|
- ✅ Copy to clipboard with `--copy` flag
|
|
28
30
|
- ✅ Force include everything with `--force` flag
|
|
29
31
|
- ✅ Generates a clean folder tree + every file's content
|
|
@@ -89,17 +91,24 @@ The `--force` flag overrides all ignore patterns and includes:
|
|
|
89
91
|
|
|
90
92
|
Use this when you need a complete, unfiltered dump of your entire project.
|
|
91
93
|
|
|
92
|
-
### ⚡ Shell Autocompletion
|
|
94
|
+
### ⚡ Shell Autocompletion (Built-in)
|
|
93
95
|
|
|
94
96
|
```bash
|
|
95
|
-
make-folder-txt
|
|
97
|
+
make-folder-txt # Autocompletion installs automatically on first run
|
|
96
98
|
```
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
**🎉 No installation required!** The tool automatically installs shell autocompletion the first time you run it.
|
|
101
|
+
|
|
102
|
+
**What gets installed automatically:**
|
|
99
103
|
- **Flag completion**: `make-folder-txt --<TAB>` shows all available flags
|
|
100
104
|
- **Folder completion**: `make-folder-txt --ignore-folder <TAB>` shows folders
|
|
101
105
|
- **File completion**: `make-folder-txt --ignore-file <TAB>` shows files
|
|
102
106
|
|
|
107
|
+
**Supported Shells:**
|
|
108
|
+
- **Bash** - Linux/macOS/Windows (WSL)
|
|
109
|
+
- **Zsh** - macOS/Linux
|
|
110
|
+
- **PowerShell** - Windows (7+)
|
|
111
|
+
|
|
103
112
|
**Example usage:**
|
|
104
113
|
```bash
|
|
105
114
|
$ make-folder-txt --ignore-folder b<TAB>
|
|
@@ -109,7 +118,92 @@ $ make-folder-txt --ignore-file p<TAB>
|
|
|
109
118
|
# → completes to "package.json" if package.json exists
|
|
110
119
|
```
|
|
111
120
|
|
|
112
|
-
|
|
121
|
+
**Manual Installation (if needed):**
|
|
122
|
+
```bash
|
|
123
|
+
make-folder-txt --install-completion # Force reinstall completion
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**PowerShell Manual Setup:**
|
|
127
|
+
```powershell
|
|
128
|
+
# If auto-installation doesn't work, load manually:
|
|
129
|
+
. .\completion\make-folder-txt-completion.ps1
|
|
130
|
+
|
|
131
|
+
# Or add to your PowerShell profile for persistence:
|
|
132
|
+
notepad $PROFILE
|
|
133
|
+
# Add: . "C:\path\to\make-folder-txt\completion\make-folder-txt-completion.ps1"
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
The completion works out of the box - just run the tool once and restart your terminal!
|
|
137
|
+
|
|
138
|
+
### 📏 File Size Control
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
make-folder-txt --skip-large 400KB # Skip files larger than 400KB
|
|
142
|
+
make-folder-txt --skip-large 5GB # Skip files larger than 5GB
|
|
143
|
+
make-folder-txt --skip-large 1.5MB # Skip files larger than 1.5MB
|
|
144
|
+
make-folder-txt --no-skip # Include all files regardless of size
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
**Default behavior**: Files larger than 500KB are skipped by default.
|
|
148
|
+
|
|
149
|
+
**Supported size units**:
|
|
150
|
+
- **B** - Bytes
|
|
151
|
+
- **KB** - Kilobytes (1024 bytes)
|
|
152
|
+
- **MB** - Megabytes (1024 KB)
|
|
153
|
+
- **GB** - Gigabytes (1024 MB)
|
|
154
|
+
- **TB** - Terabytes (1024 GB)
|
|
155
|
+
|
|
156
|
+
**Examples:**
|
|
157
|
+
```bash
|
|
158
|
+
# More restrictive - skip anything over 100KB
|
|
159
|
+
make-folder-txt --skip-large 100KB
|
|
160
|
+
|
|
161
|
+
# More permissive - allow files up to 10MB
|
|
162
|
+
make-folder-txt --skip-large 10MB
|
|
163
|
+
|
|
164
|
+
# Include everything - no size limits
|
|
165
|
+
make-folder-txt --no-skip
|
|
166
|
+
|
|
167
|
+
# Combine with other options
|
|
168
|
+
make-folder-txt --skip-large 2MB --ignore-folder node_modules
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
**Size format**: Accepts decimal numbers (e.g., `1.5MB`, `0.5GB`) and various units.
|
|
172
|
+
|
|
173
|
+
### 📂 Output Splitting
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
make-folder-txt --split-method folder # Split by folders
|
|
177
|
+
make-folder-txt --split-method file # Split by files
|
|
178
|
+
make-folder-txt --split-method size --split-size 5MB # Split by file size
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Split Methods:**
|
|
182
|
+
- **`folder`** - Creates separate files for each folder
|
|
183
|
+
- **`file`** - Creates separate files for each individual file
|
|
184
|
+
- **`size`** - Splits output when content exceeds specified size
|
|
185
|
+
|
|
186
|
+
**Examples:**
|
|
187
|
+
```bash
|
|
188
|
+
# Split by folders - creates folder-name.txt for each folder
|
|
189
|
+
make-folder-txt --split-method folder
|
|
190
|
+
|
|
191
|
+
# Split by files - creates filename.txt for each file
|
|
192
|
+
make-folder-txt --split-method file
|
|
193
|
+
|
|
194
|
+
# Split by size - creates part-1.txt, part-2.txt, etc.
|
|
195
|
+
make-folder-txt --split-method size --split-size 5MB
|
|
196
|
+
|
|
197
|
+
# Combine with other options
|
|
198
|
+
make-folder-txt --split-method size --split-size 2MB --ignore-folder node_modules
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Output Files:**
|
|
202
|
+
- **Folder method**: `projectname-foldername.txt`
|
|
203
|
+
- **File method**: `projectname-filename.txt`
|
|
204
|
+
- **Size method**: `projectname-part-1.txt`, `projectname-part-2.txt`, etc.
|
|
205
|
+
|
|
206
|
+
**Note**: Splitting is not compatible with `--copy` flag.
|
|
113
207
|
|
|
114
208
|
Ignore specific folders/files by name:
|
|
115
209
|
|
package/bin/make-folder-txt.js
CHANGED
|
@@ -179,13 +179,37 @@ function collectFiles(
|
|
|
179
179
|
return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
|
|
180
180
|
}
|
|
181
181
|
|
|
182
|
-
function
|
|
182
|
+
function parseFileSize(sizeStr) {
|
|
183
|
+
const units = {
|
|
184
|
+
'B': 1,
|
|
185
|
+
'KB': 1024,
|
|
186
|
+
'MB': 1024 * 1024,
|
|
187
|
+
'GB': 1024 * 1024 * 1024,
|
|
188
|
+
'TB': 1024 * 1024 * 1024 * 1024
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
|
|
192
|
+
if (!match) {
|
|
193
|
+
console.error(`Error: Invalid size format "${sizeStr}". Use format like "500KB", "2MB", "1GB".`);
|
|
194
|
+
process.exit(1);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const value = parseFloat(match[1]);
|
|
198
|
+
const unit = match[2].toUpperCase();
|
|
199
|
+
return Math.floor(value * units[unit]);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function readContent(absPath, force = false, maxFileSize = 500 * 1024) {
|
|
183
203
|
const ext = path.extname(absPath).toLowerCase();
|
|
184
204
|
if (!force && BINARY_EXTS.has(ext)) return "[binary / skipped]";
|
|
185
205
|
try {
|
|
186
206
|
const stat = fs.statSync(absPath);
|
|
187
|
-
if (!force && stat.size >
|
|
188
|
-
|
|
207
|
+
if (!force && stat.size > maxFileSize) {
|
|
208
|
+
const sizeStr = stat.size < 1024 ? `${stat.size} B` :
|
|
209
|
+
stat.size < 1024 * 1024 ? `${(stat.size / 1024).toFixed(1)} KB` :
|
|
210
|
+
stat.size < 1024 * 1024 * 1024 ? `${(stat.size / (1024 * 1024)).toFixed(1)} MB` :
|
|
211
|
+
`${(stat.size / (1024 * 1024 * 1024)).toFixed(1)} GB`;
|
|
212
|
+
return `[file too large: ${sizeStr} – skipped]`;
|
|
189
213
|
}
|
|
190
214
|
return fs.readFileSync(absPath, "utf8");
|
|
191
215
|
} catch (err) {
|
|
@@ -193,10 +217,361 @@ function readContent(absPath, force = false) {
|
|
|
193
217
|
}
|
|
194
218
|
}
|
|
195
219
|
|
|
220
|
+
function splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag) {
|
|
221
|
+
const folders = new Map();
|
|
222
|
+
|
|
223
|
+
// Group files by folder
|
|
224
|
+
filePaths.forEach(({ abs, rel }) => {
|
|
225
|
+
const folderPath = path.dirname(rel);
|
|
226
|
+
const folderKey = folderPath === '/' ? rootName : folderPath.slice(1);
|
|
227
|
+
|
|
228
|
+
if (!folders.has(folderKey)) {
|
|
229
|
+
folders.set(folderKey, []);
|
|
230
|
+
}
|
|
231
|
+
folders.get(folderKey).push({ abs, rel });
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const results = [];
|
|
235
|
+
|
|
236
|
+
folders.forEach((files, folderName) => {
|
|
237
|
+
const out = [];
|
|
238
|
+
const divider = "=".repeat(80);
|
|
239
|
+
const subDivider = "-".repeat(80);
|
|
240
|
+
|
|
241
|
+
out.push(divider);
|
|
242
|
+
out.push(`START OF FOLDER: ${folderName}`);
|
|
243
|
+
out.push(divider);
|
|
244
|
+
out.push("");
|
|
245
|
+
|
|
246
|
+
// Add folder structure (only this folder's structure)
|
|
247
|
+
const folderTreeLines = treeLines.filter(line =>
|
|
248
|
+
line.includes(folderName + '/') || line === `${rootName}/`
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
out.push(divider);
|
|
252
|
+
out.push("PROJECT STRUCTURE");
|
|
253
|
+
out.push(divider);
|
|
254
|
+
out.push(`Root: ${folderPath}\n`);
|
|
255
|
+
out.push(`${rootName}/`);
|
|
256
|
+
folderTreeLines.forEach(l => out.push(l));
|
|
257
|
+
out.push("");
|
|
258
|
+
out.push(`Total files in this folder: ${files.length}`);
|
|
259
|
+
out.push("");
|
|
260
|
+
|
|
261
|
+
out.push(divider);
|
|
262
|
+
out.push("FILE CONTENTS");
|
|
263
|
+
out.push(divider);
|
|
264
|
+
|
|
265
|
+
files.forEach(({ abs, rel }) => {
|
|
266
|
+
out.push("");
|
|
267
|
+
out.push(subDivider);
|
|
268
|
+
out.push(`FILE: ${rel}`);
|
|
269
|
+
out.push(subDivider);
|
|
270
|
+
out.push(readContent(abs, forceFlag, effectiveMaxSize));
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
out.push("");
|
|
274
|
+
out.push(divider);
|
|
275
|
+
out.push(`END OF FOLDER: ${folderName}`);
|
|
276
|
+
out.push(divider);
|
|
277
|
+
|
|
278
|
+
const fileName = `${rootName}-${folderName.replace(/[\/\\]/g, '-')}.txt`;
|
|
279
|
+
const filePath = path.join(process.cwd(), fileName);
|
|
280
|
+
|
|
281
|
+
fs.writeFileSync(filePath, out.join("\n"), "utf8");
|
|
282
|
+
const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
|
|
283
|
+
|
|
284
|
+
results.push({
|
|
285
|
+
file: filePath,
|
|
286
|
+
size: sizeKB,
|
|
287
|
+
files: files.length,
|
|
288
|
+
folder: folderName
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
return results;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag) {
|
|
296
|
+
const results = [];
|
|
297
|
+
|
|
298
|
+
filePaths.forEach(({ abs, rel }) => {
|
|
299
|
+
const out = [];
|
|
300
|
+
const divider = "=".repeat(80);
|
|
301
|
+
const subDivider = "-".repeat(80);
|
|
302
|
+
const fileName = path.basename(rel, path.extname(rel));
|
|
303
|
+
|
|
304
|
+
out.push(divider);
|
|
305
|
+
out.push(`FILE: ${rel}`);
|
|
306
|
+
out.push(divider);
|
|
307
|
+
out.push("");
|
|
308
|
+
|
|
309
|
+
out.push(divider);
|
|
310
|
+
out.push("FILE CONTENTS");
|
|
311
|
+
out.push(divider);
|
|
312
|
+
out.push(readContent(abs, forceFlag, effectiveMaxSize));
|
|
313
|
+
|
|
314
|
+
out.push("");
|
|
315
|
+
out.push(divider);
|
|
316
|
+
out.push(`END OF FILE: ${rel}`);
|
|
317
|
+
out.push(divider);
|
|
318
|
+
|
|
319
|
+
const outputFileName = `${rootName}-${fileName}.txt`;
|
|
320
|
+
const filePath = path.join(process.cwd(), outputFileName);
|
|
321
|
+
|
|
322
|
+
fs.writeFileSync(filePath, out.join("\n"), "utf8");
|
|
323
|
+
const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
|
|
324
|
+
|
|
325
|
+
results.push({
|
|
326
|
+
file: filePath,
|
|
327
|
+
size: sizeKB,
|
|
328
|
+
files: 1,
|
|
329
|
+
fileName: fileName
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
return results;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
function splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag) {
|
|
337
|
+
const results = [];
|
|
338
|
+
let currentPart = 1;
|
|
339
|
+
let currentSize = 0;
|
|
340
|
+
let currentFiles = [];
|
|
341
|
+
|
|
342
|
+
const divider = "=".repeat(80);
|
|
343
|
+
const subDivider = "-".repeat(80);
|
|
344
|
+
|
|
345
|
+
// Start with header
|
|
346
|
+
let out = [];
|
|
347
|
+
out.push(divider);
|
|
348
|
+
out.push(`START OF FOLDER: ${rootName} (Part ${currentPart})`);
|
|
349
|
+
out.push(divider);
|
|
350
|
+
out.push("");
|
|
351
|
+
|
|
352
|
+
out.push(divider);
|
|
353
|
+
out.push("PROJECT STRUCTURE");
|
|
354
|
+
out.push(divider);
|
|
355
|
+
out.push(`Root: ${folderPath}\n`);
|
|
356
|
+
out.push(`${rootName}/`);
|
|
357
|
+
treeLines.forEach(l => out.push(l));
|
|
358
|
+
out.push("");
|
|
359
|
+
out.push(`Total files: ${filePaths.length}`);
|
|
360
|
+
out.push("");
|
|
361
|
+
|
|
362
|
+
out.push(divider);
|
|
363
|
+
out.push("FILE CONTENTS");
|
|
364
|
+
out.push(divider);
|
|
365
|
+
|
|
366
|
+
filePaths.forEach(({ abs, rel }) => {
|
|
367
|
+
const content = readContent(abs, forceFlag, effectiveMaxSize);
|
|
368
|
+
const fileContent = [
|
|
369
|
+
"",
|
|
370
|
+
subDivider,
|
|
371
|
+
`FILE: ${rel}`,
|
|
372
|
+
subDivider,
|
|
373
|
+
content
|
|
374
|
+
];
|
|
375
|
+
|
|
376
|
+
const contentSize = fileContent.join("\n").length;
|
|
377
|
+
|
|
378
|
+
// Check if adding this file would exceed the split size
|
|
379
|
+
if (currentSize + contentSize > splitSize && currentFiles.length > 0) {
|
|
380
|
+
// Finish current part
|
|
381
|
+
out.push("");
|
|
382
|
+
out.push(divider);
|
|
383
|
+
out.push(`END OF FOLDER: ${rootName} (Part ${currentPart})`);
|
|
384
|
+
out.push(divider);
|
|
385
|
+
|
|
386
|
+
// Write current part
|
|
387
|
+
const fileName = `${rootName}-part-${currentPart}.txt`;
|
|
388
|
+
const filePath = path.join(process.cwd(), fileName);
|
|
389
|
+
fs.writeFileSync(filePath, out.join("\n"), "utf8");
|
|
390
|
+
const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
|
|
391
|
+
|
|
392
|
+
results.push({
|
|
393
|
+
file: filePath,
|
|
394
|
+
size: sizeKB,
|
|
395
|
+
files: currentFiles.length,
|
|
396
|
+
part: currentPart
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
// Start new part
|
|
400
|
+
currentPart++;
|
|
401
|
+
currentSize = 0;
|
|
402
|
+
currentFiles = [];
|
|
403
|
+
|
|
404
|
+
out = [];
|
|
405
|
+
out.push(divider);
|
|
406
|
+
out.push(`START OF FOLDER: ${rootName} (Part ${currentPart})`);
|
|
407
|
+
out.push(divider);
|
|
408
|
+
out.push("");
|
|
409
|
+
|
|
410
|
+
out.push(divider);
|
|
411
|
+
out.push("PROJECT STRUCTURE");
|
|
412
|
+
out.push(divider);
|
|
413
|
+
out.push(`Root: ${folderPath}\n`);
|
|
414
|
+
out.push(`${rootName}/`);
|
|
415
|
+
treeLines.forEach(l => out.push(l));
|
|
416
|
+
out.push("");
|
|
417
|
+
out.push(`Total files: ${filePaths.length}`);
|
|
418
|
+
out.push("");
|
|
419
|
+
|
|
420
|
+
out.push(divider);
|
|
421
|
+
out.push("FILE CONTENTS");
|
|
422
|
+
out.push(divider);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
// Add file to current part
|
|
426
|
+
out.push(...fileContent);
|
|
427
|
+
currentSize += contentSize;
|
|
428
|
+
currentFiles.push(rel);
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// Write final part
|
|
432
|
+
out.push("");
|
|
433
|
+
out.push(divider);
|
|
434
|
+
out.push(`END OF FOLDER: ${rootName} (Part ${currentPart})`);
|
|
435
|
+
out.push(divider);
|
|
436
|
+
|
|
437
|
+
const fileName = `${rootName}-part-${currentPart}.txt`;
|
|
438
|
+
const filePath = path.join(process.cwd(), fileName);
|
|
439
|
+
fs.writeFileSync(filePath, out.join("\n"), "utf8");
|
|
440
|
+
const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
|
|
441
|
+
|
|
442
|
+
results.push({
|
|
443
|
+
file: filePath,
|
|
444
|
+
size: sizeKB,
|
|
445
|
+
files: currentFiles.length,
|
|
446
|
+
part: currentPart
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
return results;
|
|
450
|
+
}
|
|
451
|
+
|
|
196
452
|
// ── main ──────────────────────────────────────────────────────────────────────
|
|
197
453
|
|
|
198
454
|
const args = process.argv.slice(2);
|
|
199
455
|
|
|
456
|
+
// Check if completion is already installed, install if not
|
|
457
|
+
function checkAndInstallCompletion() {
|
|
458
|
+
const { execSync } = require('child_process');
|
|
459
|
+
const path = require('path');
|
|
460
|
+
const os = require('os');
|
|
461
|
+
const fs = require('fs');
|
|
462
|
+
|
|
463
|
+
try {
|
|
464
|
+
const homeDir = os.homedir();
|
|
465
|
+
const shell = process.env.SHELL || '';
|
|
466
|
+
const platform = process.platform;
|
|
467
|
+
let completionInstalled = false;
|
|
468
|
+
|
|
469
|
+
if (platform === 'win32') {
|
|
470
|
+
// Check PowerShell completion - try multiple profile locations
|
|
471
|
+
const profilePaths = [
|
|
472
|
+
path.join(homeDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
|
|
473
|
+
path.join(homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
|
|
474
|
+
path.join(homeDir, '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1')
|
|
475
|
+
];
|
|
476
|
+
|
|
477
|
+
for (const profilePath of profilePaths) {
|
|
478
|
+
if (fs.existsSync(profilePath)) {
|
|
479
|
+
const profileContent = fs.readFileSync(profilePath, 'utf8');
|
|
480
|
+
if (profileContent.includes('make-folder-txt-completion')) {
|
|
481
|
+
completionInstalled = true;
|
|
482
|
+
break;
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
} else if (shell.includes('zsh')) {
|
|
487
|
+
// Check zsh completion
|
|
488
|
+
const zshrc = path.join(homeDir, '.zshrc');
|
|
489
|
+
if (fs.existsSync(zshrc)) {
|
|
490
|
+
const zshrcContent = fs.readFileSync(zshrc, 'utf8');
|
|
491
|
+
if (zshrcContent.includes('make-folder-txt')) {
|
|
492
|
+
completionInstalled = true;
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
// Check bash completion
|
|
497
|
+
const bashrc = path.join(homeDir, '.bashrc');
|
|
498
|
+
if (fs.existsSync(bashrc)) {
|
|
499
|
+
const bashrcContent = fs.readFileSync(bashrc, 'utf8');
|
|
500
|
+
if (bashrcContent.includes('make-folder-txt-completion')) {
|
|
501
|
+
completionInstalled = true;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// If completion is not installed, install it automatically
|
|
507
|
+
if (!completionInstalled) {
|
|
508
|
+
console.log('🔧 Installing shell autocompletion for make-folder-txt...');
|
|
509
|
+
|
|
510
|
+
if (platform === 'win32') {
|
|
511
|
+
// Windows PowerShell
|
|
512
|
+
try {
|
|
513
|
+
execSync('powershell -Command "Get-Host"', { stdio: 'ignore' });
|
|
514
|
+
const installScript = path.join(__dirname, '..', 'completion', 'install-powershell-completion.ps1');
|
|
515
|
+
execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'ignore' });
|
|
516
|
+
console.log('✅ PowerShell completion installed!');
|
|
517
|
+
} catch (err) {
|
|
518
|
+
// Silent fail for PowerShell
|
|
519
|
+
}
|
|
520
|
+
} else if (shell.includes('zsh')) {
|
|
521
|
+
// zsh
|
|
522
|
+
try {
|
|
523
|
+
const zshrc = path.join(homeDir, '.zshrc');
|
|
524
|
+
const completionDir = path.join(homeDir, '.zsh', 'completions');
|
|
525
|
+
execSync(`mkdir -p "${completionDir}"`, { stdio: 'ignore' });
|
|
526
|
+
const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.zsh');
|
|
527
|
+
execSync(`cp "${completionPath}" "${completionDir}/_make-folder-txt"`, { stdio: 'ignore' });
|
|
528
|
+
|
|
529
|
+
try {
|
|
530
|
+
const zshrcContent = fs.readFileSync(zshrc, 'utf8');
|
|
531
|
+
if (!zshrcContent.includes('fpath+=~/.zsh/completions')) {
|
|
532
|
+
fs.appendFileSync(zshrc, '\n# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
|
|
533
|
+
}
|
|
534
|
+
} catch (e) {
|
|
535
|
+
fs.writeFileSync(zshrc, '# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
|
|
536
|
+
}
|
|
537
|
+
console.log('✅ Zsh completion installed!');
|
|
538
|
+
} catch (err) {
|
|
539
|
+
// Silent fail for zsh
|
|
540
|
+
}
|
|
541
|
+
} else {
|
|
542
|
+
// bash
|
|
543
|
+
try {
|
|
544
|
+
const bashrc = path.join(homeDir, '.bashrc');
|
|
545
|
+
const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.bash');
|
|
546
|
+
try {
|
|
547
|
+
const bashrcContent = fs.readFileSync(bashrc, 'utf8');
|
|
548
|
+
if (!bashrcContent.includes('make-folder-txt-completion.bash')) {
|
|
549
|
+
fs.appendFileSync(bashrc, `\n# make-folder-txt completion\nsource "${completionPath}"\n`);
|
|
550
|
+
}
|
|
551
|
+
} catch (e) {
|
|
552
|
+
fs.writeFileSync(bashrc, `# make-folder-txt completion\nsource "${completionPath}"\n`);
|
|
553
|
+
}
|
|
554
|
+
console.log('✅ Bash completion installed!');
|
|
555
|
+
} catch (err) {
|
|
556
|
+
// Silent fail for bash
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
console.log('💡 Restart your terminal to enable autocompletion');
|
|
561
|
+
console.log('');
|
|
562
|
+
}
|
|
563
|
+
} catch (err) {
|
|
564
|
+
// Silent fail - don't interrupt the main functionality
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
// Run completion check on first run (but not for help/version commands)
|
|
569
|
+
if (!args.includes("--help") && !args.includes("-h") &&
|
|
570
|
+
!args.includes("--version") && !args.includes("-v") &&
|
|
571
|
+
!args.includes("--install-completion")) {
|
|
572
|
+
checkAndInstallCompletion();
|
|
573
|
+
}
|
|
574
|
+
|
|
200
575
|
if (args.includes("--install-completion")) {
|
|
201
576
|
const { execSync } = require('child_process');
|
|
202
577
|
const path = require('path');
|
|
@@ -205,8 +580,26 @@ if (args.includes("--install-completion")) {
|
|
|
205
580
|
try {
|
|
206
581
|
const homeDir = os.homedir();
|
|
207
582
|
const shell = process.env.SHELL || '';
|
|
583
|
+
const platform = process.platform;
|
|
208
584
|
|
|
209
|
-
if (
|
|
585
|
+
if (platform === 'win32') {
|
|
586
|
+
// Windows - Check if PowerShell is available
|
|
587
|
+
try {
|
|
588
|
+
execSync('powershell -Command "Get-Host"', { stdio: 'ignore' });
|
|
589
|
+
|
|
590
|
+
// Install PowerShell completion
|
|
591
|
+
const completionScript = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.ps1');
|
|
592
|
+
const installScript = path.join(__dirname, '..', 'completion', 'install-powershell-completion.ps1');
|
|
593
|
+
|
|
594
|
+
// Run the PowerShell installation script
|
|
595
|
+
execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'inherit' });
|
|
596
|
+
|
|
597
|
+
} catch (err) {
|
|
598
|
+
console.error('❌ Failed to install PowerShell completion:', err.message);
|
|
599
|
+
process.exit(1);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
} else if (shell.includes('zsh')) {
|
|
210
603
|
// Install for zsh
|
|
211
604
|
const zshrc = path.join(homeDir, '.zshrc');
|
|
212
605
|
const completionDir = path.join(homeDir, '.zsh', 'completions');
|
|
@@ -278,9 +671,13 @@ Dump an entire project folder into a single readable .txt file.
|
|
|
278
671
|
--ignore-file, -ifi <names...> Ignore specific files by name
|
|
279
672
|
--only-folder, -ofo <names...> Include only specific folders
|
|
280
673
|
--only-file, -ofi <names...> Include only specific files
|
|
674
|
+
--skip-large <size> Skip files larger than specified size (default: 500KB)
|
|
675
|
+
--no-skip Include all files regardless of size
|
|
676
|
+
--split-method <method> Split output: folder, file, or size
|
|
677
|
+
--split-size <size> Split output when size exceeds limit (requires --split-method size)
|
|
281
678
|
--copy Copy output to clipboard
|
|
282
679
|
--force Include everything (overrides all ignore patterns)
|
|
283
|
-
--install-completion
|
|
680
|
+
--install-completion Install shell autocompletion (bash/zsh/PowerShell) - usually automatic
|
|
284
681
|
--help, -h Show this help message
|
|
285
682
|
--version, -v Show version information
|
|
286
683
|
|
|
@@ -288,6 +685,12 @@ Dump an entire project folder into a single readable .txt file.
|
|
|
288
685
|
make-folder-txt
|
|
289
686
|
make-folder-txt --copy
|
|
290
687
|
make-folder-txt --force
|
|
688
|
+
make-folder-txt --skip-large 400KB
|
|
689
|
+
make-folder-txt --skip-large 5GB
|
|
690
|
+
make-folder-txt --no-skip
|
|
691
|
+
make-folder-txt --split-method folder
|
|
692
|
+
make-folder-txt --split-method file
|
|
693
|
+
make-folder-txt --split-method size --split-size 5MB
|
|
291
694
|
make-folder-txt --install-completion
|
|
292
695
|
make-folder-txt --ignore-folder node_modules dist
|
|
293
696
|
make-folder-txt -ifo node_modules dist
|
|
@@ -319,6 +722,10 @@ const onlyFiles = new Set();
|
|
|
319
722
|
let outputArg = null;
|
|
320
723
|
let copyToClipboardFlag = false;
|
|
321
724
|
let forceFlag = false;
|
|
725
|
+
let maxFileSize = 500 * 1024; // Default 500KB
|
|
726
|
+
let noSkipFlag = false;
|
|
727
|
+
let splitMethod = null; // 'folder', 'file', 'size'
|
|
728
|
+
let splitSize = null; // size in bytes
|
|
322
729
|
|
|
323
730
|
for (let i = 0; i < args.length; i += 1) {
|
|
324
731
|
const arg = args[i];
|
|
@@ -333,6 +740,81 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
333
740
|
continue;
|
|
334
741
|
}
|
|
335
742
|
|
|
743
|
+
if (arg === "--no-skip") {
|
|
744
|
+
noSkipFlag = true;
|
|
745
|
+
continue;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
if (arg === "--skip-large") {
|
|
749
|
+
if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
|
|
750
|
+
console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
|
|
751
|
+
process.exit(1);
|
|
752
|
+
}
|
|
753
|
+
maxFileSize = parseFileSize(args[i + 1]);
|
|
754
|
+
i += 1;
|
|
755
|
+
continue;
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
if (arg.startsWith("--skip-large=")) {
|
|
759
|
+
const value = arg.slice("--skip-large=".length);
|
|
760
|
+
if (!value) {
|
|
761
|
+
console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
|
|
762
|
+
process.exit(1);
|
|
763
|
+
}
|
|
764
|
+
maxFileSize = parseFileSize(value);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (arg === "--split-method") {
|
|
769
|
+
if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
|
|
770
|
+
console.error("Error: --split-method requires a method (folder, file, or size).");
|
|
771
|
+
process.exit(1);
|
|
772
|
+
}
|
|
773
|
+
const method = args[i + 1].toLowerCase();
|
|
774
|
+
if (!['folder', 'file', 'size'].includes(method)) {
|
|
775
|
+
console.error("Error: --split-method must be one of: folder, file, size");
|
|
776
|
+
process.exit(1);
|
|
777
|
+
}
|
|
778
|
+
splitMethod = method;
|
|
779
|
+
i += 1;
|
|
780
|
+
continue;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
if (arg.startsWith("--split-method=")) {
|
|
784
|
+
const value = arg.slice("--split-method=".length);
|
|
785
|
+
if (!value) {
|
|
786
|
+
console.error("Error: --split-method requires a method (folder, file, or size).");
|
|
787
|
+
process.exit(1);
|
|
788
|
+
}
|
|
789
|
+
const method = value.toLowerCase();
|
|
790
|
+
if (!['folder', 'file', 'size'].includes(method)) {
|
|
791
|
+
console.error("Error: --split-method must be one of: folder, file, size");
|
|
792
|
+
process.exit(1);
|
|
793
|
+
}
|
|
794
|
+
splitMethod = method;
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
|
|
798
|
+
if (arg === "--split-size") {
|
|
799
|
+
if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
|
|
800
|
+
console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
|
|
801
|
+
process.exit(1);
|
|
802
|
+
}
|
|
803
|
+
splitSize = parseFileSize(args[i + 1]);
|
|
804
|
+
i += 1;
|
|
805
|
+
continue;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (arg.startsWith("--split-size=")) {
|
|
809
|
+
const value = arg.slice("--split-size=".length);
|
|
810
|
+
if (!value) {
|
|
811
|
+
console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
|
|
812
|
+
process.exit(1);
|
|
813
|
+
}
|
|
814
|
+
splitSize = parseFileSize(value);
|
|
815
|
+
continue;
|
|
816
|
+
}
|
|
817
|
+
|
|
336
818
|
if (arg === "--ignore-folder" || arg === "-ifo") {
|
|
337
819
|
let consumed = 0;
|
|
338
820
|
while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
|
|
@@ -451,6 +933,17 @@ for (let i = 0; i < args.length; i += 1) {
|
|
|
451
933
|
process.exit(1);
|
|
452
934
|
}
|
|
453
935
|
|
|
936
|
+
// Validate split options
|
|
937
|
+
if (splitMethod === 'size' && !splitSize) {
|
|
938
|
+
console.error("Error: --split-method size requires --split-size to be specified.");
|
|
939
|
+
process.exit(1);
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
if (splitSize && splitMethod !== 'size') {
|
|
943
|
+
console.error("Error: --split-size can only be used with --split-method size.");
|
|
944
|
+
process.exit(1);
|
|
945
|
+
}
|
|
946
|
+
|
|
454
947
|
const folderPath = process.cwd();
|
|
455
948
|
const rootName = path.basename(folderPath);
|
|
456
949
|
|
|
@@ -474,7 +967,47 @@ const { lines: treeLines, filePaths } = collectFiles(
|
|
|
474
967
|
{ hasOnlyFilters, rootName, txtIgnore, force: forceFlag },
|
|
475
968
|
);
|
|
476
969
|
|
|
477
|
-
// ──
|
|
970
|
+
// ── handle splitting ──────────────────────────────────────────────────────────────
|
|
971
|
+
const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
|
|
972
|
+
|
|
973
|
+
if (splitMethod) {
|
|
974
|
+
console.log(`🔧 Splitting output by: ${splitMethod}`);
|
|
975
|
+
|
|
976
|
+
let results;
|
|
977
|
+
|
|
978
|
+
if (splitMethod === 'folder') {
|
|
979
|
+
results = splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag);
|
|
980
|
+
} else if (splitMethod === 'file') {
|
|
981
|
+
results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
|
|
982
|
+
} else if (splitMethod === 'size') {
|
|
983
|
+
results = splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag);
|
|
984
|
+
}
|
|
985
|
+
|
|
986
|
+
console.log(`✅ Done! Created ${results.length} split files:`);
|
|
987
|
+
console.log('');
|
|
988
|
+
|
|
989
|
+
results.forEach((result, index) => {
|
|
990
|
+
if (splitMethod === 'folder') {
|
|
991
|
+
console.log(`📁 Folder: ${result.folder}`);
|
|
992
|
+
} else if (splitMethod === 'file') {
|
|
993
|
+
console.log(`📄 File: ${result.fileName}`);
|
|
994
|
+
} else if (splitMethod === 'size') {
|
|
995
|
+
console.log(`📦 Part ${result.part}`);
|
|
996
|
+
}
|
|
997
|
+
console.log(`📄 Output : ${result.file}`);
|
|
998
|
+
console.log(`📊 Size : ${result.size} KB`);
|
|
999
|
+
console.log(`🗂️ Files : ${result.files}`);
|
|
1000
|
+
console.log('');
|
|
1001
|
+
});
|
|
1002
|
+
|
|
1003
|
+
if (copyToClipboardFlag) {
|
|
1004
|
+
console.log('⚠️ --copy flag is not compatible with splitting - clipboard copy skipped');
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
process.exit(0);
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
// ── build output (no splitting) ───────────────────────────────────────────────────
|
|
478
1011
|
const out = [];
|
|
479
1012
|
const divider = "=".repeat(80);
|
|
480
1013
|
const subDivider = "-".repeat(80);
|
|
@@ -499,11 +1032,12 @@ out.push("FILE CONTENTS");
|
|
|
499
1032
|
out.push(divider);
|
|
500
1033
|
|
|
501
1034
|
filePaths.forEach(({ abs, rel }) => {
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
1035
|
+
out.push("");
|
|
1036
|
+
out.push(subDivider);
|
|
1037
|
+
out.push(`FILE: ${rel}`);
|
|
1038
|
+
out.push(subDivider);
|
|
1039
|
+
const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
|
|
1040
|
+
out.push(readContent(abs, forceFlag, effectiveMaxSize));
|
|
507
1041
|
});
|
|
508
1042
|
|
|
509
1043
|
out.push("");
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# PowerShell completion installation script for make-folder-txt
|
|
2
|
+
|
|
3
|
+
function Install-MakeFolderTxtCompletion {
|
|
4
|
+
param(
|
|
5
|
+
[switch]$Force,
|
|
6
|
+
[switch]$CurrentUser
|
|
7
|
+
)
|
|
8
|
+
|
|
9
|
+
$ErrorActionPreference = 'Stop'
|
|
10
|
+
|
|
11
|
+
# Determine PowerShell profile path
|
|
12
|
+
if ($CurrentUser) {
|
|
13
|
+
$profilePath = $PROFILE.CurrentUserCurrentHost
|
|
14
|
+
} else {
|
|
15
|
+
$profilePath = $PROFILE.AllUsersCurrentHost
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
# Create profile directory if it doesn't exist
|
|
19
|
+
$profileDir = Split-Path $profilePath -Parent
|
|
20
|
+
if (-not (Test-Path $profileDir)) {
|
|
21
|
+
try {
|
|
22
|
+
New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
|
|
23
|
+
Write-Host "Created profile directory: $profileDir" -ForegroundColor Green
|
|
24
|
+
} catch {
|
|
25
|
+
Write-Error "Failed to create profile directory: $profileDir"
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
# Get the completion script content
|
|
31
|
+
$completionScriptPath = Join-Path $PSScriptRoot 'make-folder-txt-completion.ps1'
|
|
32
|
+
if (-not (Test-Path $completionScriptPath)) {
|
|
33
|
+
Write-Error "Completion script not found: $completionScriptPath"
|
|
34
|
+
return
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
$completionContent = Get-Content $completionScriptPath -Raw
|
|
38
|
+
|
|
39
|
+
# Check if completion is already installed
|
|
40
|
+
if (Test-Path $profilePath) {
|
|
41
|
+
$profileContent = Get-Content $profilePath -Raw
|
|
42
|
+
if ($profileContent -match 'make-folder-txt.*completion') {
|
|
43
|
+
if (-not $Force) {
|
|
44
|
+
Write-Host "make-folder-txt completion is already installed in $profilePath" -ForegroundColor Yellow
|
|
45
|
+
Write-Host "Use -Force to reinstall" -ForegroundColor Yellow
|
|
46
|
+
return
|
|
47
|
+
}
|
|
48
|
+
Write-Host "Removing existing completion..." -ForegroundColor Yellow
|
|
49
|
+
# Remove existing completion
|
|
50
|
+
$profileContent = $profileContent -replace '(?s)# make-folder-txt completion.*?Register-ArgumentComplester.*?Export-ModuleMember.*?\n', ''
|
|
51
|
+
Set-Content $profilePath $profileContent -Force
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
# Add completion to profile
|
|
56
|
+
$completionBlock = @"
|
|
57
|
+
|
|
58
|
+
# make-folder-txt completion
|
|
59
|
+
$completionContent
|
|
60
|
+
"@
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
Add-Content $profilePath $completionBlock -Force
|
|
64
|
+
Write-Host "✅ PowerShell completion installed successfully!" -ForegroundColor Green
|
|
65
|
+
Write-Host "Added to: $profilePath" -ForegroundColor Cyan
|
|
66
|
+
Write-Host "Restart PowerShell or run: . `$profile" -ForegroundColor Cyan
|
|
67
|
+
} catch {
|
|
68
|
+
Write-Error "Failed to install completion: $_"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
# Auto-install if script is run directly
|
|
73
|
+
if ($MyInvocation.InvocationName -eq $MyInvocation.MyCommand.Name) {
|
|
74
|
+
Install-MakeFolderTxtCompletion -CurrentUser
|
|
75
|
+
}
|
|
@@ -7,7 +7,7 @@ _make_folder_txt_completion() {
|
|
|
7
7
|
cur="${COMP_WORDS[COMP_CWORD]}"
|
|
8
8
|
prev="${COMP_WORDS[COMP_CWORD-1]}"
|
|
9
9
|
|
|
10
|
-
opts="--ignore-folder -ifo --ignore-file -ifi --only-folder -ofo --only-file -ofi --copy --force --help --version -h -v"
|
|
10
|
+
opts="--ignore-folder -ifo --ignore-file -ifi --only-folder -ofo --only-file -ofi --skip-large --no-skip --split-method --split-size --copy --force --help --version -h -v"
|
|
11
11
|
|
|
12
12
|
case "${prev}" in
|
|
13
13
|
--ignore-folder|-ifo)
|
|
@@ -28,6 +28,24 @@ _make_folder_txt_completion() {
|
|
|
28
28
|
COMPREPLY=( $(compgen -W "${folders}" -- ${cur}) )
|
|
29
29
|
return 0
|
|
30
30
|
;;
|
|
31
|
+
--skip-large)
|
|
32
|
+
# Complete with common size formats
|
|
33
|
+
local sizes="100KB 200KB 400KB 500KB 1MB 5MB 10MB 100MB 1GB 5GB"
|
|
34
|
+
COMPREPLY=( $(compgen -W "${sizes}" -- ${cur}) )
|
|
35
|
+
return 0
|
|
36
|
+
;;
|
|
37
|
+
--split-method)
|
|
38
|
+
# Complete with split methods
|
|
39
|
+
local methods="folder file size"
|
|
40
|
+
COMPREPLY=( $(compgen -W "${methods}" -- ${cur}) )
|
|
41
|
+
return 0
|
|
42
|
+
;;
|
|
43
|
+
--split-size)
|
|
44
|
+
# Complete with common size formats
|
|
45
|
+
local sizes="1MB 5MB 10MB 50MB 100MB 500MB 1GB"
|
|
46
|
+
COMPREPLY=( $(compgen -W "${sizes}" -- ${cur}) )
|
|
47
|
+
return 0
|
|
48
|
+
;;
|
|
31
49
|
*)
|
|
32
50
|
;;
|
|
33
51
|
esac
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# make-folder-txt PowerShell completion script
|
|
2
|
+
|
|
3
|
+
Register-ArgumentCompleter -Native -CommandName 'make-folder-txt' -ScriptBlock {
|
|
4
|
+
param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
|
5
|
+
|
|
6
|
+
# Get the current argument being completed
|
|
7
|
+
$currentArgument = $wordToComplete
|
|
8
|
+
|
|
9
|
+
# Define available options
|
|
10
|
+
$options = @(
|
|
11
|
+
'--ignore-folder', '-ifo',
|
|
12
|
+
'--ignore-file', '-ifi',
|
|
13
|
+
'--only-folder', '-ofo',
|
|
14
|
+
'--only-file', '-ofi',
|
|
15
|
+
'--skip-large',
|
|
16
|
+
'--no-skip',
|
|
17
|
+
'--split-method',
|
|
18
|
+
'--split-size',
|
|
19
|
+
'--copy',
|
|
20
|
+
'--force',
|
|
21
|
+
'--install-completion',
|
|
22
|
+
'--help', '-h',
|
|
23
|
+
'--version', '-v'
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# Get the previous parameter to determine context
|
|
27
|
+
$previousParameter = if ($commandAst.CommandElements.Count -gt 1) {
|
|
28
|
+
$commandAst.CommandElements[-2].Extent.Text
|
|
29
|
+
} else {
|
|
30
|
+
''
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
switch ($previousParameter) {
|
|
34
|
+
{ $_ -in '--ignore-folder', '-ifo', '--only-folder', '-ofo' } {
|
|
35
|
+
# Complete with folder names
|
|
36
|
+
try {
|
|
37
|
+
$folders = Get-ChildItem -Directory -Name | Where-Object { $_ -like "*$currentArgument*" }
|
|
38
|
+
return $folders | ForEach-Object {
|
|
39
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Folder: $_")
|
|
40
|
+
}
|
|
41
|
+
} catch {
|
|
42
|
+
return @()
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
{ $_ -in '--ignore-file', '-ifi', '--only-file', '-ofi' } {
|
|
46
|
+
# Complete with file names
|
|
47
|
+
try {
|
|
48
|
+
$files = Get-ChildItem -File -Name | Where-Object { $_ -like "*$currentArgument*" }
|
|
49
|
+
return $files | ForEach-Object {
|
|
50
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "File: $_")
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
return @()
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
'--skip-large' {
|
|
57
|
+
# Complete with common size formats
|
|
58
|
+
$sizes = @('100KB', '200KB', '400KB', '500KB', '1MB', '5MB', '10MB', '100MB', '1GB', '5GB')
|
|
59
|
+
$matchingSizes = $sizes | Where-Object { $_ -like "*$currentArgument*" }
|
|
60
|
+
return $matchingSizes | ForEach-Object {
|
|
61
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Size: $_")
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
'--split-method' {
|
|
65
|
+
# Complete with split methods
|
|
66
|
+
$methods = @('folder', 'file', 'size')
|
|
67
|
+
$matchingMethods = $methods | Where-Object { $_ -like "*$currentArgument*" }
|
|
68
|
+
return $matchingMethods | ForEach-Object {
|
|
69
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Method: $_")
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
'--split-size' {
|
|
73
|
+
# Complete with common size formats
|
|
74
|
+
$sizes = @('1MB', '5MB', '10MB', '50MB', '100MB', '500MB', '1GB')
|
|
75
|
+
$matchingSizes = $sizes | Where-Object { $_ -like "*$currentArgument*" }
|
|
76
|
+
return $matchingSizes | ForEach-Object {
|
|
77
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Size: $_")
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
default {
|
|
81
|
+
# Complete with options
|
|
82
|
+
if ($currentArgument -like '-*') {
|
|
83
|
+
$matchingOptions = $options | Where-Object { $_ -like "*$currentArgument*" }
|
|
84
|
+
return $matchingOptions | ForEach-Object {
|
|
85
|
+
[System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Option: $_")
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return @()
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Export the completion function
|
|
95
|
+
Export-ModuleMember -Function *
|
|
@@ -12,6 +12,10 @@ _make_folder_txt() {
|
|
|
12
12
|
'-ofo[Include only specific folders]:folder:_directories'
|
|
13
13
|
'--only-file[Include only specific files]:file:_files'
|
|
14
14
|
'-ofi[Include only specific files]:file:_files'
|
|
15
|
+
'--skip-large[Skip files larger than specified size]:size:(100KB 200KB 400KB 500KB 1MB 5MB 10MB 100MB 1GB 5GB)'
|
|
16
|
+
'--no-skip[Include all files regardless of size]'
|
|
17
|
+
'--split-method[Split output by method]:method:(folder file size)'
|
|
18
|
+
'--split-size[Split output when size exceeds limit]:size:(1MB 5MB 10MB 50MB 100MB 500MB 1GB)'
|
|
15
19
|
'--copy[Copy output to clipboard]'
|
|
16
20
|
'--force[Include everything (overrides all ignore patterns)]'
|
|
17
21
|
'--help[Show help message]'
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
================================================================================
|
|
2
|
+
START OF FOLDER: make-folder-txt
|
|
3
|
+
================================================================================
|
|
4
|
+
|
|
5
|
+
================================================================================
|
|
6
|
+
PROJECT STRUCTURE
|
|
7
|
+
================================================================================
|
|
8
|
+
Root: C:\Programming\make-folder-txt
|
|
9
|
+
|
|
10
|
+
make-folder-txt/
|
|
11
|
+
├── package.json
|
|
12
|
+
|
|
13
|
+
Total files: 1
|
|
14
|
+
|
|
15
|
+
================================================================================
|
|
16
|
+
FILE CONTENTS
|
|
17
|
+
================================================================================
|
|
18
|
+
|
|
19
|
+
--------------------------------------------------------------------------------
|
|
20
|
+
FILE: /package.json
|
|
21
|
+
--------------------------------------------------------------------------------
|
|
22
|
+
{
|
|
23
|
+
"name": "make-folder-txt",
|
|
24
|
+
"version": "2.0.1",
|
|
25
|
+
"description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
|
|
26
|
+
"main": "bin/make-folder-txt.js",
|
|
27
|
+
"bin": {
|
|
28
|
+
"make-folder-txt": "bin/make-folder-txt.js"
|
|
29
|
+
},
|
|
30
|
+
"scripts": {
|
|
31
|
+
"test": "echo \"No tests yet\" && exit 0"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [
|
|
34
|
+
"folder",
|
|
35
|
+
"dump",
|
|
36
|
+
"project",
|
|
37
|
+
"structure",
|
|
38
|
+
"txt",
|
|
39
|
+
"cli",
|
|
40
|
+
"export"
|
|
41
|
+
],
|
|
42
|
+
"author": "Muhammad Saad Amin",
|
|
43
|
+
"license": "MIT",
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=14.0.0"
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
================================================================================
|
|
51
|
+
END OF FOLDER: make-folder-txt
|
|
52
|
+
================================================================================
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "make-folder-txt",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
|
|
5
5
|
"main": "bin/make-folder-txt.js",
|
|
6
6
|
"bin": {
|