make-folder-txt 2.2.9 → 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.
Files changed (2) hide show
  1. package/bin/make-folder-txt.js +788 -592
  2. package/package.json +1 -1
@@ -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
  }
@@ -129,13 +187,13 @@ function collectFiles(
129
187
  const childPath = path.join(dir, entry.name);
130
188
  // When rootOnlyInclude is active, a folder only "counts" if it's directly under root
131
189
  const folderIsSelected = rootOnlyInclude
132
- ? (dir === rootDir && onlyFolders.has(entry.name))
190
+ ? dir === rootDir && onlyFolders.has(entry.name)
133
191
  : onlyFolders.has(entry.name);
134
192
 
135
193
  const childInSelectedFolder = inSelectedFolder || folderIsSelected;
136
194
  const childLines = [];
137
195
  const childFiles = [];
138
-
196
+
139
197
  // If rootOnlyInclude is true, skip recursing into non-selected subdirectories
140
198
  let child;
141
199
  if (rootOnlyInclude && dir !== rootDir && !inSelectedFolder) {
@@ -164,8 +222,9 @@ function collectFiles(
164
222
  }
165
223
 
166
224
  // Include the dir if: no filters active, or it's explicitly selected, or (non-root-only) a child matched
167
- const shouldIncludeDir = !hasOnlyFilters ||
168
- folderIsSelected ||
225
+ const shouldIncludeDir =
226
+ !hasOnlyFilters ||
227
+ folderIsSelected ||
169
228
  (!rootOnlyInclude && child.hasIncluded);
170
229
 
171
230
  if (shouldIncludeDir) {
@@ -177,48 +236,74 @@ function collectFiles(
177
236
  if (!force && ignoreFiles.has(entry.name)) return;
178
237
 
179
238
  // Get relative path for .txtignore pattern matching
180
- const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
181
-
239
+ const relPathForIgnore = path
240
+ .relative(rootDir, path.join(dir, entry.name))
241
+ .split(path.sep)
242
+ .join("/");
243
+
182
244
  // Check against .txtignore patterns (both filename and relative path) unless force is enabled
183
- 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
+ ) {
184
251
  return;
185
252
  }
186
253
 
187
254
  // Ignore .txt files that match the folder name (e.g., foldername.txt) unless force is enabled
188
- 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;
189
261
 
190
262
  // Check if this file matches any of the onlyFiles patterns
191
- const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder ||
192
- onlyFiles.has(entry.name) ||
193
- onlyFiles.has(relPathForIgnore) ||
263
+ const shouldIncludeFile =
264
+ !hasOnlyFilters ||
265
+ inSelectedFolder ||
266
+ onlyFiles.has(entry.name) ||
267
+ onlyFiles.has(relPathForIgnore) ||
194
268
  onlyFiles.has(`/${relPathForIgnore}`);
195
-
269
+
196
270
  if (!shouldIncludeFile) return;
197
271
 
198
272
  lines.push(`${indent}${connector}${entry.name}`);
199
- 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("/");
200
279
  filePaths.push({ abs: path.join(dir, entry.name), rel: relPath });
201
280
  }
202
281
  });
203
282
 
204
- 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
+ };
205
288
  }
206
289
 
207
290
  function parseFileSize(sizeStr) {
208
291
  const units = {
209
- 'B': 1,
210
- 'KB': 1024,
211
- 'MB': 1024 * 1024,
212
- 'GB': 1024 * 1024 * 1024,
213
- '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,
214
297
  };
215
-
298
+
216
299
  const match = sizeStr.match(/^(\d+(?:\.\d+)?)\s*(B|KB|MB|GB|TB)$/i);
217
300
  if (!match) {
218
- 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
+ );
219
304
  process.exit(1);
220
305
  }
221
-
306
+
222
307
  const value = parseFloat(match[1]);
223
308
  const unit = match[2].toUpperCase();
224
309
  return Math.floor(value * units[unit]);
@@ -230,10 +315,14 @@ function readContent(absPath, force = false, maxFileSize = 500 * 1024) {
230
315
  try {
231
316
  const stat = fs.statSync(absPath);
232
317
  if (!force && stat.size > maxFileSize) {
233
- const sizeStr = stat.size < 1024 ? `${stat.size} B` :
234
- stat.size < 1024 * 1024 ? `${(stat.size / 1024).toFixed(1)} KB` :
235
- stat.size < 1024 * 1024 * 1024 ? `${(stat.size / (1024 * 1024)).toFixed(1)} MB` :
236
- `${(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`;
237
326
  return `[file too large: ${sizeStr} – skipped]`;
238
327
  }
239
328
  return fs.readFileSync(absPath, "utf8");
@@ -242,51 +331,57 @@ function readContent(absPath, force = false, maxFileSize = 500 * 1024) {
242
331
  }
243
332
  }
244
333
 
245
- function splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag) {
334
+ function splitByFolders(
335
+ treeLines,
336
+ filePaths,
337
+ rootName,
338
+ effectiveMaxSize,
339
+ forceFlag,
340
+ ) {
246
341
  const folders = new Map();
247
-
342
+
248
343
  // Group files by folder
249
344
  filePaths.forEach(({ abs, rel }) => {
250
345
  const folderPath = path.dirname(rel);
251
- const folderKey = folderPath === '/' ? rootName : folderPath.slice(1);
252
-
346
+ const folderKey = folderPath === "/" ? rootName : folderPath.slice(1);
347
+
253
348
  if (!folders.has(folderKey)) {
254
349
  folders.set(folderKey, []);
255
350
  }
256
351
  folders.get(folderKey).push({ abs, rel });
257
352
  });
258
-
353
+
259
354
  const results = [];
260
-
355
+
261
356
  folders.forEach((files, folderName) => {
262
357
  const out = [];
263
358
  const divider = "=".repeat(80);
264
359
  const subDivider = "-".repeat(80);
265
-
360
+
266
361
  out.push(divider);
267
362
  out.push(`START OF FOLDER: ${folderName}`);
268
363
  out.push(divider);
269
364
  out.push("");
270
-
365
+
271
366
  // Add folder structure (only this folder's structure)
272
- const folderTreeLines = treeLines.filter(line =>
273
- line.includes(folderName + '/') || line === `${rootName}/`
367
+ const folderTreeLines = treeLines.filter(
368
+ (line) => line.includes(folderName + "/") || line === `${rootName}/`,
274
369
  );
275
-
370
+
276
371
  out.push(divider);
277
372
  out.push("PROJECT STRUCTURE");
278
373
  out.push(divider);
279
- out.push(`Root: ${folderPath}\n`);
374
+ out.push(`Root: ${process.cwd()}\n`);
280
375
  out.push(`${rootName}/`);
281
- folderTreeLines.forEach(l => out.push(l));
376
+ folderTreeLines.forEach((l) => out.push(l));
282
377
  out.push("");
283
378
  out.push(`Total files in this folder: ${files.length}`);
284
379
  out.push("");
285
-
380
+
286
381
  out.push(divider);
287
382
  out.push("FILE CONTENTS");
288
383
  out.push(divider);
289
-
384
+
290
385
  files.forEach(({ abs, rel }) => {
291
386
  out.push("");
292
387
  out.push(subDivider);
@@ -294,112 +389,113 @@ function splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceF
294
389
  out.push(subDivider);
295
390
  out.push(readContent(abs, forceFlag, effectiveMaxSize));
296
391
  });
297
-
392
+
298
393
  out.push("");
299
394
  out.push(divider);
300
395
  out.push(`END OF FOLDER: ${folderName}`);
301
396
  out.push(divider);
302
-
303
- const fileName = `${rootName}-${folderName.replace(/[\/\\]/g, '-')}.txt`;
397
+
398
+ const fileName = `${rootName}-${folderName.replace(/[\/\\]/g, "-")}.txt`;
304
399
  const filePath = path.join(process.cwd(), fileName);
305
-
400
+
306
401
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
307
402
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
308
-
403
+
309
404
  results.push({
310
405
  file: filePath,
311
406
  size: sizeKB,
312
407
  files: files.length,
313
- folder: folderName
408
+ folder: folderName,
314
409
  });
315
410
  });
316
-
411
+
317
412
  return results;
318
413
  }
319
414
 
320
415
  function splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag) {
321
416
  const results = [];
322
-
417
+
323
418
  filePaths.forEach(({ abs, rel }) => {
324
419
  const out = [];
325
420
  const divider = "=".repeat(80);
326
421
  const subDivider = "-".repeat(80);
327
422
  const fileName = path.basename(rel, path.extname(rel));
328
-
423
+
329
424
  out.push(divider);
330
425
  out.push(`FILE: ${rel}`);
331
426
  out.push(divider);
332
427
  out.push("");
333
-
428
+
334
429
  out.push(divider);
335
430
  out.push("FILE CONTENTS");
336
431
  out.push(divider);
337
432
  out.push(readContent(abs, forceFlag, effectiveMaxSize));
338
-
433
+
339
434
  out.push("");
340
435
  out.push(divider);
341
436
  out.push(`END OF FILE: ${rel}`);
342
437
  out.push(divider);
343
-
438
+
344
439
  const outputFileName = `${rootName}-${fileName}.txt`;
345
440
  const filePath = path.join(process.cwd(), outputFileName);
346
-
441
+
347
442
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
348
443
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
349
-
444
+
350
445
  results.push({
351
446
  file: filePath,
352
447
  size: sizeKB,
353
448
  files: 1,
354
- fileName: fileName
449
+ fileName: fileName,
355
450
  });
356
451
  });
357
-
452
+
358
453
  return results;
359
454
  }
360
455
 
361
- function splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize, forceFlag) {
456
+ function splitBySize(
457
+ treeLines,
458
+ filePaths,
459
+ rootName,
460
+ splitSize,
461
+ effectiveMaxSize,
462
+ forceFlag,
463
+ ) {
362
464
  const results = [];
363
465
  let currentPart = 1;
364
466
  let currentSize = 0;
365
467
  let currentFiles = [];
366
-
468
+
367
469
  const divider = "=".repeat(80);
368
470
  const subDivider = "-".repeat(80);
369
-
471
+
370
472
  // Start with header
371
473
  let out = [];
372
474
  out.push(divider);
373
475
  out.push(`START OF FOLDER: ${rootName} (Part ${currentPart})`);
374
476
  out.push(divider);
375
477
  out.push("");
376
-
478
+
377
479
  out.push(divider);
378
480
  out.push("PROJECT STRUCTURE");
379
481
  out.push(divider);
380
- out.push(`Root: ${folderPath}\n`);
482
+ out.push(`Root: ${process.cwd()}\n`);
381
483
  out.push(`${rootName}/`);
382
- treeLines.forEach(l => out.push(l));
484
+ treeLines.forEach((l) => out.push(l));
383
485
  out.push("");
384
486
  out.push(`Total files: ${filePaths.length}`);
385
487
  out.push("");
386
-
488
+
387
489
  out.push(divider);
388
490
  out.push("FILE CONTENTS");
389
491
  out.push(divider);
390
-
492
+
391
493
  filePaths.forEach(({ abs, rel }) => {
392
494
  const content = readContent(abs, forceFlag, effectiveMaxSize);
393
- const fileContent = [
394
- "",
395
- subDivider,
396
- `FILE: ${rel}`,
397
- subDivider,
398
- content
399
- ];
400
-
495
+ const fileContent = ["", subDivider, `FILE: ${rel}`, subDivider, content];
496
+
401
497
  const contentSize = fileContent.join("\n").length;
402
-
498
+
403
499
  // Check if adding this file would exceed the split size
404
500
  if (currentSize + contentSize > splitSize && currentFiles.length > 0) {
405
501
  // Finish current part
@@ -407,178 +503,203 @@ function splitBySize(treeLines, filePaths, rootName, splitSize, effectiveMaxSize
407
503
  out.push(divider);
408
504
  out.push(`END OF FOLDER: ${rootName} (Part ${currentPart})`);
409
505
  out.push(divider);
410
-
506
+
411
507
  // Write current part
412
508
  const fileName = `${rootName}-part-${currentPart}.txt`;
413
509
  const filePath = path.join(process.cwd(), fileName);
414
510
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
415
511
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
416
-
512
+
417
513
  results.push({
418
514
  file: filePath,
419
515
  size: sizeKB,
420
516
  files: currentFiles.length,
421
- part: currentPart
517
+ part: currentPart,
422
518
  });
423
-
519
+
424
520
  // Start new part
425
521
  currentPart++;
426
522
  currentSize = 0;
427
523
  currentFiles = [];
428
-
524
+
429
525
  out = [];
430
526
  out.push(divider);
431
527
  out.push(`START OF FOLDER: ${rootName} (Part ${currentPart})`);
432
528
  out.push(divider);
433
529
  out.push("");
434
-
530
+
435
531
  out.push(divider);
436
532
  out.push("PROJECT STRUCTURE");
437
533
  out.push(divider);
438
- out.push(`Root: ${folderPath}\n`);
534
+ out.push(`Root: ${process.cwd()}\n`);
439
535
  out.push(`${rootName}/`);
440
- treeLines.forEach(l => out.push(l));
536
+ treeLines.forEach((l) => out.push(l));
441
537
  out.push("");
442
538
  out.push(`Total files: ${filePaths.length}`);
443
539
  out.push("");
444
-
540
+
445
541
  out.push(divider);
446
542
  out.push("FILE CONTENTS");
447
543
  out.push(divider);
448
544
  }
449
-
545
+
450
546
  // Add file to current part
451
547
  out.push(...fileContent);
452
548
  currentSize += contentSize;
453
549
  currentFiles.push(rel);
454
550
  });
455
-
551
+
456
552
  // Write final part
457
553
  out.push("");
458
554
  out.push(divider);
459
555
  out.push(`END OF FOLDER: ${rootName} (Part ${currentPart})`);
460
556
  out.push(divider);
461
-
557
+
462
558
  const fileName = `${rootName}-part-${currentPart}.txt`;
463
559
  const filePath = path.join(process.cwd(), fileName);
464
560
  fs.writeFileSync(filePath, out.join("\n"), "utf8");
465
561
  const sizeKB = (fs.statSync(filePath).size / 1024).toFixed(1);
466
-
562
+
467
563
  results.push({
468
564
  file: filePath,
469
565
  size: sizeKB,
470
566
  files: currentFiles.length,
471
- part: currentPart
567
+ part: currentPart,
472
568
  });
473
-
569
+
474
570
  return results;
475
571
  }
476
572
 
477
573
  // ── configuration ────────────────────────────────────────────────────────────────
478
574
 
479
575
  function createInteractiveConfig() {
480
- const readline = require('readline');
481
- const fs = require('fs');
482
- const path = require('path');
483
- const os = require('os');
484
-
576
+ const readline = require("readline");
577
+ const fs = require("fs");
578
+ const path = require("path");
579
+ const os = require("os");
580
+
485
581
  const rl = readline.createInterface({
486
582
  input: process.stdin,
487
- output: process.stdout
583
+ output: process.stdout,
488
584
  });
489
585
 
490
- console.log('\n🔧 make-folder-txt Configuration Setup');
491
- console.log('=====================================\n');
586
+ console.log("\n🔧 make-folder-txt Configuration Setup");
587
+ console.log("=====================================\n");
492
588
 
493
589
  return new Promise((resolve) => {
494
590
  const config = {
495
- maxFileSize: '500KB',
496
- splitMethod: 'none',
497
- splitSize: '5MB',
498
- copyToClipboard: false
591
+ maxFileSize: "500KB",
592
+ splitMethod: "none",
593
+ splitSize: "5MB",
594
+ copyToClipboard: false,
499
595
  };
500
596
 
501
597
  let currentStep = 0;
502
598
  const questions = [
503
599
  {
504
- key: 'maxFileSize',
505
- question: 'Maximum file size to include (e.g., 500KB, 2MB, 1GB): ',
506
- default: '500KB',
600
+ key: "maxFileSize",
601
+ question: "Maximum file size to include (e.g., 500KB, 2MB, 1GB): ",
602
+ default: "500KB",
507
603
  validate: (value) => {
508
604
  if (!value.trim()) return true;
509
- const validUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
605
+ const validUnits = ["B", "KB", "MB", "GB", "TB"];
510
606
  const match = value.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
511
- if (!match) return 'Please enter a valid size (e.g., 500KB, 2MB, 1GB)';
512
- 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(", ")}`;
513
611
  return true;
514
- }
612
+ },
515
613
  },
516
614
  {
517
- key: 'splitMethod',
518
- question: 'Split output method (none, folder, file, size): ',
519
- default: 'none',
615
+ key: "splitMethod",
616
+ question: "Split output method (none, folder, file, size): ",
617
+ default: "none",
520
618
  validate: (value) => {
521
- const validMethods = ['none', 'folder', 'file', 'size'];
522
- 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(", ")}`;
523
622
  return true;
524
- }
623
+ },
525
624
  },
526
625
  {
527
- key: 'splitSize',
528
- question: 'Split size when using size method (e.g., 5MB, 10MB): ',
529
- default: '5MB',
530
- 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",
531
630
  validate: (value) => {
532
631
  if (!value.trim()) return true;
533
- const validUnits = ['B', 'KB', 'MB', 'GB', 'TB'];
632
+ const validUnits = ["B", "KB", "MB", "GB", "TB"];
534
633
  const match = value.match(/^(\d+(?:\.\d+)?)\s*([A-Z]+)$/i);
535
- if (!match) return 'Please enter a valid size (e.g., 5MB, 10MB)';
536
- 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(", ")}`;
537
637
  return true;
538
- }
638
+ },
539
639
  },
540
640
  {
541
- key: 'copyToClipboard',
542
- question: 'Copy to clipboard automatically? (y/n): ',
543
- default: 'n',
641
+ key: "copyToClipboard",
642
+ question: "Copy to clipboard automatically? (y/n): ",
643
+ default: "n",
544
644
  validate: (value) => {
545
645
  const answer = value.toLowerCase();
546
- 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";
547
648
  return true;
548
649
  },
549
- transform: (value) => ['y', 'yes'].includes(value.toLowerCase())
650
+ transform: (value) => ["y", "yes"].includes(value.toLowerCase()),
550
651
  },
551
652
  {
552
- key: 'addToTxtIgnore',
553
- question: 'Add ignore patterns to .txtignore file? (y/n): ',
554
- default: 'n',
653
+ key: "addToTxtIgnore",
654
+ question: "Add ignore patterns to .txtignore file? (y/n): ",
655
+ default: "n",
555
656
  validate: (value) => {
556
657
  const answer = value.toLowerCase();
557
- 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";
558
660
  return true;
559
661
  },
560
- transform: (value) => ['y', 'yes'].includes(value.toLowerCase())
662
+ transform: (value) => ["y", "yes"].includes(value.toLowerCase()),
561
663
  },
562
664
  {
563
- key: 'ignoreFolders',
564
- question: 'Ignore folders (comma-separated, or press Enter to skip): ',
565
- default: '',
665
+ key: "ignoreFolders",
666
+ question: "Ignore folders (comma-separated, or press Enter to skip): ",
667
+ default: "",
566
668
  ask: () => config.addToTxtIgnore,
567
669
  transform: (value) => {
568
- if (!value || value.trim() === '') return [];
569
- return value.split(',').map(f => f.trim()).filter(f => f);
570
- }
670
+ if (!value || value.trim() === "") return [];
671
+ return value
672
+ .split(",")
673
+ .map((f) => f.trim())
674
+ .filter((f) => f);
675
+ },
571
676
  },
572
677
  {
573
- key: 'ignoreFiles',
574
- question: 'Ignore files (comma-separated, or press Enter to skip): ',
575
- default: '',
678
+ key: "ignoreFiles",
679
+ question: "Ignore files (comma-separated, or press Enter to skip): ",
680
+ default: "",
576
681
  ask: () => config.addToTxtIgnore,
577
682
  transform: (value) => {
578
- if (!value || value.trim() === '') return [];
579
- return value.split(',').map(f => f.trim()).filter(f => f);
580
- }
581
- }
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
+ },
582
703
  ];
583
704
 
584
705
  function askQuestion() {
@@ -589,7 +710,7 @@ function createInteractiveConfig() {
589
710
  }
590
711
 
591
712
  const q = questions[currentStep];
592
-
713
+
593
714
  // Skip if conditional ask returns false
594
715
  if (q.ask && !q.ask()) {
595
716
  currentStep++;
@@ -597,36 +718,40 @@ function createInteractiveConfig() {
597
718
  return;
598
719
  }
599
720
 
600
- const defaultValue = typeof q.default === 'function' ? q.default() : q.default;
601
- rl.question(q.question + (defaultValue ? `(${defaultValue}) ` : ''), (answer) => {
602
- const value = answer.trim() || defaultValue;
603
-
604
- // Validate input
605
- if (q.validate) {
606
- const validation = q.validate(value);
607
- if (validation !== true) {
608
- console.log(`❌ ${validation}`);
609
- askQuestion();
610
- 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
+ }
611
736
  }
612
- }
613
737
 
614
- // Transform value if needed
615
- if (q.transform) {
616
- config[q.key] = q.transform(value);
617
- } else {
618
- config[q.key] = value;
619
- }
738
+ // Transform value if needed
739
+ if (q.transform) {
740
+ config[q.key] = q.transform(value);
741
+ } else {
742
+ config[q.key] = value;
743
+ }
620
744
 
621
- currentStep++;
622
- askQuestion();
623
- });
745
+ currentStep++;
746
+ askQuestion();
747
+ },
748
+ );
624
749
  }
625
750
 
626
751
  function saveConfig() {
627
752
  try {
628
753
  // Create .txtconfig file with proper formatting
629
- const configPath = path.join(process.cwd(), '.txtconfig');
754
+ const configPath = path.join(process.cwd(), ".txtconfig");
630
755
  const configContent = `{
631
756
  "maxFileSize": "${config.maxFileSize}",
632
757
  "splitMethod": "${config.splitMethod}",
@@ -637,39 +762,81 @@ function createInteractiveConfig() {
637
762
  fs.writeFileSync(configPath, configContent);
638
763
 
639
764
  // Update .txtignore if user wants to add ignore patterns
640
- if (config.addToTxtIgnore && (config.ignoreFolders.length > 0 || config.ignoreFiles.length > 0)) {
641
- const ignorePath = path.join(process.cwd(), '.txtignore');
642
- let ignoreContent = '';
643
-
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
+
644
772
  // Read existing content if file exists
645
773
  if (fs.existsSync(ignorePath)) {
646
- ignoreContent = fs.readFileSync(ignorePath, 'utf8');
647
- if (!ignoreContent.endsWith('\n')) ignoreContent += '\n';
774
+ ignoreContent = fs.readFileSync(ignorePath, "utf8");
775
+ if (!ignoreContent.endsWith("\n")) ignoreContent += "\n";
648
776
  }
649
777
 
650
778
  // Add new ignore patterns
651
779
  const newPatterns = [
652
- ...config.ignoreFolders.map(f => f.endsWith('/') ? f : `${f}/`),
653
- ...config.ignoreFiles
780
+ ...config.ignoreFolders.map((f) => (f.endsWith("/") ? f : `${f}/`)),
781
+ ...config.ignoreFiles,
654
782
  ];
655
783
 
656
784
  if (newPatterns.length > 0) {
657
- ignoreContent += '\n# Added by make-folder-txt config\n';
658
- ignoreContent += newPatterns.join('\n') + '\n';
785
+ ignoreContent += "\n# Added by make-folder-txt config\n";
786
+ ignoreContent += newPatterns.join("\n") + "\n";
659
787
  fs.writeFileSync(ignorePath, ignoreContent);
660
788
  }
661
789
  }
662
790
 
663
- 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!");
664
826
  console.log(`📄 Config file: ${configPath}`);
665
- 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
+ ) {
666
831
  console.log(`📝 Ignore patterns added to .txtignore`);
667
832
  }
668
- console.log('\n💡 Your settings will now be used automatically!');
669
- console.log('🔄 Run --delete-config to reset to defaults');
670
-
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");
671
838
  } catch (err) {
672
- console.error('❌ Error saving configuration:', err.message);
839
+ console.error("❌ Error saving configuration:", err.message);
673
840
  } finally {
674
841
  rl.close();
675
842
  resolve();
@@ -681,20 +848,20 @@ function createInteractiveConfig() {
681
848
  }
682
849
 
683
850
  function loadConfig() {
684
- const fs = require('fs');
685
- const path = require('path');
686
-
851
+ const fs = require("fs");
852
+ const path = require("path");
853
+
687
854
  try {
688
- const configPath = path.join(process.cwd(), '.txtconfig');
855
+ const configPath = path.join(process.cwd(), ".txtconfig");
689
856
  if (!fs.existsSync(configPath)) {
690
- console.error('❌ .txtconfig file not found. Run --make-config first.');
857
+ console.error("❌ .txtconfig file not found. Run --make-config first.");
691
858
  process.exit(1);
692
859
  }
693
-
694
- const configContent = fs.readFileSync(configPath, 'utf8');
860
+
861
+ const configContent = fs.readFileSync(configPath, "utf8");
695
862
  return JSON.parse(configContent);
696
863
  } catch (err) {
697
- console.error('❌ Error loading configuration:', err.message);
864
+ console.error("❌ Error loading configuration:", err.message);
698
865
  process.exit(1);
699
866
  }
700
867
  }
@@ -717,55 +884,62 @@ async function main() {
717
884
 
718
885
  if (args.includes("--delete-config")) {
719
886
  try {
720
- const fs = require('fs');
721
- const path = require('path');
722
- const configPath = path.join(process.cwd(), '.txtconfig');
723
-
887
+ const fs = require("fs");
888
+ const path = require("path");
889
+ const configPath = path.join(process.cwd(), ".txtconfig");
890
+
724
891
  if (fs.existsSync(configPath)) {
725
892
  fs.unlinkSync(configPath);
726
- console.log('✅ Configuration file deleted successfully!');
727
- console.log('🔄 Tool will now use default settings');
893
+ console.log("✅ Configuration file deleted successfully!");
894
+ console.log("🔄 Tool will now use default settings");
728
895
  } else {
729
- console.log('ℹ️ No configuration file found - already using defaults');
896
+ console.log("ℹ️ No configuration file found - already using defaults");
730
897
  }
731
898
  } catch (err) {
732
- console.error('❌ Error deleting configuration:', err.message);
899
+ console.error("❌ Error deleting configuration:", err.message);
733
900
  }
734
901
  process.exit(0);
735
902
  }
736
903
 
737
904
  // Auto-use config if it exists and no other flags are provided
738
- const configPath = path.join(process.cwd(), '.txtconfig');
905
+ const configPath = path.join(process.cwd(), ".txtconfig");
739
906
  const hasConfig = fs.existsSync(configPath);
740
- const hasOtherFlags = args.length > 0 && !args.includes('--help') && !args.includes('-h') && !args.includes('--version') && !args.includes('-v');
741
-
907
+ const hasOtherFlags =
908
+ args.length > 0 &&
909
+ !args.includes("--help") &&
910
+ !args.includes("-h") &&
911
+ !args.includes("--version") &&
912
+ !args.includes("-v");
913
+
742
914
  if (hasConfig && !hasOtherFlags) {
743
915
  const config = loadConfig();
744
-
916
+
745
917
  // Apply config to command line arguments
746
918
  const newArgs = [];
747
-
919
+
748
920
  // Add max file size if not default
749
- if (config.maxFileSize !== '500KB') {
750
- newArgs.push('--skip-large', config.maxFileSize);
921
+ if (config.maxFileSize !== "500KB") {
922
+ newArgs.push("--skip-large", config.maxFileSize);
751
923
  }
752
-
924
+
753
925
  // Add split method if not none
754
- if (config.splitMethod !== 'none') {
755
- newArgs.push('--split-method', config.splitMethod);
756
- if (config.splitMethod === 'size') {
757
- 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);
758
930
  }
759
931
  }
760
-
932
+
761
933
  // Add copy to clipboard if true
762
934
  if (config.copyToClipboard) {
763
- newArgs.push('--copy');
935
+ newArgs.push("--copy");
764
936
  }
765
-
937
+
766
938
  // Replace args with config-based args
767
939
  args.splice(0, args.length, ...newArgs);
768
- 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
+ );
769
943
  }
770
944
 
771
945
  if (args.includes("--help") || args.includes("-h")) {
@@ -787,7 +961,7 @@ Dump an entire project folder into a single readable .txt file.
787
961
  --split-size <size> Split output when size exceeds limit (requires --split-method size)
788
962
  --copy Copy output to clipboard
789
963
  --force Include everything (overrides all ignore patterns)
790
- --make-config Create interactive configuration
964
+ --make-config Create interactive configuration (with optional .gitignore setup)
791
965
  --delete-config Delete configuration and reset to defaults
792
966
  --help, -h Show this help message
793
967
  --version, -v Show version information
@@ -824,417 +998,439 @@ Dump an entire project folder into a single readable .txt file.
824
998
  coverage/
825
999
  LICENSE
826
1000
  `);
827
- 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;
828
1023
  }
829
1024
 
830
- const ignoreDirs = new Set(IGNORE_DIRS);
831
- const ignoreFiles = new Set(IGNORE_FILES);
832
- const onlyFolders = new Set();
833
- const onlyFiles = new Set();
834
- let outputArg = null;
835
- let copyToClipboardFlag = false;
836
- let forceFlag = false;
837
- let maxFileSize = 500 * 1024; // Default 500KB
838
- let noSkipFlag = false;
839
- let splitMethod = null; // 'folder', 'file', 'size'
840
- let splitSize = null; // size in bytes
841
- let rootOnlyInclude = false; // For /include flag
842
-
843
- for (let i = 0; i < args.length; i += 1) {
844
- const arg = args[i];
845
-
846
- if (arg === "--copy") {
847
- copyToClipboardFlag = true;
848
- continue;
849
- }
1025
+ if (arg === "--force") {
1026
+ forceFlag = true;
1027
+ continue;
1028
+ }
850
1029
 
851
- if (arg === "--force") {
852
- forceFlag = true;
853
- continue;
854
- }
1030
+ if (arg === "--no-skip") {
1031
+ noSkipFlag = true;
1032
+ continue;
1033
+ }
855
1034
 
856
- if (arg === "--no-skip") {
857
- noSkipFlag = true;
858
- 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);
859
1041
  }
1042
+ maxFileSize = parseFileSize(args[i + 1]);
1043
+ i += 1;
1044
+ continue;
1045
+ }
860
1046
 
861
- if (arg === "--skip-large") {
862
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
863
- console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
864
- process.exit(1);
865
- }
866
- maxFileSize = parseFileSize(args[i + 1]);
867
- i += 1;
868
- 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);
869
1054
  }
1055
+ maxFileSize = parseFileSize(value);
1056
+ continue;
1057
+ }
870
1058
 
871
- if (arg.startsWith("--skip-large=")) {
872
- const value = arg.slice("--skip-large=".length);
873
- if (!value) {
874
- console.error("Error: --skip-large requires a size value (e.g., 400KB, 5GB).");
875
- process.exit(1);
876
- }
877
- maxFileSize = parseFileSize(value);
878
- 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);
879
1065
  }
880
-
881
- if (arg === "--split-method") {
882
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
883
- console.error("Error: --split-method requires a method (folder, file, or size).");
884
- process.exit(1);
885
- }
886
- const method = args[i + 1].toLowerCase();
887
- if (!['folder', 'file', 'size'].includes(method)) {
888
- console.error("Error: --split-method must be one of: folder, file, size");
889
- process.exit(1);
890
- }
891
- splitMethod = method;
892
- i += 1;
893
- 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);
894
1072
  }
1073
+ splitMethod = method;
1074
+ i += 1;
1075
+ continue;
1076
+ }
895
1077
 
896
- if (arg.startsWith("--split-method=")) {
897
- const value = arg.slice("--split-method=".length);
898
- if (!value) {
899
- console.error("Error: --split-method requires a method (folder, file, or size).");
900
- process.exit(1);
901
- }
902
- const method = value.toLowerCase();
903
- if (!['folder', 'file', 'size'].includes(method)) {
904
- console.error("Error: --split-method must be one of: folder, file, size");
905
- process.exit(1);
906
- }
907
- splitMethod = method;
908
- 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);
909
1085
  }
910
-
911
- if (arg === "--split-size") {
912
- if (i + 1 >= args.length || args[i + 1].startsWith("-")) {
913
- console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
914
- process.exit(1);
915
- }
916
- splitSize = parseFileSize(args[i + 1]);
917
- i += 1;
918
- 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);
919
1092
  }
1093
+ splitMethod = method;
1094
+ continue;
1095
+ }
920
1096
 
921
- if (arg.startsWith("--split-size=")) {
922
- const value = arg.slice("--split-size=".length);
923
- if (!value) {
924
- console.error("Error: --split-size requires a size value (e.g., 5MB, 10MB).");
925
- process.exit(1);
926
- }
927
- splitSize = parseFileSize(value);
928
- 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);
929
1103
  }
1104
+ splitSize = parseFileSize(args[i + 1]);
1105
+ i += 1;
1106
+ continue;
1107
+ }
930
1108
 
931
- if (arg === "--ignore-folder" || arg === "-ifo") {
932
- let consumed = 0;
933
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
934
- let folderName = args[i + 1];
935
-
936
- // Check for /folder syntax (root-only include)
937
- if (folderName.startsWith("/")) {
938
- const cleanFolderName = folderName.slice(1); // Remove leading /
939
- onlyFolders.add(cleanFolderName);
940
- rootOnlyInclude = true;
941
- i += 1;
942
- consumed += 1;
943
- } else {
944
- // Normal folder path (could be nested like folder/subfolder)
945
- onlyFolders.add(folderName);
946
- i += 1;
947
- consumed += 1;
948
- }
949
- }
950
- if (consumed === 0) {
951
- console.error("Error: --ignore-folder requires at least one folder name.");
952
- process.exit(1);
953
- }
954
- 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);
955
1116
  }
1117
+ splitSize = parseFileSize(value);
1118
+ continue;
1119
+ }
956
1120
 
957
- if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
958
- const value = arg.startsWith("--ignore-folder=")
959
- ? arg.slice("--ignore-folder=".length)
960
- : arg.slice("-ifo=".length);
961
- if (!value) {
962
- console.error("Error: --ignore-folder requires a folder name.");
963
- process.exit(1);
964
- }
965
-
966
- // Check for /folder syntax (root-only include)
967
- if (value.startsWith("/")) {
968
- const cleanFolderName = value.slice(1); // Remove leading /
969
- onlyFolders.add(cleanFolderName);
970
- rootOnlyInclude = true;
971
- } else {
972
- // Normal folder path (could be nested like folder/subfolder)
973
- onlyFolders.add(value);
974
- }
975
- 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);
976
1136
  }
1137
+ continue;
1138
+ }
977
1139
 
978
- if (arg === "--ignore-file" || arg === "-ifi") {
979
- let consumed = 0;
980
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
981
- ignoreFiles.add(args[i + 1]);
982
- i += 1;
983
- consumed += 1;
984
- }
985
- if (consumed === 0) {
986
- console.error("Error: --ignore-file requires at least one file name.");
987
- process.exit(1);
988
- }
989
- 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);
990
1147
  }
1148
+ let folderName = value.replace(/\\/g, "/").replace(/\/+$/, "");
1149
+ ignoreDirs.add(folderName);
1150
+ continue;
1151
+ }
991
1152
 
992
- if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
993
- const value = arg.startsWith("--ignore-file=")
994
- ? arg.slice("--ignore-file=".length)
995
- : arg.slice("-ifi=".length);
996
- if (!value) {
997
- console.error("Error: --ignore-file requires a file name.");
998
- process.exit(1);
999
- }
1000
- ignoreFiles.add(value);
1001
- 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;
1002
1159
  }
1160
+ if (consumed === 0) {
1161
+ console.error("Error: --ignore-file requires at least one file name.");
1162
+ process.exit(1);
1163
+ }
1164
+ continue;
1165
+ }
1003
1166
 
1004
- if (arg === "--only-folder" || arg === "-ofo") {
1005
- let consumed = 0;
1006
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1007
- let folderName = args[i + 1];
1008
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
1009
- // Detect leading / as root-only signal (same as --only-file behavior)
1010
- if (folderName.startsWith("/")) {
1011
- rootOnlyInclude = true;
1012
- }
1013
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
1014
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
1015
- onlyFolders.add(folderName);
1016
- i += 1;
1017
- consumed += 1;
1018
- }
1019
- if (consumed === 0) {
1020
- console.error("Error: --only-folder requires at least one folder name.");
1021
- process.exit(1);
1022
- }
1023
- 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);
1024
1174
  }
1175
+ ignoreFiles.add(value);
1176
+ continue;
1177
+ }
1025
1178
 
1026
- if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
1027
- const value = arg.startsWith("--only-folder=")
1028
- ? arg.slice("--only-folder=".length)
1029
- : arg.slice("-ofo=".length);
1030
- if (!value) {
1031
- console.error("Error: --only-folder requires a folder name.");
1032
- process.exit(1);
1033
- }
1034
- let folderName = value;
1035
- folderName = folderName.replace(/\\/g, '/'); // Convert backslashes to forward slashes
1036
- // Detect leading / as root-only signal
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)
1037
1185
  if (folderName.startsWith("/")) {
1038
1186
  rootOnlyInclude = true;
1039
1187
  }
1040
- folderName = folderName.replace(/^\.?\//, ''); // Remove leading ./ or /
1041
- folderName = folderName.replace(/\/+$/, ''); // Remove trailing slashes
1188
+ folderName = folderName.replace(/^\.?\//, ""); // Remove leading ./ or /
1189
+ folderName = folderName.replace(/\/+$/, ""); // Remove trailing slashes
1042
1190
  onlyFolders.add(folderName);
1043
- 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);
1044
1199
  }
1200
+ continue;
1201
+ }
1045
1202
 
1046
- if (arg === "--only-file" || arg === "-ofi") {
1047
- let consumed = 0;
1048
- while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
1049
- let fileName = args[i + 1];
1050
-
1051
- // Check for /file syntax (root-only include)
1052
- if (fileName.startsWith("/")) {
1053
- const cleanFileName = fileName.slice(1); // Remove leading /
1054
- onlyFiles.add(cleanFileName);
1055
- rootOnlyInclude = true;
1056
- i += 1;
1057
- consumed += 1;
1058
- } else {
1059
- // Normal file path (could be nested like folder/file.ext)
1060
- onlyFiles.add(fileName);
1061
- i += 1;
1062
- consumed += 1;
1063
- }
1064
- }
1065
- if (consumed === 0) {
1066
- console.error("Error: --only-file requires at least one file name.");
1067
- process.exit(1);
1068
- }
1069
- 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);
1070
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];
1071
1227
 
1072
- if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
1073
- const value = arg.startsWith("--only-file=")
1074
- ? arg.slice("--only-file=".length)
1075
- : arg.slice("-ofi=".length);
1076
- if (!value) {
1077
- console.error("Error: --only-file requires a file name.");
1078
- process.exit(1);
1079
- }
1080
-
1081
1228
  // Check for /file syntax (root-only include)
1082
- if (value.startsWith("/")) {
1083
- const cleanFileName = value.slice(1); // Remove leading /
1229
+ if (fileName.startsWith("/")) {
1230
+ const cleanFileName = fileName.slice(1); // Remove leading /
1084
1231
  onlyFiles.add(cleanFileName);
1085
1232
  rootOnlyInclude = true;
1233
+ i += 1;
1234
+ consumed += 1;
1086
1235
  } else {
1087
1236
  // Normal file path (could be nested like folder/file.ext)
1088
- onlyFiles.add(value);
1237
+ onlyFiles.add(fileName);
1238
+ i += 1;
1239
+ consumed += 1;
1089
1240
  }
1090
- continue;
1091
1241
  }
1092
-
1093
- // Unknown argument
1094
- console.error(`Error: Unknown option "${arg}"`);
1095
- console.error("Use --help for available options.");
1096
- 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;
1097
1247
  }
1098
1248
 
1099
- // Validate split options
1100
- if (splitMethod === 'size' && !splitSize) {
1101
- console.error("Error: --split-method size requires --split-size to be specified");
1102
- process.exit(1);
1103
- }
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
+ }
1104
1257
 
1105
- if (splitSize && splitMethod !== 'size') {
1106
- console.error("Error: --split-size can only be used with --split-method size");
1107
- 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;
1108
1268
  }
1109
1269
 
1110
- // ── config ────────────────────────────────────────────────────────────────────────
1111
-
1112
- const folderPath = process.cwd();
1113
- const rootName = path.basename(folderPath);
1114
- const txtIgnore = readTxtIgnore(folderPath);
1115
-
1116
- // ── build tree & collect file paths ───────────────────────────────────────────────
1117
-
1118
- const { lines: treeLines, filePaths } = collectFiles(
1119
- folderPath,
1120
- folderPath,
1121
- ignoreDirs,
1122
- ignoreFiles,
1123
- onlyFolders,
1124
- onlyFiles,
1125
- {
1126
- rootName,
1127
- txtIgnore,
1128
- force: forceFlag,
1129
- hasOnlyFilters: onlyFolders.size > 0 || onlyFiles.size > 0,
1130
- rootOnlyInclude
1131
- }
1132
- );
1270
+ // Unknown argument
1271
+ console.error(`Error: Unknown option "${arg}"`);
1272
+ console.error("Use --help for available options.");
1273
+ process.exit(1);
1274
+ }
1133
1275
 
1134
- // ── 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
+ }
1135
1283
 
1136
- 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
+ }
1137
1290
 
1138
- // ── 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
+ }
1139
1348
 
1140
- const effectiveMaxSize = noSkipFlag ? Infinity : maxFileSize;
1349
+ console.log(`✅ Done! Created ${results.length} split files:`);
1350
+ console.log("");
1141
1351
 
1142
- if (splitMethod) {
1143
- console.log(`🔧 Splitting output by: ${splitMethod}`);
1144
-
1145
- let results;
1146
-
1147
- if (splitMethod === 'folder') {
1148
- results = splitByFolders(treeLines, filePaths, rootName, effectiveMaxSize, forceFlag);
1149
- } else if (splitMethod === 'file') {
1150
- results = splitByFiles(filePaths, rootName, effectiveMaxSize, forceFlag);
1151
- } else if (splitMethod === 'size') {
1152
- 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}`);
1153
1359
  }
1154
-
1155
- console.log(`✅ Done! Created ${results.length} split files:`);
1156
- console.log('');
1157
-
1158
- results.forEach((result, index) => {
1159
- if (splitMethod === 'folder') {
1160
- console.log(`📁 Folder: ${result.folder}`);
1161
- } else if (splitMethod === 'file') {
1162
- console.log(`📄 File: ${result.fileName}`);
1163
- } else if (splitMethod === 'size') {
1164
- console.log(`📦 Part ${result.part}`);
1165
- }
1166
- console.log(`📄 Output : ${result.file}`);
1167
- console.log(`📊 Size : ${result.size} KB`);
1168
- console.log(`🗂️ Files : ${result.files}`);
1169
- console.log('');
1170
- });
1171
-
1172
- if (copyToClipboardFlag) {
1173
- console.log('⚠️ --copy flag is not compatible with splitting - clipboard copy skipped');
1174
- }
1175
-
1176
- 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
+ );
1177
1370
  }
1178
1371
 
1179
- // ── build output (no splitting) ───────────────────────────────────────────────────
1180
- const out = [];
1181
- const divider = "=".repeat(80);
1182
- const subDivider = "-".repeat(80);
1372
+ process.exit(0);
1373
+ }
1183
1374
 
1184
- out.push(divider);
1185
- out.push(`START OF FOLDER: ${rootName}`);
1186
- out.push(divider);
1187
- out.push("");
1375
+ // ── build output (no splitting) ───────────────────────────────────────────────────
1376
+ const out = [];
1377
+ const divider = "=".repeat(80);
1378
+ const subDivider = "-".repeat(80);
1188
1379
 
1189
- out.push(divider);
1190
- out.push("PROJECT STRUCTURE");
1191
- out.push(divider);
1192
- out.push(`Root: ${folderPath}\n`);
1380
+ out.push(divider);
1381
+ out.push(`START OF FOLDER: ${rootName}`);
1382
+ out.push(divider);
1383
+ out.push("");
1193
1384
 
1194
- out.push(`${rootName}/`);
1195
- treeLines.forEach(l => out.push(l));
1196
- out.push("");
1197
- out.push(`Total files: ${filePaths.length}`);
1198
- out.push("");
1385
+ out.push(divider);
1386
+ out.push("PROJECT STRUCTURE");
1387
+ out.push(divider);
1388
+ out.push(`Root: ${folderPath}\n`);
1199
1389
 
1200
- out.push(divider);
1201
- out.push("FILE CONTENTS");
1202
- 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("");
1203
1395
 
1204
- filePaths.forEach(({ abs, rel }) => {
1205
- out.push("");
1206
- out.push(subDivider);
1207
- out.push(`FILE: ${rel}`);
1208
- out.push(subDivider);
1209
- out.push(readContent(abs, forceFlag, effectiveMaxSize));
1210
- });
1396
+ out.push(divider);
1397
+ out.push("FILE CONTENTS");
1398
+ out.push(divider);
1211
1399
 
1400
+ filePaths.forEach(({ abs, rel }) => {
1212
1401
  out.push("");
1213
- out.push(divider);
1214
- out.push(`END OF FOLDER: ${rootName}`);
1215
- 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);
1216
1412
 
1217
- fs.writeFileSync(outputFile, out.join("\n"), "utf8");
1413
+ fs.writeFileSync(outputFile, out.join("\n"), "utf8");
1218
1414
 
1219
- const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
1220
- console.log(`✅ Done!`);
1221
- console.log(`📄 Output : ${outputFile}`);
1222
- console.log(`📊 Size : ${sizeKB} KB`);
1223
- 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}`);
1224
1420
 
1225
- if (copyToClipboardFlag) {
1226
- const content = fs.readFileSync(outputFile, 'utf8');
1227
- const success = copyToClipboard(content);
1228
- if (success) {
1229
- console.log(`📋 Copied to clipboard!`);
1230
- }
1421
+ if (copyToClipboardFlag) {
1422
+ const content = fs.readFileSync(outputFile, "utf8");
1423
+ const success = copyToClipboard(content);
1424
+ if (success) {
1425
+ console.log(`📋 Copied to clipboard!`);
1231
1426
  }
1427
+ }
1232
1428
 
1233
- console.log('');
1429
+ console.log("");
1234
1430
  }
1235
1431
 
1236
1432
  // Run the main function
1237
- main().catch(err => {
1238
- console.error('❌ Error:', err.message);
1433
+ main().catch((err) => {
1434
+ console.error("❌ Error:", err.message);
1239
1435
  process.exit(1);
1240
- });
1436
+ });