make-folder-txt 2.0.1 → 2.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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
 
@@ -24,6 +24,8 @@ Ever needed to share your entire project with ChatGPT, Claude, or a teammate —
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
@@ -133,6 +135,76 @@ notepad $PROFILE
133
135
 
134
136
  The completion works out of the box - just run the tool once and restart your terminal!
135
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.
207
+
136
208
  Ignore specific folders/files by name:
137
209
 
138
210
  ```bash
@@ -179,13 +179,37 @@ function collectFiles(
179
179
  return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
180
180
  }
181
181
 
182
- function readContent(absPath, force = false) {
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 > 500 * 1024) {
188
- return `[file too large: ${(stat.size / 1024).toFixed(1)} KB skipped]`;
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,11 +217,243 @@ 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
 
200
- // Check if completion is already installed, install if not
456
+ // Check if completion is already installed, install if not (silent on subsequent runs)
201
457
  function checkAndInstallCompletion() {
202
458
  const { execSync } = require('child_process');
203
459
  const path = require('path');
@@ -211,19 +467,33 @@ function checkAndInstallCompletion() {
211
467
  let completionInstalled = false;
212
468
 
213
469
  if (platform === 'win32') {
214
- // Check PowerShell completion - try multiple profile locations
215
- const profilePaths = [
216
- path.join(homeDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
217
- path.join(homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
218
- path.join(homeDir, '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1')
219
- ];
220
-
221
- for (const profilePath of profilePaths) {
470
+ // Check PowerShell completion - use the same path as the installation script
471
+ try {
472
+ // Get the PowerShell profile path like the installation script does
473
+ const profilePathResult = execSync('powershell -Command "echo $PROFILE.CurrentUserCurrentHost"', { encoding: 'utf8' }).trim();
474
+ const profilePath = profilePathResult.replace(/['"]/g, '').replace(/\.CurrentUserCurrentHost$/, ''); // Remove quotes and suffix
475
+
222
476
  if (fs.existsSync(profilePath)) {
223
477
  const profileContent = fs.readFileSync(profilePath, 'utf8');
224
- if (profileContent.includes('make-folder-txt-completion')) {
478
+ if (profileContent.includes('make-folder-txt') || profileContent.includes('Register-ArgumentCompleter')) {
225
479
  completionInstalled = true;
226
- break;
480
+ }
481
+ }
482
+ } catch (err) {
483
+ // If PowerShell detection fails, try fallback paths
484
+ const profilePaths = [
485
+ path.join(homeDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
486
+ path.join(homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
487
+ path.join(homeDir, '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1')
488
+ ];
489
+
490
+ for (const profilePath of profilePaths) {
491
+ if (fs.existsSync(profilePath)) {
492
+ const profileContent = fs.readFileSync(profilePath, 'utf8');
493
+ if (profileContent.includes('make-folder-txt') || profileContent.includes('Register-ArgumentCompleter')) {
494
+ completionInstalled = true;
495
+ break;
496
+ }
227
497
  }
228
498
  }
229
499
  }
@@ -247,7 +517,7 @@ function checkAndInstallCompletion() {
247
517
  }
248
518
  }
249
519
 
250
- // If completion is not installed, install it automatically
520
+ // If completion is not installed, install it automatically (show message only first time)
251
521
  if (!completionInstalled) {
252
522
  console.log('🔧 Installing shell autocompletion for make-folder-txt...');
253
523
 
@@ -257,7 +527,7 @@ function checkAndInstallCompletion() {
257
527
  execSync('powershell -Command "Get-Host"', { stdio: 'ignore' });
258
528
  const installScript = path.join(__dirname, '..', 'completion', 'install-powershell-completion.ps1');
259
529
  execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'ignore' });
260
- console.log('✅ PowerShell completion installed!');
530
+ console.log('✅ PowerShell completion installed! Restart your terminal to enable autocompletion');
261
531
  } catch (err) {
262
532
  // Silent fail for PowerShell
263
533
  }
@@ -278,7 +548,7 @@ function checkAndInstallCompletion() {
278
548
  } catch (e) {
279
549
  fs.writeFileSync(zshrc, '# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
280
550
  }
281
- console.log('✅ Zsh completion installed!');
551
+ console.log('✅ Zsh completion installed! Restart your terminal or run: source ~/.zshrc');
282
552
  } catch (err) {
283
553
  // Silent fail for zsh
284
554
  }
@@ -295,7 +565,7 @@ function checkAndInstallCompletion() {
295
565
  } catch (e) {
296
566
  fs.writeFileSync(bashrc, `# make-folder-txt completion\nsource "${completionPath}"\n`);
297
567
  }
298
- console.log('✅ Bash completion installed!');
568
+ console.log('✅ Bash completion installed! Restart your terminal or run: source ~/.bashrc');
299
569
  } catch (err) {
300
570
  // Silent fail for bash
301
571
  }
@@ -309,7 +579,7 @@ function checkAndInstallCompletion() {
309
579
  }
310
580
  }
311
581
 
312
- // Run completion check on first run (but not for help/version commands)
582
+ // Run completion check on first run (but not for help/version/install-completion commands)
313
583
  if (!args.includes("--help") && !args.includes("-h") &&
314
584
  !args.includes("--version") && !args.includes("-v") &&
315
585
  !args.includes("--install-completion")) {
@@ -336,7 +606,8 @@ if (args.includes("--install-completion")) {
336
606
  const installScript = path.join(__dirname, '..', 'completion', 'install-powershell-completion.ps1');
337
607
 
338
608
  // Run the PowerShell installation script
339
- execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'inherit' });
609
+ execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'ignore' });
610
+ console.log('✅ PowerShell completion installed! Restart your terminal to enable autocompletion');
340
611
 
341
612
  } catch (err) {
342
613
  console.error('❌ Failed to install PowerShell completion:', err.message);
@@ -415,6 +686,10 @@ Dump an entire project folder into a single readable .txt file.
415
686
  --ignore-file, -ifi <names...> Ignore specific files by name
416
687
  --only-folder, -ofo <names...> Include only specific folders
417
688
  --only-file, -ofi <names...> Include only specific files
689
+ --skip-large <size> Skip files larger than specified size (default: 500KB)
690
+ --no-skip Include all files regardless of size
691
+ --split-method <method> Split output: folder, file, or size
692
+ --split-size <size> Split output when size exceeds limit (requires --split-method size)
418
693
  --copy Copy output to clipboard
419
694
  --force Include everything (overrides all ignore patterns)
420
695
  --install-completion Install shell autocompletion (bash/zsh/PowerShell) - usually automatic
@@ -425,6 +700,12 @@ Dump an entire project folder into a single readable .txt file.
425
700
  make-folder-txt
426
701
  make-folder-txt --copy
427
702
  make-folder-txt --force
703
+ make-folder-txt --skip-large 400KB
704
+ make-folder-txt --skip-large 5GB
705
+ make-folder-txt --no-skip
706
+ make-folder-txt --split-method folder
707
+ make-folder-txt --split-method file
708
+ make-folder-txt --split-method size --split-size 5MB
428
709
  make-folder-txt --install-completion
429
710
  make-folder-txt --ignore-folder node_modules dist
430
711
  make-folder-txt -ifo node_modules dist
@@ -456,6 +737,10 @@ const onlyFiles = new Set();
456
737
  let outputArg = null;
457
738
  let copyToClipboardFlag = false;
458
739
  let forceFlag = false;
740
+ let maxFileSize = 500 * 1024; // Default 500KB
741
+ let noSkipFlag = false;
742
+ let splitMethod = null; // 'folder', 'file', 'size'
743
+ let splitSize = null; // size in bytes
459
744
 
460
745
  for (let i = 0; i < args.length; i += 1) {
461
746
  const arg = args[i];
@@ -470,6 +755,81 @@ for (let i = 0; i < args.length; i += 1) {
470
755
  continue;
471
756
  }
472
757
 
758
+ if (arg === "--no-skip") {
759
+ noSkipFlag = true;
760
+ continue;
761
+ }
762
+
763
+ if (arg === "--skip-large") {
764
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
765
+ console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
766
+ process.exit(1);
767
+ }
768
+ maxFileSize = parseFileSize(args[i + 1]);
769
+ i += 1;
770
+ continue;
771
+ }
772
+
773
+ if (arg.startsWith("--skip-large=")) {
774
+ const value = arg.slice("--skip-large=".length);
775
+ if (!value) {
776
+ console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
777
+ process.exit(1);
778
+ }
779
+ maxFileSize = parseFileSize(value);
780
+ continue;
781
+ }
782
+
783
+ if (arg === "--split-method") {
784
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
785
+ console.error("Error: --split-method requires a method (folder, file, or size).");
786
+ process.exit(1);
787
+ }
788
+ const method = args[i + 1].toLowerCase();
789
+ if (!['folder', 'file', 'size'].includes(method)) {
790
+ console.error("Error: --split-method must be one of: folder, file, size");
791
+ process.exit(1);
792
+ }
793
+ splitMethod = method;
794
+ i += 1;
795
+ continue;
796
+ }
797
+
798
+ if (arg.startsWith("--split-method=")) {
799
+ const value = arg.slice("--split-method=".length);
800
+ if (!value) {
801
+ console.error("Error: --split-method requires a method (folder, file, or size).");
802
+ process.exit(1);
803
+ }
804
+ const method = value.toLowerCase();
805
+ if (!['folder', 'file', 'size'].includes(method)) {
806
+ console.error("Error: --split-method must be one of: folder, file, size");
807
+ process.exit(1);
808
+ }
809
+ splitMethod = method;
810
+ continue;
811
+ }
812
+
813
+ if (arg === "--split-size") {
814
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
815
+ console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
816
+ process.exit(1);
817
+ }
818
+ splitSize = parseFileSize(args[i + 1]);
819
+ i += 1;
820
+ continue;
821
+ }
822
+
823
+ if (arg.startsWith("--split-size=")) {
824
+ const value = arg.slice("--split-size=".length);
825
+ if (!value) {
826
+ console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
827
+ process.exit(1);
828
+ }
829
+ splitSize = parseFileSize(value);
830
+ continue;
831
+ }
832
+
473
833
  if (arg === "--ignore-folder" || arg === "-ifo") {
474
834
  let consumed = 0;
475
835
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
@@ -588,6 +948,17 @@ for (let i = 0; i < args.length; i += 1) {
588
948
  process.exit(1);
589
949
  }
590
950
 
951
+ // Validate split options
952
+ if (splitMethod === 'size' && !splitSize) {
953
+ console.error("Error: --split-method size requires --split-size to be specified.");
954
+ process.exit(1);
955
+ }
956
+
957
+ if (splitSize && splitMethod !== 'size') {
958
+ console.error("Error: --split-size can only be used with --split-method size.");
959
+ process.exit(1);
960
+ }
961
+
591
962
  const folderPath = process.cwd();
592
963
  const rootName = path.basename(folderPath);
593
964
 
@@ -611,7 +982,47 @@ const { lines: treeLines, filePaths } = collectFiles(
611
982
  { hasOnlyFilters, rootName, txtIgnore, force: forceFlag },
612
983
  );
613
984
 
614
- // ── build output ──────────────────────────────────────────────────────────────
985
+ // ── handle splitting ──────────────────────────────────────────────────────────────
986
+ const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
987
+
988
+ if (splitMethod) {
989
+ console.log(`🔧 Splitting output by: ${splitMethod}`);
990
+
991
+ let results;
992
+
993
+ if (splitMethod === 'folder') {
994
+ results = splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag);
995
+ } else if (splitMethod === 'file') {
996
+ results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
997
+ } else if (splitMethod === 'size') {
998
+ results = splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag);
999
+ }
1000
+
1001
+ console.log(`✅ Done! Created ${results.length} split files:`);
1002
+ console.log('');
1003
+
1004
+ results.forEach((result, index) => {
1005
+ if (splitMethod === 'folder') {
1006
+ console.log(`📁 Folder: ${result.folder}`);
1007
+ } else if (splitMethod === 'file') {
1008
+ console.log(`📄 File: ${result.fileName}`);
1009
+ } else if (splitMethod === 'size') {
1010
+ console.log(`📦 Part ${result.part}`);
1011
+ }
1012
+ console.log(`📄 Output : ${result.file}`);
1013
+ console.log(`📊 Size : ${result.size} KB`);
1014
+ console.log(`🗂️ Files : ${result.files}`);
1015
+ console.log('');
1016
+ });
1017
+
1018
+ if (copyToClipboardFlag) {
1019
+ console.log('⚠️ --copy flag is not compatible with splitting - clipboard copy skipped');
1020
+ }
1021
+
1022
+ process.exit(0);
1023
+ }
1024
+
1025
+ // ── build output (no splitting) ───────────────────────────────────────────────────
615
1026
  const out = [];
616
1027
  const divider = "=".repeat(80);
617
1028
  const subDivider = "-".repeat(80);
@@ -636,11 +1047,12 @@ out.push("FILE CONTENTS");
636
1047
  out.push(divider);
637
1048
 
638
1049
  filePaths.forEach(({ abs, rel }) => {
639
- out.push("");
640
- out.push(subDivider);
641
- out.push(`FILE: ${rel}`);
642
- out.push(subDivider);
643
- out.push(readContent(abs, forceFlag));
1050
+ out.push("");
1051
+ out.push(subDivider);
1052
+ out.push(`FILE: ${rel}`);
1053
+ out.push(subDivider);
1054
+ const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
1055
+ out.push(readContent(abs, forceFlag, effectiveMaxSize));
644
1056
  });
645
1057
 
646
1058
  out.push("");
@@ -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
@@ -12,6 +12,10 @@ Register-ArgumentCompleter -Native -CommandName 'make-folder-txt' -ScriptBlock {
12
12
  '--ignore-file', '-ifi',
13
13
  '--only-folder', '-ofo',
14
14
  '--only-file', '-ofi',
15
+ '--skip-large',
16
+ '--no-skip',
17
+ '--split-method',
18
+ '--split-size',
15
19
  '--copy',
16
20
  '--force',
17
21
  '--install-completion',
@@ -49,6 +53,30 @@ Register-ArgumentCompleter -Native -CommandName 'make-folder-txt' -ScriptBlock {
49
53
  return @()
50
54
  }
51
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
+ }
52
80
  default {
53
81
  # Complete with options
54
82
  if ($currentArgument -like '-*') {
@@ -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.1.0",
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.0.1",
3
+ "version": "2.1.1",
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": {