make-folder-txt 2.2.7 → 2.2.10

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.
@@ -6,65 +6,112 @@ const { version } = require("../package.json");
6
6
  const { execSync } = require("child_process");
7
7
 
8
8
  // ── config ────────────────────────────────────────────────────────────────────
9
- const IGNORE_DIRS = new Set(["node_modules", ".git", ".next", "dist", "build", ".cache"]);
10
- const IGNORE_FILES = new Set([".DS_Store", "Thumbs.db", "desktop.ini", ".txtignore"]);
9
+ const IGNORE_DIRS = new Set([
10
+ "node_modules",
11
+ ".git",
12
+ ".next",
13
+ "dist",
14
+ "build",
15
+ ".cache",
16
+ ]);
17
+ const IGNORE_FILES = new Set([
18
+ ".DS_Store",
19
+ "Thumbs.db",
20
+ "desktop.ini",
21
+ ".txtignore",
22
+ ]);
11
23
 
12
24
  const BINARY_EXTS = new Set([
13
- ".png", ".jpg", ".jpeg", ".gif", ".bmp", ".ico", ".svg", ".webp",
14
- ".pdf", ".zip", ".tar", ".gz", ".rar", ".7z",
15
- ".exe", ".dll", ".so", ".dylib", ".bin",
16
- ".mp3", ".mp4", ".wav", ".avi", ".mov",
17
- ".woff", ".woff2", ".ttf", ".eot", ".otf",
25
+ ".png",
26
+ ".jpg",
27
+ ".jpeg",
28
+ ".gif",
29
+ ".bmp",
30
+ ".ico",
31
+ ".svg",
32
+ ".webp",
33
+ ".pdf",
34
+ ".zip",
35
+ ".tar",
36
+ ".gz",
37
+ ".rar",
38
+ ".7z",
39
+ ".exe",
40
+ ".dll",
41
+ ".so",
42
+ ".dylib",
43
+ ".bin",
44
+ ".mp3",
45
+ ".mp4",
46
+ ".wav",
47
+ ".avi",
48
+ ".mov",
49
+ ".woff",
50
+ ".woff2",
51
+ ".ttf",
52
+ ".eot",
53
+ ".otf",
18
54
  ".lock",
19
55
  ]);
20
56
 
21
57
  // ── helpers ───────────────────────────────────────────────────────────────────
22
58
 
23
59
  function readTxtIgnore(rootDir) {
24
- const txtIgnorePath = path.join(rootDir, '.txtignore');
60
+ const txtIgnorePath = path.join(rootDir, ".txtignore");
25
61
  const ignorePatterns = new Set();
26
-
62
+
27
63
  try {
28
- const content = fs.readFileSync(txtIgnorePath, 'utf8');
29
- const lines = content.split('\n')
30
- .map(line => line.trim())
31
- .filter(line => line && !line.startsWith('#'));
32
-
33
- lines.forEach(line => ignorePatterns.add(line));
64
+ const content = fs.readFileSync(txtIgnorePath, "utf8");
65
+ const lines = content
66
+ .split("\n")
67
+ .map((line) => line.trim())
68
+ .filter((line) => line && !line.startsWith("#"));
69
+
70
+ lines.forEach((line) => ignorePatterns.add(line));
34
71
  } catch (err) {
35
72
  // .txtignore doesn't exist or can't be read - that's fine
36
73
  }
37
-
74
+
38
75
  return ignorePatterns;
39
76
  }
40
77
 
41
78
  function copyToClipboard(text) {
42
79
  try {
43
- if (process.platform === 'win32') {
80
+ if (process.platform === "win32") {
44
81
  // Windows - use PowerShell for better handling of large content
45
- const tempFile = require('os').tmpdir() + '\\make-folder-txt-clipboard-temp.txt';
46
- require('fs').writeFileSync(tempFile, text, 'utf8');
47
- execSync(`powershell -Command "Get-Content '${tempFile}' | Set-Clipboard"`, { stdio: 'ignore' });
48
- require('fs').unlinkSync(tempFile);
49
- } else if (process.platform === 'darwin') {
82
+ const tempFile =
83
+ require("os").tmpdir() + "\\make-folder-txt-clipboard-temp.txt";
84
+ require("fs").writeFileSync(tempFile, text, "utf8");
85
+ execSync(
86
+ `powershell -Command "Get-Content '${tempFile}' | Set-Clipboard"`,
87
+ { stdio: "ignore" },
88
+ );
89
+ require("fs").unlinkSync(tempFile);
90
+ } else if (process.platform === "darwin") {
50
91
  // macOS
51
- execSync(`echo ${JSON.stringify(text)} | pbcopy`, { stdio: 'ignore' });
92
+ execSync(`echo ${JSON.stringify(text)} | pbcopy`, { stdio: "ignore" });
52
93
  } else {
53
94
  // Linux (requires xclip or xsel)
54
95
  try {
55
- execSync(`echo ${JSON.stringify(text)} | xclip -selection clipboard`, { stdio: 'ignore' });
96
+ execSync(`echo ${JSON.stringify(text)} | xclip -selection clipboard`, {
97
+ stdio: "ignore",
98
+ });
56
99
  } catch {
57
100
  try {
58
- execSync(`echo ${JSON.stringify(text)} | xsel --clipboard --input`, { stdio: 'ignore' });
101
+ execSync(`echo ${JSON.stringify(text)} | xsel --clipboard --input`, {
102
+ stdio: "ignore",
103
+ });
59
104
  } catch {
60
- console.warn('⚠️ Could not copy to clipboard. Install xclip or xsel on Linux.');
105
+ console.warn(
106
+ "⚠️ Could not copy to clipboard. Install xclip or xsel on Linux.",
107
+ );
61
108
  return false;
62
109
  }
63
110
  }
64
111
  }
65
112
  return true;
66
113
  } catch (err) {
67
- console.warn('⚠️ Could not copy to clipboard: ' + err.message);
114
+ console.warn("⚠️ Could not copy to clipboard: " + err.message);
68
115
  return false;
69
116
  }
70
117
  }
@@ -98,7 +145,8 @@ function collectFiles(
98
145
  }
99
146
 
100
147
  entries.sort((a, b) => {
101
- if (a.isDirectory() === b.isDirectory()) return a.name.localeCompare(b.name);
148
+ if (a.isDirectory() === b.isDirectory())
149
+ return a.name.localeCompare(b.name);
102
150
  return a.isDirectory() ? -1 : 1;
103
151
  });
104
152
 
@@ -116,10 +164,20 @@ function collectFiles(
116
164
  }
117
165
 
118
166
  // Get relative path for .txtignore pattern matching
119
- const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
120
-
167
+ const relPathForIgnore = path
168
+ .relative(rootDir, path.join(dir, entry.name))
169
+ .split(path.sep)
170
+ .join("/");
171
+
121
172
  // Check against .txtignore patterns (both dirname and relative path) unless force is enabled
122
- if (!force && (txtIgnore.has(entry.name) || txtIgnore.has(`${entry.name}/`) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`${relPathForIgnore}/`) || txtIgnore.has(`/${relPathForIgnore}/`))) {
173
+ if (
174
+ !force &&
175
+ (txtIgnore.has(entry.name) ||
176
+ txtIgnore.has(`${entry.name}/`) ||
177
+ txtIgnore.has(relPathForIgnore) ||
178
+ txtIgnore.has(`${relPathForIgnore}/`) ||
179
+ txtIgnore.has(`/${relPathForIgnore}/`))
180
+ ) {
123
181
  if (!hasOnlyFilters) {
124
182
  lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
125
183
  }
@@ -127,14 +185,19 @@ function collectFiles(
127
185
  }
128
186
 
129
187
  const childPath = path.join(dir, entry.name);
130
- const childInSelectedFolder = inSelectedFolder || onlyFolders.has(entry.name);
188
+ // When rootOnlyInclude is active, a folder only "counts" if it's directly under root
189
+ const folderIsSelected = rootOnlyInclude
190
+ ? dir === rootDir && onlyFolders.has(entry.name)
191
+ : onlyFolders.has(entry.name);
192
+
193
+ const childInSelectedFolder = inSelectedFolder || folderIsSelected;
131
194
  const childLines = [];
132
195
  const childFiles = [];
133
-
134
- // If rootOnlyInclude is true, only include root-level items
196
+
197
+ // If rootOnlyInclude is true, skip recursing into non-selected subdirectories
135
198
  let child;
136
- if (rootOnlyInclude && dir !== rootDir) {
137
- // Don't recurse into subdirectories when rootOnlyInclude is true
199
+ if (rootOnlyInclude && dir !== rootDir && !inSelectedFolder) {
200
+ // Don't recurse unless already inside a selected folder
138
201
  child = { lines: [], filePaths: [], hasIncluded: false };
139
202
  } else {
140
203
  child = collectFiles(
@@ -158,8 +221,11 @@ function collectFiles(
158
221
  );
159
222
  }
160
223
 
161
- const explicitlySelectedFolder = hasOnlyFilters && onlyFolders.has(entry.name);
162
- const shouldIncludeDir = !hasOnlyFilters || (rootOnlyInclude ? false : child.hasIncluded) || explicitlySelectedFolder;
224
+ // Include the dir if: no filters active, or it's explicitly selected, or (non-root-only) a child matched
225
+ const shouldIncludeDir =
226
+ !hasOnlyFilters ||
227
+ folderIsSelected ||
228
+ (!rootOnlyInclude && child.hasIncluded);
163
229
 
164
230
  if (shouldIncludeDir) {
165
231
  lines.push(`${indent}${connector}${entry.name}/`);
@@ -170,48 +236,74 @@ function collectFiles(
170
236
  if (!force && ignoreFiles.has(entry.name)) return;
171
237
 
172
238
  // Get relative path for .txtignore pattern matching
173
- const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
174
-
239
+ const relPathForIgnore = path
240
+ .relative(rootDir, path.join(dir, entry.name))
241
+ .split(path.sep)
242
+ .join("/");
243
+
175
244
  // Check against .txtignore patterns (both filename and relative path) unless force is enabled
176
- if (!force && (txtIgnore.has(entry.name) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`/${relPathForIgnore}`))) {
245
+ if (
246
+ !force &&
247
+ (txtIgnore.has(entry.name) ||
248
+ txtIgnore.has(relPathForIgnore) ||
249
+ txtIgnore.has(`/${relPathForIgnore}`))
250
+ ) {
177
251
  return;
178
252
  }
179
253
 
180
254
  // Ignore .txt files that match the folder name (e.g., foldername.txt) unless force is enabled
181
- if (!force && entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
255
+ if (
256
+ !force &&
257
+ entry.name.endsWith(".txt") &&
258
+ entry.name === `${rootName}.txt`
259
+ )
260
+ return;
182
261
 
183
262
  // Check if this file matches any of the onlyFiles patterns
184
- const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder ||
185
- onlyFiles.has(entry.name) ||
186
- onlyFiles.has(relPathForIgnore) ||
263
+ const shouldIncludeFile =
264
+ !hasOnlyFilters ||
265
+ inSelectedFolder ||
266
+ onlyFiles.has(entry.name) ||
267
+ onlyFiles.has(relPathForIgnore) ||
187
268
  onlyFiles.has(`/${relPathForIgnore}`);
188
-
269
+
189
270
  if (!shouldIncludeFile) return;
190
271
 
191
272
  lines.push(`${indent}${connector}${entry.name}`);
192
- const relPath = "/" + path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
273
+ const relPath =
274
+ "/" +
275
+ path
276
+ .relative(rootDir, path.join(dir, entry.name))
277
+ .split(path.sep)
278
+ .join("/");
193
279
  filePaths.push({ abs: path.join(dir, entry.name), rel: relPath });
194
280
  }
195
281
  });
196
282
 
197
- return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
283
+ return {
284
+ lines,
285
+ filePaths,
286
+ hasIncluded: filePaths.length > 0 || lines.length > 0,
287
+ };
198
288
  }
199
289
 
200
290
  function parseFileSize(sizeStr) {
201
291
  const units = {
202
- 'B': 1,
203
- 'KB': 1024,
204
- 'MB': 1024 * 1024,
205
- 'GB': 1024 * 1024 * 1024,
206
- 'TB': 1024 * 1024 * 1024 * 1024
292
+ B: 1,
293
+ KB: 1024,
294
+ MB: 1024 * 1024,
295
+ GB: 1024 * 1024 * 1024,
296
+ TB: 1024 * 1024 * 1024 * 1024,
207
297
  };
208
-
298
+
209
299
  const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
210
300
  if (!match) {
211
- console.error(`Error: Invalid size format "${sizeStr}". Use format like "500KB", "2MB", "1GB".`);
301
+ console.error(
302
+ `Error: Invalid size format "${sizeStr}". Use format like "500KB", "2MB", "1GB".`,
303
+ );
212
304
  process.exit(1);
213
305
  }
214
-
306
+
215
307
  const value = parseFloat(match[1]);
216
308
  const unit = match[2].toUpperCase();
217
309
  return Math.floor(value * units[unit]);
@@ -223,10 +315,14 @@ function readContent(absPath, force = false, maxFileSize = 500 * 1024) {
223
315
  try {
224
316
  const stat = fs.statSync(absPath);
225
317
  if (!force && stat.size > maxFileSize) {
226
- const sizeStr = stat.size < 1024 ? `${stat.size} B` :
227
- stat.size < 1024 * 1024 ? `${(stat.size / 1024).toFixed(1)} KB` :
228
- stat.size < 1024 * 1024 * 1024 ? `${(stat.size / (1024 * 1024)).toFixed(1)} MB` :
229
- `${(stat.size / (1024 * 1024 * 1024)).toFixed(1)} GB`;
318
+ const sizeStr =
319
+ stat.size < 1024
320
+ ? `${stat.size} B`
321
+ : stat.size < 1024 * 1024
322
+ ? `${(stat.size / 1024).toFixed(1)} KB`
323
+ : stat.size < 1024 * 1024 * 1024
324
+ ? `${(stat.size / (1024 * 1024)).toFixed(1)} MB`
325
+ : `${(stat.size / (1024 * 1024 * 1024)).toFixed(1)} GB`;
230
326
  return `[file too large: ${sizeStr} – skipped]`;
231
327
  }
232
328
  return fs.readFileSync(absPath, "utf8");
@@ -235,51 +331,57 @@ function readContent(absPath, force = false, maxFileSize = 500 * 1024) {
235
331
  }
236
332
  }
237
333
 
238
- function splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag) {
334
+ function splitByFolders(
335
+ treeLines,
336
+ filePaths,
337
+ rootName,
338
+ effectiveMaxSize,
339
+ forceFlag,
340
+ ) {
239
341
  const folders = new Map();
240
-
342
+
241
343
  // Group files by folder
242
344
  filePaths.forEach(({ abs, rel }) => {
243
345
  const folderPath = path.dirname(rel);
244
- const folderKey = folderPath === '/' ? rootName : folderPath.slice(1);
245
-
346
+ const folderKey = folderPath === "/" ? rootName : folderPath.slice(1);
347
+
246
348
  if (!folders.has(folderKey)) {
247
349
  folders.set(folderKey, []);
248
350
  }
249
351
  folders.get(folderKey).push({ abs, rel });
250
352
  });
251
-
353
+
252
354
  const results = [];
253
-
355
+
254
356
  folders.forEach((files, folderName) => {
255
357
  const out = [];
256
358
  const divider = "=".repeat(80);
257
359
  const subDivider = "-".repeat(80);
258
-
360
+
259
361
  out.push(divider);
260
362
  out.push(`START OF FOLDER: ${folderName}`);
261
363
  out.push(divider);
262
364
  out.push("");
263
-
365
+
264
366
  // Add folder structure (only this folder's structure)
265
- const folderTreeLines = treeLines.filter(line =>
266
- line.includes(folderName + '/') || line === `${rootName}/`
367
+ const folderTreeLines = treeLines.filter(
368
+ (line) => line.includes(folderName + "/") || line === `${rootName}/`,
267
369
  );
268
-
370
+
269
371
  out.push(divider);
270
372
  out.push("PROJECT STRUCTURE");
271
373
  out.push(divider);
272
- out.push(`Root: ${folderPath}\n`);
374
+ out.push(`Root: ${process.cwd()}\n`);
273
375
  out.push(`${rootName}/`);
274
- folderTreeLines.forEach(l => out.push(l));
376
+ folderTreeLines.forEach((l) => out.push(l));
275
377
  out.push("");
276
378
  out.push(`Total files in this folder: ${files.length}`);
277
379
  out.push("");
278
-
380
+
279
381
  out.push(divider);
280
382
  out.push("FILE CONTENTS");
281
383
  out.push(divider);
282
-
384
+
283
385
  files.forEach(({ abs, rel }) => {
284
386
  out.push("");
285
387
  out.push(subDivider);
@@ -287,112 +389,113 @@ function splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceF
287
389
  out.push(subDivider);
288
390
  out.push(readContent(abs, forceFlag, effectiveMaxSize));
289
391
  });
290
-
392
+
291
393
  out.push("");
292
394
  out.push(divider);
293
395
  out.push(`END OF FOLDER: ${folderName}`);
294
396
  out.push(divider);
295
-
296
- const fileName = `${rootName}-${folderName.replace(/[\/\\]/g, '-')}.txt`;
397
+
398
+ const fileName = `${rootName}-${folderName.replace(/[\/\\]/g, "-")}.txt`;
297
399
  const filePath = path.join(process.cwd(), fileName);
298
-
400
+
299
401
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
300
402
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
301
-
403
+
302
404
  results.push({
303
405
  file: filePath,
304
406
  size: sizeKB,
305
407
  files: files.length,
306
- folder: folderName
408
+ folder: folderName,
307
409
  });
308
410
  });
309
-
411
+
310
412
  return results;
311
413
  }
312
414
 
313
415
  function splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag) {
314
416
  const results = [];
315
-
417
+
316
418
  filePaths.forEach(({ abs, rel }) => {
317
419
  const out = [];
318
420
  const divider = "=".repeat(80);
319
421
  const subDivider = "-".repeat(80);
320
422
  const fileName = path.basename(rel, path.extname(rel));
321
-
423
+
322
424
  out.push(divider);
323
425
  out.push(`FILE: ${rel}`);
324
426
  out.push(divider);
325
427
  out.push("");
326
-
428
+
327
429
  out.push(divider);
328
430
  out.push("FILE CONTENTS");
329
431
  out.push(divider);
330
432
  out.push(readContent(abs, forceFlag, effectiveMaxSize));
331
-
433
+
332
434
  out.push("");
333
435
  out.push(divider);
334
436
  out.push(`END OF FILE: ${rel}`);
335
437
  out.push(divider);
336
-
438
+
337
439
  const outputFileName = `${rootName}-${fileName}.txt`;
338
440
  const filePath = path.join(process.cwd(), outputFileName);
339
-
441
+
340
442
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
341
443
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
342
-
444
+
343
445
  results.push({
344
446
  file: filePath,
345
447
  size: sizeKB,
346
448
  files: 1,
347
- fileName: fileName
449
+ fileName: fileName,
348
450
  });
349
451
  });
350
-
452
+
351
453
  return results;
352
454
  }
353
455
 
354
- function splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag) {
456
+ function splitBySize(
457
+ treeLines,
458
+ filePaths,
459
+ rootName,
460
+ splitSize,
461
+ effectiveMaxSize,
462
+ forceFlag,
463
+ ) {
355
464
  const results = [];
356
465
  let currentPart = 1;
357
466
  let currentSize = 0;
358
467
  let currentFiles = [];
359
-
468
+
360
469
  const divider = "=".repeat(80);
361
470
  const subDivider = "-".repeat(80);
362
-
471
+
363
472
  // Start with header
364
473
  let out = [];
365
474
  out.push(divider);
366
475
  out.push(`START OF FOLDER: ${rootName} (Part ${currentPart})`);
367
476
  out.push(divider);
368
477
  out.push("");
369
-
478
+
370
479
  out.push(divider);
371
480
  out.push("PROJECT STRUCTURE");
372
481
  out.push(divider);
373
- out.push(`Root: ${folderPath}\n`);
482
+ out.push(`Root: ${process.cwd()}\n`);
374
483
  out.push(`${rootName}/`);
375
- treeLines.forEach(l => out.push(l));
484
+ treeLines.forEach((l) => out.push(l));
376
485
  out.push("");
377
486
  out.push(`Total files: ${filePaths.length}`);
378
487
  out.push("");
379
-
488
+
380
489
  out.push(divider);
381
490
  out.push("FILE CONTENTS");
382
491
  out.push(divider);
383
-
492
+
384
493
  filePaths.forEach(({ abs, rel }) => {
385
494
  const content = readContent(abs, forceFlag, effectiveMaxSize);
386
- const fileContent = [
387
- "",
388
- subDivider,
389
- `FILE: ${rel}`,
390
- subDivider,
391
- content
392
- ];
393
-
495
+ const fileContent = ["", subDivider, `FILE: ${rel}`, subDivider, content];
496
+
394
497
  const contentSize = fileContent.join("\n").length;
395
-
498
+
396
499
  // Check if adding this file would exceed the split size
397
500
  if (currentSize + contentSize > splitSize && currentFiles.length > 0) {
398
501
  // Finish current part
@@ -400,178 +503,203 @@ function splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize
400
503
  out.push(divider);
401
504
  out.push(`END OF FOLDER: ${rootName} (Part ${currentPart})`);
402
505
  out.push(divider);
403
-
506
+
404
507
  // Write current part
405
508
  const fileName = `${rootName}-part-${currentPart}.txt`;
406
509
  const filePath = path.join(process.cwd(), fileName);
407
510
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
408
511
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
409
-
512
+
410
513
  results.push({
411
514
  file: filePath,
412
515
  size: sizeKB,
413
516
  files: currentFiles.length,
414
- part: currentPart
517
+ part: currentPart,
415
518
  });
416
-
519
+
417
520
  // Start new part
418
521
  currentPart++;
419
522
  currentSize = 0;
420
523
  currentFiles = [];
421
-
524
+
422
525
  out = [];
423
526
  out.push(divider);
424
527
  out.push(`START OF FOLDER: ${rootName} (Part ${currentPart})`);
425
528
  out.push(divider);
426
529
  out.push("");
427
-
530
+
428
531
  out.push(divider);
429
532
  out.push("PROJECT STRUCTURE");
430
533
  out.push(divider);
431
- out.push(`Root: ${folderPath}\n`);
534
+ out.push(`Root: ${process.cwd()}\n`);
432
535
  out.push(`${rootName}/`);
433
- treeLines.forEach(l => out.push(l));
536
+ treeLines.forEach((l) => out.push(l));
434
537
  out.push("");
435
538
  out.push(`Total files: ${filePaths.length}`);
436
539
  out.push("");
437
-
540
+
438
541
  out.push(divider);
439
542
  out.push("FILE CONTENTS");
440
543
  out.push(divider);
441
544
  }
442
-
545
+
443
546
  // Add file to current part
444
547
  out.push(...fileContent);
445
548
  currentSize += contentSize;
446
549
  currentFiles.push(rel);
447
550
  });
448
-
551
+
449
552
  // Write final part
450
553
  out.push("");
451
554
  out.push(divider);
452
555
  out.push(`END OF FOLDER: ${rootName} (Part ${currentPart})`);
453
556
  out.push(divider);
454
-
557
+
455
558
  const fileName = `${rootName}-part-${currentPart}.txt`;
456
559
  const filePath = path.join(process.cwd(), fileName);
457
560
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
458
561
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
459
-
562
+
460
563
  results.push({
461
564
  file: filePath,
462
565
  size: sizeKB,
463
566
  files: currentFiles.length,
464
- part: currentPart
567
+ part: currentPart,
465
568
  });
466
-
569
+
467
570
  return results;
468
571
  }
469
572
 
470
573
  // ── configuration ────────────────────────────────────────────────────────────────
471
574
 
472
575
  function createInteractiveConfig() {
473
- const readline = require('readline');
474
- const fs = require('fs');
475
- const path = require('path');
476
- const os = require('os');
477
-
576
+ const readline = require("readline");
577
+ const fs = require("fs");
578
+ const path = require("path");
579
+ const os = require("os");
580
+
478
581
  const rl = readline.createInterface({
479
582
  input: process.stdin,
480
- output: process.stdout
583
+ output: process.stdout,
481
584
  });
482
585
 
483
- console.log('\n🔧 make-folder-txt Configuration Setup');
484
- console.log('=====================================\n');
586
+ console.log("\n🔧 make-folder-txt Configuration Setup");
587
+ console.log("=====================================\n");
485
588
 
486
589
  return new Promise((resolve) => {
487
590
  const config = {
488
- maxFileSize: '500KB',
489
- splitMethod: 'none',
490
- splitSize: '5MB',
491
- copyToClipboard: false
591
+ maxFileSize: "500KB",
592
+ splitMethod: "none",
593
+ splitSize: "5MB",
594
+ copyToClipboard: false,
492
595
  };
493
596
 
494
597
  let currentStep = 0;
495
598
  const questions = [
496
599
  {
497
- key: 'maxFileSize',
498
- question: 'Maximum file size to include (e.g., 500KB, 2MB, 1GB): ',
499
- default: '500KB',
600
+ key: "maxFileSize",
601
+ question: "Maximum file size to include (e.g., 500KB, 2MB, 1GB): ",
602
+ default: "500KB",
500
603
  validate: (value) => {
501
604
  if (!value.trim()) return true;
502
- const validUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
605
+ const validUnits = ["B", "KB", "MB", "GB", "TB"];
503
606
  const match = value.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
504
- if (!match) return 'Please enter a valid size (e.g., 500KB, 2MB, 1GB)';
505
- if (!validUnits.includes(match[2].toUpperCase())) return `Invalid unit. Use: ${validUnits.join(', ')}`;
607
+ if (!match)
608
+ return "Please enter a valid size (e.g., 500KB, 2MB, 1GB)";
609
+ if (!validUnits.includes(match[2].toUpperCase()))
610
+ return `Invalid unit. Use: ${validUnits.join(", ")}`;
506
611
  return true;
507
- }
612
+ },
508
613
  },
509
614
  {
510
- key: 'splitMethod',
511
- question: 'Split output method (none, folder, file, size): ',
512
- default: 'none',
615
+ key: "splitMethod",
616
+ question: "Split output method (none, folder, file, size): ",
617
+ default: "none",
513
618
  validate: (value) => {
514
- const validMethods = ['none', 'folder', 'file', 'size'];
515
- if (!validMethods.includes(value.toLowerCase())) return `Please choose: ${validMethods.join(', ')}`;
619
+ const validMethods = ["none", "folder", "file", "size"];
620
+ if (!validMethods.includes(value.toLowerCase()))
621
+ return `Please choose: ${validMethods.join(", ")}`;
516
622
  return true;
517
- }
623
+ },
518
624
  },
519
625
  {
520
- key: 'splitSize',
521
- question: 'Split size when using size method (e.g., 5MB, 10MB): ',
522
- default: '5MB',
523
- ask: () => config.splitMethod === 'size',
626
+ key: "splitSize",
627
+ question: "Split size when using size method (e.g., 5MB, 10MB): ",
628
+ default: "5MB",
629
+ ask: () => config.splitMethod === "size",
524
630
  validate: (value) => {
525
631
  if (!value.trim()) return true;
526
- const validUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
632
+ const validUnits = ["B", "KB", "MB", "GB", "TB"];
527
633
  const match = value.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
528
- if (!match) return 'Please enter a valid size (e.g., 5MB, 10MB)';
529
- if (!validUnits.includes(match[2].toUpperCase())) return `Invalid unit. Use: ${validUnits.join(', ')}`;
634
+ if (!match) return "Please enter a valid size (e.g., 5MB, 10MB)";
635
+ if (!validUnits.includes(match[2].toUpperCase()))
636
+ return `Invalid unit. Use: ${validUnits.join(", ")}`;
530
637
  return true;
531
- }
638
+ },
532
639
  },
533
640
  {
534
- key: 'copyToClipboard',
535
- question: 'Copy to clipboard automatically? (y/n): ',
536
- default: 'n',
641
+ key: "copyToClipboard",
642
+ question: "Copy to clipboard automatically? (y/n): ",
643
+ default: "n",
537
644
  validate: (value) => {
538
645
  const answer = value.toLowerCase();
539
- if (!['y', 'n', 'yes', 'no'].includes(answer)) return 'Please enter y/n or yes/no';
646
+ if (!["y", "n", "yes", "no"].includes(answer))
647
+ return "Please enter y/n or yes/no";
540
648
  return true;
541
649
  },
542
- transform: (value) => ['y', 'yes'].includes(value.toLowerCase())
650
+ transform: (value) => ["y", "yes"].includes(value.toLowerCase()),
543
651
  },
544
652
  {
545
- key: 'addToTxtIgnore',
546
- question: 'Add ignore patterns to .txtignore file? (y/n): ',
547
- default: 'n',
653
+ key: "addToTxtIgnore",
654
+ question: "Add ignore patterns to .txtignore file? (y/n): ",
655
+ default: "n",
548
656
  validate: (value) => {
549
657
  const answer = value.toLowerCase();
550
- if (!['y', 'n', 'yes', 'no'].includes(answer)) return 'Please enter y/n or yes/no';
658
+ if (!["y", "n", "yes", "no"].includes(answer))
659
+ return "Please enter y/n or yes/no";
551
660
  return true;
552
661
  },
553
- transform: (value) => ['y', 'yes'].includes(value.toLowerCase())
662
+ transform: (value) => ["y", "yes"].includes(value.toLowerCase()),
554
663
  },
555
664
  {
556
- key: 'ignoreFolders',
557
- question: 'Ignore folders (comma-separated, or press Enter to skip): ',
558
- default: '',
665
+ key: "ignoreFolders",
666
+ question: "Ignore folders (comma-separated, or press Enter to skip): ",
667
+ default: "",
559
668
  ask: () => config.addToTxtIgnore,
560
669
  transform: (value) => {
561
- if (!value || value.trim() === '') return [];
562
- return value.split(',').map(f => f.trim()).filter(f => f);
563
- }
670
+ if (!value || value.trim() === "") return [];
671
+ return value
672
+ .split(",")
673
+ .map((f) => f.trim())
674
+ .filter((f) => f);
675
+ },
564
676
  },
565
677
  {
566
- key: 'ignoreFiles',
567
- question: 'Ignore files (comma-separated, or press Enter to skip): ',
568
- default: '',
678
+ key: "ignoreFiles",
679
+ question: "Ignore files (comma-separated, or press Enter to skip): ",
680
+ default: "",
569
681
  ask: () => config.addToTxtIgnore,
570
682
  transform: (value) => {
571
- if (!value || value.trim() === '') return [];
572
- return value.split(',').map(f => f.trim()).filter(f => f);
573
- }
574
- }
683
+ if (!value || value.trim() === "") return [];
684
+ return value
685
+ .split(",")
686
+ .map((f) => f.trim())
687
+ .filter((f) => f);
688
+ },
689
+ },
690
+ {
691
+ key: "addToGitignore",
692
+ question:
693
+ "Add .txtconfig, .txtignore, and <folder>.txt to .gitignore? (y/n): ",
694
+ default: "n",
695
+ validate: (value) => {
696
+ const answer = value.toLowerCase();
697
+ if (!["y", "n", "yes", "no"].includes(answer))
698
+ return "Please enter y/n or yes/no";
699
+ return true;
700
+ },
701
+ transform: (value) => ["y", "yes"].includes(value.toLowerCase()),
702
+ },
575
703
  ];
576
704
 
577
705
  function askQuestion() {
@@ -582,7 +710,7 @@ function createInteractiveConfig() {
582
710
  }
583
711
 
584
712
  const q = questions[currentStep];
585
-
713
+
586
714
  // Skip if conditional ask returns false
587
715
  if (q.ask && !q.ask()) {
588
716
  currentStep++;
@@ -590,36 +718,40 @@ function createInteractiveConfig() {
590
718
  return;
591
719
  }
592
720
 
593
- const defaultValue = typeof q.default === 'function' ? q.default() : q.default;
594
- rl.question(q.question + (defaultValue ? `(${defaultValue}) ` : ''), (answer) => {
595
- const value = answer.trim() || defaultValue;
596
-
597
- // Validate input
598
- if (q.validate) {
599
- const validation = q.validate(value);
600
- if (validation !== true) {
601
- console.log(`❌ ${validation}`);
602
- askQuestion();
603
- return;
721
+ const defaultValue =
722
+ typeof q.default === "function" ? q.default() : q.default;
723
+ rl.question(
724
+ q.question + (defaultValue ? `(${defaultValue}) ` : ""),
725
+ (answer) => {
726
+ const value = answer.trim() || defaultValue;
727
+
728
+ // Validate input
729
+ if (q.validate) {
730
+ const validation = q.validate(value);
731
+ if (validation !== true) {
732
+ console.log(`❌ ${validation}`);
733
+ askQuestion();
734
+ return;
735
+ }
604
736
  }
605
- }
606
737
 
607
- // Transform value if needed
608
- if (q.transform) {
609
- config[q.key] = q.transform(value);
610
- } else {
611
- config[q.key] = value;
612
- }
738
+ // Transform value if needed
739
+ if (q.transform) {
740
+ config[q.key] = q.transform(value);
741
+ } else {
742
+ config[q.key] = value;
743
+ }
613
744
 
614
- currentStep++;
615
- askQuestion();
616
- });
745
+ currentStep++;
746
+ askQuestion();
747
+ },
748
+ );
617
749
  }
618
750
 
619
751
  function saveConfig() {
620
752
  try {
621
753
  // Create .txtconfig file with proper formatting
622
- const configPath = path.join(process.cwd(), '.txtconfig');
754
+ const configPath = path.join(process.cwd(), ".txtconfig");
623
755
  const configContent = `{
624
756
  "maxFileSize": "${config.maxFileSize}",
625
757
  "splitMethod": "${config.splitMethod}",
@@ -630,39 +762,81 @@ function createInteractiveConfig() {
630
762
  fs.writeFileSync(configPath, configContent);
631
763
 
632
764
  // Update .txtignore if user wants to add ignore patterns
633
- if (config.addToTxtIgnore && (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)) {
634
- const ignorePath = path.join(process.cwd(), '.txtignore');
635
- let ignoreContent = '';
636
-
765
+ if (
766
+ config.addToTxtIgnore &&
767
+ (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)
768
+ ) {
769
+ const ignorePath = path.join(process.cwd(), ".txtignore");
770
+ let ignoreContent = "";
771
+
637
772
  // Read existing content if file exists
638
773
  if (fs.existsSync(ignorePath)) {
639
- ignoreContent = fs.readFileSync(ignorePath, 'utf8');
640
- if (!ignoreContent.endsWith('\n')) ignoreContent += '\n';
774
+ ignoreContent = fs.readFileSync(ignorePath, "utf8");
775
+ if (!ignoreContent.endsWith("\n")) ignoreContent += "\n";
641
776
  }
642
777
 
643
778
  // Add new ignore patterns
644
779
  const newPatterns = [
645
- ...config.ignoreFolders.map(f => f.endsWith('/') ? f : `${f}/`),
646
- ...config.ignoreFiles
780
+ ...config.ignoreFolders.map((f) => (f.endsWith("/") ? f : `${f}/`)),
781
+ ...config.ignoreFiles,
647
782
  ];
648
783
 
649
784
  if (newPatterns.length > 0) {
650
- ignoreContent += '\n# Added by make-folder-txt config\n';
651
- ignoreContent += newPatterns.join('\n') + '\n';
785
+ ignoreContent += "\n# Added by make-folder-txt config\n";
786
+ ignoreContent += newPatterns.join("\n") + "\n";
652
787
  fs.writeFileSync(ignorePath, ignoreContent);
653
788
  }
654
789
  }
655
790
 
656
- console.log('\n✅ Configuration saved successfully!');
791
+ // Update .gitignore if user requested
792
+ if (config.addToGitignore) {
793
+ const gitignorePath = path.join(process.cwd(), ".gitignore");
794
+ const rootFolderName = path.basename(process.cwd());
795
+ const entriesToAdd = [
796
+ ".txtconfig",
797
+ ".txtignore",
798
+ `${rootFolderName}.txt`,
799
+ ];
800
+
801
+ let gitignoreContent = "";
802
+ if (fs.existsSync(gitignorePath)) {
803
+ gitignoreContent = fs.readFileSync(gitignorePath, "utf8");
804
+ if (!gitignoreContent.endsWith("\n")) gitignoreContent += "\n";
805
+ }
806
+
807
+ // Only add entries that aren't already present
808
+ const newEntries = entriesToAdd.filter((entry) => {
809
+ const lines = gitignoreContent.split("\n").map((l) => l.trim());
810
+ return !lines.includes(entry);
811
+ });
812
+
813
+ if (newEntries.length > 0) {
814
+ gitignoreContent += "\n# make-folder-txt\n";
815
+ gitignoreContent += newEntries.join("\n") + "\n";
816
+ fs.writeFileSync(gitignorePath, gitignoreContent);
817
+ console.log(`\n🙈 Added to .gitignore: ${newEntries.join(", ")}`);
818
+ } else {
819
+ console.log(
820
+ `\nℹ️ .gitignore entries already present — nothing added`,
821
+ );
822
+ }
823
+ }
824
+
825
+ console.log("\n✅ Configuration saved successfully!");
657
826
  console.log(`📄 Config file: ${configPath}`);
658
- if (config.addToTxtIgnore && (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)) {
827
+ if (
828
+ config.addToTxtIgnore &&
829
+ (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)
830
+ ) {
659
831
  console.log(`📝 Ignore patterns added to .txtignore`);
660
832
  }
661
- console.log('\n💡 Your settings will now be used automatically!');
662
- console.log('🔄 Run --delete-config to reset to defaults');
663
-
833
+ if (config.addToGitignore) {
834
+ console.log(`🙈 .gitignore updated`);
835
+ }
836
+ console.log("\n💡 Your settings will now be used automatically!");
837
+ console.log("🔄 Run --delete-config to reset to defaults");
664
838
  } catch (err) {
665
- console.error('❌ Error saving configuration:', err.message);
839
+ console.error("❌ Error saving configuration:", err.message);
666
840
  } finally {
667
841
  rl.close();
668
842
  resolve();
@@ -674,20 +848,20 @@ function createInteractiveConfig() {
674
848
  }
675
849
 
676
850
  function loadConfig() {
677
- const fs = require('fs');
678
- const path = require('path');
679
-
851
+ const fs = require("fs");
852
+ const path = require("path");
853
+
680
854
  try {
681
- const configPath = path.join(process.cwd(), '.txtconfig');
855
+ const configPath = path.join(process.cwd(), ".txtconfig");
682
856
  if (!fs.existsSync(configPath)) {
683
- console.error('❌ .txtconfig file not found. Run --make-config first.');
857
+ console.error("❌ .txtconfig file not found. Run --make-config first.");
684
858
  process.exit(1);
685
859
  }
686
-
687
- const configContent = fs.readFileSync(configPath, 'utf8');
860
+
861
+ const configContent = fs.readFileSync(configPath, "utf8");
688
862
  return JSON.parse(configContent);
689
863
  } catch (err) {
690
- console.error('❌ Error loading configuration:', err.message);
864
+ console.error("❌ Error loading configuration:", err.message);
691
865
  process.exit(1);
692
866
  }
693
867
  }
@@ -710,55 +884,62 @@ async function main() {
710
884
 
711
885
  if (args.includes("--delete-config")) {
712
886
  try {
713
- const fs = require('fs');
714
- const path = require('path');
715
- const configPath = path.join(process.cwd(), '.txtconfig');
716
-
887
+ const fs = require("fs");
888
+ const path = require("path");
889
+ const configPath = path.join(process.cwd(), ".txtconfig");
890
+
717
891
  if (fs.existsSync(configPath)) {
718
892
  fs.unlinkSync(configPath);
719
- console.log('✅ Configuration file deleted successfully!');
720
- console.log('🔄 Tool will now use default settings');
893
+ console.log("✅ Configuration file deleted successfully!");
894
+ console.log("🔄 Tool will now use default settings");
721
895
  } else {
722
- console.log('ℹ️ No configuration file found - already using defaults');
896
+ console.log("ℹ️ No configuration file found - already using defaults");
723
897
  }
724
898
  } catch (err) {
725
- console.error('❌ Error deleting configuration:', err.message);
899
+ console.error("❌ Error deleting configuration:", err.message);
726
900
  }
727
901
  process.exit(0);
728
902
  }
729
903
 
730
904
  // Auto-use config if it exists and no other flags are provided
731
- const configPath = path.join(process.cwd(), '.txtconfig');
905
+ const configPath = path.join(process.cwd(), ".txtconfig");
732
906
  const hasConfig = fs.existsSync(configPath);
733
- const hasOtherFlags = args.length > 0 && !args.includes('--help') && !args.includes('-h') && !args.includes('--version') && !args.includes('-v');
734
-
907
+ const hasOtherFlags =
908
+ args.length > 0 &&
909
+ !args.includes("--help") &&
910
+ !args.includes("-h") &&
911
+ !args.includes("--version") &&
912
+ !args.includes("-v");
913
+
735
914
  if (hasConfig && !hasOtherFlags) {
736
915
  const config = loadConfig();
737
-
916
+
738
917
  // Apply config to command line arguments
739
918
  const newArgs = [];
740
-
919
+
741
920
  // Add max file size if not default
742
- if (config.maxFileSize !== '500KB') {
743
- newArgs.push('--skip-large', config.maxFileSize);
921
+ if (config.maxFileSize !== "500KB") {
922
+ newArgs.push("--skip-large", config.maxFileSize);
744
923
  }
745
-
924
+
746
925
  // Add split method if not none
747
- if (config.splitMethod !== 'none') {
748
- newArgs.push('--split-method', config.splitMethod);
749
- if (config.splitMethod === 'size') {
750
- newArgs.push('--split-size', config.splitSize);
926
+ if (config.splitMethod !== "none") {
927
+ newArgs.push("--split-method", config.splitMethod);
928
+ if (config.splitMethod === "size") {
929
+ newArgs.push("--split-size", config.splitSize);
751
930
  }
752
931
  }
753
-
932
+
754
933
  // Add copy to clipboard if true
755
934
  if (config.copyToClipboard) {
756
- newArgs.push('--copy');
935
+ newArgs.push("--copy");
757
936
  }
758
-
937
+
759
938
  // Replace args with config-based args
760
939
  args.splice(0, args.length, ...newArgs);
761
- console.log('🔧 Using saved configuration (use --delete-config to reset to defaults)');
940
+ console.log(
941
+ "🔧 Using saved configuration (use --delete-config to reset to defaults)",
942
+ );
762
943
  }
763
944
 
764
945
  if (args.includes("--help") || args.includes("-h")) {
@@ -780,7 +961,7 @@ Dump an entire project folder into a single readable .txt file.
780
961
  --split-size <size> Split output when size exceeds limit (requires --split-method size)
781
962
  --copy Copy output to clipboard
782
963
  --force Include everything (overrides all ignore patterns)
783
- --make-config Create interactive configuration
964
+ --make-config Create interactive configuration (with optional .gitignore setup)
784
965
  --delete-config Delete configuration and reset to defaults
785
966
  --help, -h Show this help message
786
967
  --version, -v Show version information
@@ -817,411 +998,439 @@ Dump an entire project folder into a single readable .txt file.
817
998
  coverage/
818
999
  LICENSE
819
1000
  `);
820
- process.exit(0);
1001
+ process.exit(0);
1002
+ }
1003
+
1004
+ const ignoreDirs = new Set(IGNORE_DIRS);
1005
+ const ignoreFiles = new Set(IGNORE_FILES);
1006
+ const onlyFolders = new Set();
1007
+ const onlyFiles = new Set();
1008
+ let outputArg = null;
1009
+ let copyToClipboardFlag = false;
1010
+ let forceFlag = false;
1011
+ let maxFileSize = 500 * 1024; // Default 500KB
1012
+ let noSkipFlag = false;
1013
+ let splitMethod = null; // 'folder', 'file', 'size'
1014
+ let splitSize = null; // size in bytes
1015
+ let rootOnlyInclude = false; // For /include flag
1016
+
1017
+ for (let i = 0; i < args.length; i += 1) {
1018
+ const arg = args[i];
1019
+
1020
+ if (arg === "--copy") {
1021
+ copyToClipboardFlag = true;
1022
+ continue;
821
1023
  }
822
1024
 
823
- const ignoreDirs = new Set(IGNORE_DIRS);
824
- const ignoreFiles = new Set(IGNORE_FILES);
825
- const onlyFolders = new Set();
826
- const onlyFiles = new Set();
827
- let outputArg = null;
828
- let copyToClipboardFlag = false;
829
- let forceFlag = false;
830
- let maxFileSize = 500 * 1024; // Default 500KB
831
- let noSkipFlag = false;
832
- let splitMethod = null; // 'folder', 'file', 'size'
833
- let splitSize = null; // size in bytes
834
- let rootOnlyInclude = false; // For /include flag
835
-
836
- for (let i = 0; i < args.length; i += 1) {
837
- const arg = args[i];
838
-
839
- if (arg === "--copy") {
840
- copyToClipboardFlag = true;
841
- continue;
842
- }
1025
+ if (arg === "--force") {
1026
+ forceFlag = true;
1027
+ continue;
1028
+ }
843
1029
 
844
- if (arg === "--force") {
845
- forceFlag = true;
846
- continue;
847
- }
1030
+ if (arg === "--no-skip") {
1031
+ noSkipFlag = true;
1032
+ continue;
1033
+ }
848
1034
 
849
- if (arg === "--no-skip") {
850
- noSkipFlag = true;
851
- continue;
1035
+ if (arg === "--skip-large") {
1036
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
1037
+ console.error(
1038
+ "Error: --skip-large requires a size value (e.g., 400KB, 5GB).",
1039
+ );
1040
+ process.exit(1);
852
1041
  }
1042
+ maxFileSize = parseFileSize(args[i + 1]);
1043
+ i += 1;
1044
+ continue;
1045
+ }
853
1046
 
854
- if (arg === "--skip-large") {
855
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
856
- console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
857
- process.exit(1);
858
- }
859
- maxFileSize = parseFileSize(args[i + 1]);
860
- i += 1;
861
- continue;
1047
+ if (arg.startsWith("--skip-large=")) {
1048
+ const value = arg.slice("--skip-large=".length);
1049
+ if (!value) {
1050
+ console.error(
1051
+ "Error: --skip-large requires a size value (e.g., 400KB, 5GB).",
1052
+ );
1053
+ process.exit(1);
862
1054
  }
1055
+ maxFileSize = parseFileSize(value);
1056
+ continue;
1057
+ }
863
1058
 
864
- if (arg.startsWith("--skip-large=")) {
865
- const value = arg.slice("--skip-large=".length);
866
- if (!value) {
867
- console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
868
- process.exit(1);
869
- }
870
- maxFileSize = parseFileSize(value);
871
- continue;
1059
+ if (arg === "--split-method") {
1060
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
1061
+ console.error(
1062
+ "Error: --split-method requires a method (folder, file, or size).",
1063
+ );
1064
+ process.exit(1);
872
1065
  }
873
-
874
- if (arg === "--split-method") {
875
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
876
- console.error("Error: --split-method requires a method (folder, file, or size).");
877
- process.exit(1);
878
- }
879
- const method = args[i + 1].toLowerCase();
880
- if (!['folder', 'file', 'size'].includes(method)) {
881
- console.error("Error: --split-method must be one of: folder, file, size");
882
- process.exit(1);
883
- }
884
- splitMethod = method;
885
- i += 1;
886
- continue;
1066
+ const method = args[i + 1].toLowerCase();
1067
+ if (!["folder", "file", "size"].includes(method)) {
1068
+ console.error(
1069
+ "Error: --split-method must be one of: folder, file, size",
1070
+ );
1071
+ process.exit(1);
887
1072
  }
1073
+ splitMethod = method;
1074
+ i += 1;
1075
+ continue;
1076
+ }
888
1077
 
889
- if (arg.startsWith("--split-method=")) {
890
- const value = arg.slice("--split-method=".length);
891
- if (!value) {
892
- console.error("Error: --split-method requires a method (folder, file, or size).");
893
- process.exit(1);
894
- }
895
- const method = value.toLowerCase();
896
- if (!['folder', 'file', 'size'].includes(method)) {
897
- console.error("Error: --split-method must be one of: folder, file, size");
898
- process.exit(1);
899
- }
900
- splitMethod = method;
901
- continue;
1078
+ if (arg.startsWith("--split-method=")) {
1079
+ const value = arg.slice("--split-method=".length);
1080
+ if (!value) {
1081
+ console.error(
1082
+ "Error: --split-method requires a method (folder, file, or size).",
1083
+ );
1084
+ process.exit(1);
902
1085
  }
903
-
904
- if (arg === "--split-size") {
905
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
906
- console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
907
- process.exit(1);
908
- }
909
- splitSize = parseFileSize(args[i + 1]);
910
- i += 1;
911
- continue;
1086
+ const method = value.toLowerCase();
1087
+ if (!["folder", "file", "size"].includes(method)) {
1088
+ console.error(
1089
+ "Error: --split-method must be one of: folder, file, size",
1090
+ );
1091
+ process.exit(1);
912
1092
  }
1093
+ splitMethod = method;
1094
+ continue;
1095
+ }
913
1096
 
914
- if (arg.startsWith("--split-size=")) {
915
- const value = arg.slice("--split-size=".length);
916
- if (!value) {
917
- console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
918
- process.exit(1);
919
- }
920
- splitSize = parseFileSize(value);
921
- continue;
1097
+ if (arg === "--split-size") {
1098
+ if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
1099
+ console.error(
1100
+ "Error: --split-size requires a size value (e.g., 5MB, 10MB).",
1101
+ );
1102
+ process.exit(1);
922
1103
  }
1104
+ splitSize = parseFileSize(args[i + 1]);
1105
+ i += 1;
1106
+ continue;
1107
+ }
923
1108
 
924
- if (arg === "--ignore-folder" || arg === "-ifo") {
925
- let consumed = 0;
926
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
927
- let folderName = args[i + 1];
928
-
929
- // Check for /folder syntax (root-only include)
930
- if (folderName.startsWith("/")) {
931
- const cleanFolderName = folderName.slice(1); // Remove leading /
932
- onlyFolders.add(cleanFolderName);
933
- rootOnlyInclude = true;
934
- i += 1;
935
- consumed += 1;
936
- } else {
937
- // Normal folder path (could be nested like folder/subfolder)
938
- onlyFolders.add(folderName);
939
- i += 1;
940
- consumed += 1;
941
- }
942
- }
943
- if (consumed === 0) {
944
- console.error("Error: --ignore-folder requires at least one folder name.");
945
- process.exit(1);
946
- }
947
- continue;
1109
+ if (arg.startsWith("--split-size=")) {
1110
+ const value = arg.slice("--split-size=".length);
1111
+ if (!value) {
1112
+ console.error(
1113
+ "Error: --split-size requires a size value (e.g., 5MB, 10MB).",
1114
+ );
1115
+ process.exit(1);
948
1116
  }
1117
+ splitSize = parseFileSize(value);
1118
+ continue;
1119
+ }
949
1120
 
950
- if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
951
- const value = arg.startsWith("--ignore-folder=")
952
- ? arg.slice("--ignore-folder=".length)
953
- : arg.slice("-ifo=".length);
954
- if (!value) {
955
- console.error("Error: --ignore-folder requires a folder name.");
956
- process.exit(1);
957
- }
958
-
959
- // Check for /folder syntax (root-only include)
960
- if (value.startsWith("/")) {
961
- const cleanFolderName = value.slice(1); // Remove leading /
962
- onlyFolders.add(cleanFolderName);
963
- rootOnlyInclude = true;
964
- } else {
965
- // Normal folder path (could be nested like folder/subfolder)
966
- onlyFolders.add(value);
967
- }
968
- continue;
1121
+ if (arg === "--ignore-folder" || arg === "-ifo") {
1122
+ let consumed = 0;
1123
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1124
+ let folderName = args[i + 1];
1125
+ folderName = folderName.replace(/\\/g, "/"); // Convert backslashes to forward slashes
1126
+ folderName = folderName.replace(/\/+$/, ""); // Remove trailing slashes
1127
+ ignoreDirs.add(folderName);
1128
+ i += 1;
1129
+ consumed += 1;
1130
+ }
1131
+ if (consumed === 0) {
1132
+ console.error(
1133
+ "Error: --ignore-folder requires at least one folder name.",
1134
+ );
1135
+ process.exit(1);
969
1136
  }
1137
+ continue;
1138
+ }
970
1139
 
971
- if (arg === "--ignore-file" || arg === "-ifi") {
972
- let consumed = 0;
973
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
974
- ignoreFiles.add(args[i + 1]);
975
- i += 1;
976
- consumed += 1;
977
- }
978
- if (consumed === 0) {
979
- console.error("Error: --ignore-file requires at least one file name.");
980
- process.exit(1);
981
- }
982
- continue;
1140
+ if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
1141
+ const value = arg.startsWith("--ignore-folder=")
1142
+ ? arg.slice("--ignore-folder=".length)
1143
+ : arg.slice("-ifo=".length);
1144
+ if (!value) {
1145
+ console.error("Error: --ignore-folder requires a folder name.");
1146
+ process.exit(1);
983
1147
  }
1148
+ let folderName = value.replace(/\\/g, "/").replace(/\/+$/, "");
1149
+ ignoreDirs.add(folderName);
1150
+ continue;
1151
+ }
984
1152
 
985
- if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
986
- const value = arg.startsWith("--ignore-file=")
987
- ? arg.slice("--ignore-file=".length)
988
- : arg.slice("-ifi=".length);
989
- if (!value) {
990
- console.error("Error: --ignore-file requires a file name.");
991
- process.exit(1);
992
- }
993
- ignoreFiles.add(value);
994
- continue;
1153
+ if (arg === "--ignore-file" || arg === "-ifi") {
1154
+ let consumed = 0;
1155
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1156
+ ignoreFiles.add(args[i + 1]);
1157
+ i += 1;
1158
+ consumed += 1;
1159
+ }
1160
+ if (consumed === 0) {
1161
+ console.error("Error: --ignore-file requires at least one file name.");
1162
+ process.exit(1);
995
1163
  }
1164
+ continue;
1165
+ }
996
1166
 
997
- if (arg === "--only-folder" || arg === "-ofo") {
998
- let consumed = 0;
999
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1000
- // Normalize the folder name
1001
- let folderName = args[i + 1];
1002
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
1003
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
1004
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
1005
- onlyFolders.add(folderName);
1006
- i += 1;
1007
- consumed += 1;
1008
- }
1009
- if (consumed === 0) {
1010
- console.error("Error: --only-folder requires at least one folder name.");
1011
- process.exit(1);
1012
- }
1013
- continue;
1167
+ if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
1168
+ const value = arg.startsWith("--ignore-file=")
1169
+ ? arg.slice("--ignore-file=".length)
1170
+ : arg.slice("-ifi=".length);
1171
+ if (!value) {
1172
+ console.error("Error: --ignore-file requires a file name.");
1173
+ process.exit(1);
1014
1174
  }
1175
+ ignoreFiles.add(value);
1176
+ continue;
1177
+ }
1015
1178
 
1016
- if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
1017
- const value = arg.startsWith("--only-folder=")
1018
- ? arg.slice("--only-folder=".length)
1019
- : arg.slice("-ofo=".length);
1020
- if (!value) {
1021
- console.error("Error: --only-folder requires a folder name.");
1022
- process.exit(1);
1179
+ if (arg === "--only-folder" || arg === "-ofo") {
1180
+ let consumed = 0;
1181
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1182
+ let folderName = args[i + 1];
1183
+ folderName = folderName.replace(/\\/g, "/"); // Convert backslashes to forward slashes
1184
+ // Detect leading / as root-only signal (same as --only-file behavior)
1185
+ if (folderName.startsWith("/")) {
1186
+ rootOnlyInclude = true;
1023
1187
  }
1024
- // Normalize the folder name
1025
- let folderName = value;
1026
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
1027
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
1028
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
1188
+ folderName = folderName.replace(/^\.?\//, ""); // Remove leading ./ or /
1189
+ folderName = folderName.replace(/\/+$/, ""); // Remove trailing slashes
1029
1190
  onlyFolders.add(folderName);
1030
- continue;
1191
+ i += 1;
1192
+ consumed += 1;
1193
+ }
1194
+ if (consumed === 0) {
1195
+ console.error(
1196
+ "Error: --only-folder requires at least one folder name.",
1197
+ );
1198
+ process.exit(1);
1031
1199
  }
1200
+ continue;
1201
+ }
1032
1202
 
1033
- if (arg === "--only-file" || arg === "-ofi") {
1034
- let consumed = 0;
1035
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1036
- let fileName = args[i + 1];
1037
-
1038
- // Check for /file syntax (root-only include)
1039
- if (fileName.startsWith("/")) {
1040
- const cleanFileName = fileName.slice(1); // Remove leading /
1041
- onlyFiles.add(cleanFileName);
1042
- rootOnlyInclude = true;
1043
- i += 1;
1044
- consumed += 1;
1045
- } else {
1046
- // Normal file path (could be nested like folder/file.ext)
1047
- onlyFiles.add(fileName);
1048
- i += 1;
1049
- consumed += 1;
1050
- }
1051
- }
1052
- if (consumed === 0) {
1053
- console.error("Error: --only-file requires at least one file name.");
1054
- process.exit(1);
1055
- }
1056
- continue;
1203
+ if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
1204
+ const value = arg.startsWith("--only-folder=")
1205
+ ? arg.slice("--only-folder=".length)
1206
+ : arg.slice("-ofo=".length);
1207
+ if (!value) {
1208
+ console.error("Error: --only-folder requires a folder name.");
1209
+ process.exit(1);
1057
1210
  }
1211
+ let folderName = value;
1212
+ folderName = folderName.replace(/\\/g, "/"); // Convert backslashes to forward slashes
1213
+ // Detect leading / as root-only signal
1214
+ if (folderName.startsWith("/")) {
1215
+ rootOnlyInclude = true;
1216
+ }
1217
+ folderName = folderName.replace(/^\.?\//, ""); // Remove leading ./ or /
1218
+ folderName = folderName.replace(/\/+$/, ""); // Remove trailing slashes
1219
+ onlyFolders.add(folderName);
1220
+ continue;
1221
+ }
1222
+
1223
+ if (arg === "--only-file" || arg === "-ofi") {
1224
+ let consumed = 0;
1225
+ while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1226
+ let fileName = args[i + 1];
1058
1227
 
1059
- if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
1060
- const value = arg.startsWith("--only-file=")
1061
- ? arg.slice("--only-file=".length)
1062
- : arg.slice("-ofi=".length);
1063
- if (!value) {
1064
- console.error("Error: --only-file requires a file name.");
1065
- process.exit(1);
1066
- }
1067
-
1068
1228
  // Check for /file syntax (root-only include)
1069
- if (value.startsWith("/")) {
1070
- const cleanFileName = value.slice(1); // Remove leading /
1229
+ if (fileName.startsWith("/")) {
1230
+ const cleanFileName = fileName.slice(1); // Remove leading /
1071
1231
  onlyFiles.add(cleanFileName);
1072
1232
  rootOnlyInclude = true;
1233
+ i += 1;
1234
+ consumed += 1;
1073
1235
  } else {
1074
1236
  // Normal file path (could be nested like folder/file.ext)
1075
- onlyFiles.add(value);
1237
+ onlyFiles.add(fileName);
1238
+ i += 1;
1239
+ consumed += 1;
1076
1240
  }
1077
- continue;
1078
1241
  }
1079
-
1080
- // Unknown argument
1081
- console.error(`Error: Unknown option "${arg}"`);
1082
- console.error("Use --help for available options.");
1083
- process.exit(1);
1242
+ if (consumed === 0) {
1243
+ console.error("Error: --only-file requires at least one file name.");
1244
+ process.exit(1);
1245
+ }
1246
+ continue;
1084
1247
  }
1085
1248
 
1086
- // Validate split options
1087
- if (splitMethod === 'size' && !splitSize) {
1088
- console.error("Error: --split-method size requires --split-size to be specified");
1089
- process.exit(1);
1090
- }
1249
+ if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
1250
+ const value = arg.startsWith("--only-file=")
1251
+ ? arg.slice("--only-file=".length)
1252
+ : arg.slice("-ofi=".length);
1253
+ if (!value) {
1254
+ console.error("Error: --only-file requires a file name.");
1255
+ process.exit(1);
1256
+ }
1091
1257
 
1092
- if (splitSize && splitMethod !== 'size') {
1093
- console.error("Error: --split-size can only be used with --split-method size");
1094
- process.exit(1);
1258
+ // Check for /file syntax (root-only include)
1259
+ if (value.startsWith("/")) {
1260
+ const cleanFileName = value.slice(1); // Remove leading /
1261
+ onlyFiles.add(cleanFileName);
1262
+ rootOnlyInclude = true;
1263
+ } else {
1264
+ // Normal file path (could be nested like folder/file.ext)
1265
+ onlyFiles.add(value);
1266
+ }
1267
+ continue;
1095
1268
  }
1096
1269
 
1097
- // ── config ────────────────────────────────────────────────────────────────────────
1098
-
1099
- const folderPath = process.cwd();
1100
- const rootName = path.basename(folderPath);
1101
- const txtIgnore = readTxtIgnore(folderPath);
1102
-
1103
- // ── build tree & collect file paths ───────────────────────────────────────────────
1104
-
1105
- const { lines: treeLines, filePaths } = collectFiles(
1106
- folderPath,
1107
- folderPath,
1108
- ignoreDirs,
1109
- ignoreFiles,
1110
- onlyFolders,
1111
- onlyFiles,
1112
- {
1113
- rootName,
1114
- txtIgnore,
1115
- force: forceFlag,
1116
- hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0,
1117
- rootOnlyInclude
1118
- }
1119
- );
1270
+ // Unknown argument
1271
+ console.error(`Error: Unknown option "${arg}"`);
1272
+ console.error("Use --help for available options.");
1273
+ process.exit(1);
1274
+ }
1120
1275
 
1121
- // ── build output filename ───────────────────────────────────────────────────────────
1276
+ // Validate split options
1277
+ if (splitMethod === "size" && !splitSize) {
1278
+ console.error(
1279
+ "Error: --split-method size requires --split-size to be specified",
1280
+ );
1281
+ process.exit(1);
1282
+ }
1122
1283
 
1123
- let outputFile = outputArg || path.join(folderPath, `${rootName}.txt`);
1284
+ if (splitSize && splitMethod !== "size") {
1285
+ console.error(
1286
+ "Error: --split-size can only be used with --split-method size",
1287
+ );
1288
+ process.exit(1);
1289
+ }
1124
1290
 
1125
- // ── handle output splitting ─────────────────────────────────────────────────────────
1291
+ // ── config ────────────────────────────────────────────────────────────────────────
1292
+
1293
+ const folderPath = process.cwd();
1294
+ const rootName = path.basename(folderPath);
1295
+ const txtIgnore = readTxtIgnore(folderPath);
1296
+
1297
+ // ── build tree & collect file paths ───────────────────────────────────────────────
1298
+
1299
+ const { lines: treeLines, filePaths } = collectFiles(
1300
+ folderPath,
1301
+ folderPath,
1302
+ ignoreDirs,
1303
+ ignoreFiles,
1304
+ onlyFolders,
1305
+ onlyFiles,
1306
+ {
1307
+ rootName,
1308
+ txtIgnore,
1309
+ force: forceFlag,
1310
+ hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0,
1311
+ rootOnlyInclude,
1312
+ },
1313
+ );
1314
+
1315
+ // ── build output filename ───────────────────────────────────────────────────────────
1316
+
1317
+ let outputFile = outputArg || path.join(folderPath, `${rootName}.txt`);
1318
+
1319
+ // ── handle output splitting ─────────────────────────────────────────────────────────
1320
+
1321
+ const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
1322
+
1323
+ if (splitMethod) {
1324
+ console.log(`🔧 Splitting output by: ${splitMethod}`);
1325
+
1326
+ let results;
1327
+
1328
+ if (splitMethod === "folder") {
1329
+ results = splitByFolders(
1330
+ treeLines,
1331
+ filePaths,
1332
+ rootName,
1333
+ effectiveMaxSize,
1334
+ forceFlag,
1335
+ );
1336
+ } else if (splitMethod === "file") {
1337
+ results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
1338
+ } else if (splitMethod === "size") {
1339
+ results = splitBySize(
1340
+ treeLines,
1341
+ filePaths,
1342
+ rootName,
1343
+ splitSize,
1344
+ effectiveMaxSize,
1345
+ forceFlag,
1346
+ );
1347
+ }
1126
1348
 
1127
- const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
1349
+ console.log(`✅ Done! Created ${results.length} split files:`);
1350
+ console.log("");
1128
1351
 
1129
- if (splitMethod) {
1130
- console.log(`🔧 Splitting output by: ${splitMethod}`);
1131
-
1132
- let results;
1133
-
1134
- if (splitMethod === 'folder') {
1135
- results = splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag);
1136
- } else if (splitMethod === 'file') {
1137
- results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
1138
- } else if (splitMethod === 'size') {
1139
- results = splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag);
1352
+ results.forEach((result, index) => {
1353
+ if (splitMethod === "folder") {
1354
+ console.log(`📁 Folder: ${result.folder}`);
1355
+ } else if (splitMethod === "file") {
1356
+ console.log(`📄 File: ${result.fileName}`);
1357
+ } else if (splitMethod === "size") {
1358
+ console.log(`📦 Part ${result.part}`);
1140
1359
  }
1141
-
1142
- console.log(`✅ Done! Created ${results.length} split files:`);
1143
- console.log('');
1144
-
1145
- results.forEach((result, index) => {
1146
- if (splitMethod === 'folder') {
1147
- console.log(`📁 Folder: ${result.folder}`);
1148
- } else if (splitMethod === 'file') {
1149
- console.log(`📄 File: ${result.fileName}`);
1150
- } else if (splitMethod === 'size') {
1151
- console.log(`📦 Part ${result.part}`);
1152
- }
1153
- console.log(`📄 Output : ${result.file}`);
1154
- console.log(`📊 Size : ${result.size} KB`);
1155
- console.log(`🗂️ Files : ${result.files}`);
1156
- console.log('');
1157
- });
1158
-
1159
- if (copyToClipboardFlag) {
1160
- console.log('⚠️ --copy flag is not compatible with splitting - clipboard copy skipped');
1161
- }
1162
-
1163
- process.exit(0);
1360
+ console.log(`📄 Output : ${result.file}`);
1361
+ console.log(`📊 Size : ${result.size} KB`);
1362
+ console.log(`🗂️ Files : ${result.files}`);
1363
+ console.log("");
1364
+ });
1365
+
1366
+ if (copyToClipboardFlag) {
1367
+ console.log(
1368
+ "⚠️ --copy flag is not compatible with splitting - clipboard copy skipped",
1369
+ );
1164
1370
  }
1165
1371
 
1166
- // ── build output (no splitting) ───────────────────────────────────────────────────
1167
- const out = [];
1168
- const divider = "=".repeat(80);
1169
- const subDivider = "-".repeat(80);
1372
+ process.exit(0);
1373
+ }
1170
1374
 
1171
- out.push(divider);
1172
- out.push(`START OF FOLDER: ${rootName}`);
1173
- out.push(divider);
1174
- out.push("");
1375
+ // ── build output (no splitting) ───────────────────────────────────────────────────
1376
+ const out = [];
1377
+ const divider = "=".repeat(80);
1378
+ const subDivider = "-".repeat(80);
1175
1379
 
1176
- out.push(divider);
1177
- out.push("PROJECT STRUCTURE");
1178
- out.push(divider);
1179
- out.push(`Root: ${folderPath}\n`);
1380
+ out.push(divider);
1381
+ out.push(`START OF FOLDER: ${rootName}`);
1382
+ out.push(divider);
1383
+ out.push("");
1180
1384
 
1181
- out.push(`${rootName}/`);
1182
- treeLines.forEach(l => out.push(l));
1183
- out.push("");
1184
- out.push(`Total files: ${filePaths.length}`);
1185
- out.push("");
1385
+ out.push(divider);
1386
+ out.push("PROJECT STRUCTURE");
1387
+ out.push(divider);
1388
+ out.push(`Root: ${folderPath}\n`);
1186
1389
 
1187
- out.push(divider);
1188
- out.push("FILE CONTENTS");
1189
- out.push(divider);
1390
+ out.push(`${rootName}/`);
1391
+ treeLines.forEach((l) => out.push(l));
1392
+ out.push("");
1393
+ out.push(`Total files: ${filePaths.length}`);
1394
+ out.push("");
1190
1395
 
1191
- filePaths.forEach(({ abs, rel }) => {
1192
- out.push("");
1193
- out.push(subDivider);
1194
- out.push(`FILE: ${rel}`);
1195
- out.push(subDivider);
1196
- out.push(readContent(abs, forceFlag, effectiveMaxSize));
1197
- });
1396
+ out.push(divider);
1397
+ out.push("FILE CONTENTS");
1398
+ out.push(divider);
1198
1399
 
1400
+ filePaths.forEach(({ abs, rel }) => {
1199
1401
  out.push("");
1200
- out.push(divider);
1201
- out.push(`END OF FOLDER: ${rootName}`);
1202
- out.push(divider);
1402
+ out.push(subDivider);
1403
+ out.push(`FILE: ${rel}`);
1404
+ out.push(subDivider);
1405
+ out.push(readContent(abs, forceFlag, effectiveMaxSize));
1406
+ });
1407
+
1408
+ out.push("");
1409
+ out.push(divider);
1410
+ out.push(`END OF FOLDER: ${rootName}`);
1411
+ out.push(divider);
1203
1412
 
1204
- fs.writeFileSync(outputFile, out.join("\n"), "utf8");
1413
+ fs.writeFileSync(outputFile, out.join("\n"), "utf8");
1205
1414
 
1206
- const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
1207
- console.log(`✅ Done!`);
1208
- console.log(`📄 Output : ${outputFile}`);
1209
- console.log(`📊 Size : ${sizeKB} KB`);
1210
- console.log(`🗂️ Files : ${filePaths.length}`);
1415
+ const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
1416
+ console.log(`✅ Done!`);
1417
+ console.log(`📄 Output : ${outputFile}`);
1418
+ console.log(`📊 Size : ${sizeKB} KB`);
1419
+ console.log(`🗂️ Files : ${filePaths.length}`);
1211
1420
 
1212
- if (copyToClipboardFlag) {
1213
- const content = fs.readFileSync(outputFile, 'utf8');
1214
- const success = copyToClipboard(content);
1215
- if (success) {
1216
- console.log(`📋 Copied to clipboard!`);
1217
- }
1421
+ if (copyToClipboardFlag) {
1422
+ const content = fs.readFileSync(outputFile, "utf8");
1423
+ const success = copyToClipboard(content);
1424
+ if (success) {
1425
+ console.log(`📋 Copied to clipboard!`);
1218
1426
  }
1427
+ }
1219
1428
 
1220
- console.log('');
1429
+ console.log("");
1221
1430
  }
1222
1431
 
1223
1432
  // Run the main function
1224
- main().catch(err => {
1225
- console.error('❌ Error:', err.message);
1433
+ main().catch((err) => {
1434
+ console.error("❌ Error:", err.message);
1226
1435
  process.exit(1);
1227
1436
  });