make-folder-txt 1.4.4 → 2.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  Perfect for sharing your codebase with **AI tools**, **teammates**, or **code reviewers** — without zipping files or giving repo access.
13
13
 
14
- [Installation](#-installation) · [Usage](#-usage) · [Help](#-get-help) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
14
+ [Installation](#-installation) · [Usage](#-usage) · [Help](#-get-help) · [Copy to Clipboard](#-copy-to-clipboard) · [Force Include Everything](#-force-include-everything) · [Shell Autocompletion](#-shell-autocompletion) · [Output Format](#-output-format) · [What Gets Skipped](#-what-gets-skipped) · [Contributing](#-contributing)
15
15
 
16
16
  </div>
17
17
 
@@ -23,6 +23,9 @@ Ever needed to share your entire project with ChatGPT, Claude, or a teammate —
23
23
 
24
24
  - ✅ Run it from any project directory — no arguments needed
25
25
  - ✅ Built-in help system with `--help` flag
26
+ - ✅ **Built-in shell autocompletion** (installs automatically)
27
+ - ✅ Copy to clipboard with `--copy` flag
28
+ - ✅ Force include everything with `--force` flag
26
29
  - ✅ Generates a clean folder tree + every file's content
27
30
  - ✅ `.txtignore` support (works like `.gitignore`)
28
31
  - ✅ Automatically skips `node_modules`, binaries, and junk files
@@ -61,13 +64,84 @@ make-folder-txt --version # Show version info
61
64
  make-folder-txt -v # Short version of version
62
65
  ```
63
66
 
67
+ ### 📋 Copy to Clipboard
68
+
69
+ ```bash
70
+ make-folder-txt --copy # Generate output and copy to clipboard
71
+ make-folder-txt --copy --ignore-folder node_modules # Copy filtered output
72
+ ```
73
+
74
+ The `--copy` flag automatically copies the generated output to your system clipboard, making it easy to paste directly into AI tools, emails, or documents. Works on Windows, macOS, and Linux (requires `xclip` or `xsel` on Linux).
75
+
76
+ ### 🔥 Force Include Everything
77
+
78
+ ```bash
79
+ make-folder-txt --force # Include everything (overrides all ignore patterns)
80
+ make-folder-txt --force --copy # Include everything and copy to clipboard
81
+ ```
82
+
83
+ The `--force` flag overrides all ignore patterns and includes:
84
+ - `node_modules` and other ignored folders
85
+ - Binary files (images, executables, etc.)
86
+ - Large files (no 500 KB limit)
87
+ - Files in `.txtignore`
88
+ - System files and other normally skipped content
89
+
90
+ Use this when you need a complete, unfiltered dump of your entire project.
91
+
92
+ ### ⚡ Shell Autocompletion (Built-in)
93
+
94
+ ```bash
95
+ make-folder-txt # Autocompletion installs automatically on first run
96
+ ```
97
+
98
+ **🎉 No installation required!** The tool automatically installs shell autocompletion the first time you run it.
99
+
100
+ **What gets installed automatically:**
101
+ - **Flag completion**: `make-folder-txt --<TAB>` shows all available flags
102
+ - **Folder completion**: `make-folder-txt --ignore-folder <TAB>` shows folders
103
+ - **File completion**: `make-folder-txt --ignore-file <TAB>` shows files
104
+
105
+ **Supported Shells:**
106
+ - **Bash** - Linux/macOS/Windows (WSL)
107
+ - **Zsh** - macOS/Linux
108
+ - **PowerShell** - Windows (7+)
109
+
110
+ **Example usage:**
111
+ ```bash
112
+ $ make-folder-txt --ignore-folder b<TAB>
113
+ # → completes to "bin/" if bin folder exists
114
+
115
+ $ make-folder-txt --ignore-file p<TAB>
116
+ # → completes to "package.json" if package.json exists
117
+ ```
118
+
119
+ **Manual Installation (if needed):**
120
+ ```bash
121
+ make-folder-txt --install-completion # Force reinstall completion
122
+ ```
123
+
124
+ **PowerShell Manual Setup:**
125
+ ```powershell
126
+ # If auto-installation doesn't work, load manually:
127
+ . .\completion\make-folder-txt-completion.ps1
128
+
129
+ # Or add to your PowerShell profile for persistence:
130
+ notepad $PROFILE
131
+ # Add: . "C:\path\to\make-folder-txt\completion\make-folder-txt-completion.ps1"
132
+ ```
133
+
134
+ The completion works out of the box - just run the tool once and restart your terminal!
135
+
64
136
  Ignore specific folders/files by name:
65
137
 
66
138
  ```bash
67
139
  make-folder-txt --ignore-folder examples extensions docs
140
+ make-folder-txt -ifo examples extensions docs # shorthand
68
141
  make-folder-txt --ignore-folder examples extensions "docs and explaination"
69
142
  make-folder-txt --ignore-folder examples extensions docs --ignore-file LICENSE
70
143
  make-folder-txt --ignore-file .env .env.local secrets.txt
144
+ make-folder-txt -ifi .env .env.local secrets.txt # shorthand
71
145
  ```
72
146
 
73
147
  Use a `.txtignore` file (works like `.gitignore`):
@@ -93,7 +167,9 @@ Include only specific folders/files by name (everything else is ignored):
93
167
 
94
168
  ```bash
95
169
  make-folder-txt --only-folder src docs
170
+ make-folder-txt -ofo src docs # shorthand
96
171
  make-folder-txt --only-file package.json README.md
172
+ make-folder-txt -ofi package.json README.md # shorthand
97
173
  make-folder-txt --only-folder src --only-file package.json
98
174
  ```
99
175
 
@@ -3,6 +3,7 @@
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { version } = require("../package.json");
6
+ const { execSync } = require("child_process");
6
7
 
7
8
  // ── config ────────────────────────────────────────────────────────────────────
8
9
  const IGNORE_DIRS = new Set(["node_modules", ".git", ".next", "dist", "build", ".cache"]);
@@ -37,6 +38,34 @@ function readTxtIgnore(rootDir) {
37
38
  return ignorePatterns;
38
39
  }
39
40
 
41
+ function copyToClipboard(text) {
42
+ try {
43
+ if (process.platform === 'win32') {
44
+ // Windows
45
+ execSync(`echo ${JSON.stringify(text).replace(/"/g, '""')} | clip`, { stdio: 'ignore' });
46
+ } else if (process.platform === 'darwin') {
47
+ // macOS
48
+ execSync(`echo ${JSON.stringify(text)} | pbcopy`, { stdio: 'ignore' });
49
+ } else {
50
+ // Linux (requires xclip or xsel)
51
+ try {
52
+ execSync(`echo ${JSON.stringify(text)} | xclip -selection clipboard`, { stdio: 'ignore' });
53
+ } catch {
54
+ try {
55
+ execSync(`echo ${JSON.stringify(text)} | xsel --clipboard --input`, { stdio: 'ignore' });
56
+ } catch {
57
+ console.warn('⚠️ Could not copy to clipboard. Install xclip or xsel on Linux.');
58
+ return false;
59
+ }
60
+ }
61
+ }
62
+ return true;
63
+ } catch (err) {
64
+ console.warn('⚠️ Could not copy to clipboard:', err.message);
65
+ return false;
66
+ }
67
+ }
68
+
40
69
  function collectFiles(
41
70
  dir,
42
71
  rootDir,
@@ -54,6 +83,7 @@ function collectFiles(
54
83
  hasOnlyFilters = false,
55
84
  rootName = "",
56
85
  txtIgnore = new Set(),
86
+ force = false,
57
87
  } = options;
58
88
 
59
89
  let entries;
@@ -74,7 +104,7 @@ function collectFiles(
74
104
  const childIndent = indent + (isLast ? " " : "│ ");
75
105
 
76
106
  if (entry.isDirectory()) {
77
- if (ignoreDirs.has(entry.name)) {
107
+ if (!force && ignoreDirs.has(entry.name)) {
78
108
  if (!hasOnlyFilters) {
79
109
  lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
80
110
  }
@@ -84,8 +114,8 @@ function collectFiles(
84
114
  // Get relative path for .txtignore pattern matching
85
115
  const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
86
116
 
87
- // Check against .txtignore patterns (both dirname and relative path)
88
- if (txtIgnore.has(entry.name) || txtIgnore.has(`${entry.name}/`) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`${relPathForIgnore}/`) || txtIgnore.has(`/${relPathForIgnore}/`)) {
117
+ // Check against .txtignore patterns (both dirname and relative path) unless force is enabled
118
+ if (!force && (txtIgnore.has(entry.name) || txtIgnore.has(`${entry.name}/`) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`${relPathForIgnore}/`) || txtIgnore.has(`/${relPathForIgnore}/`))) {
89
119
  if (!hasOnlyFilters) {
90
120
  lines.push(`${indent}${connector}${entry.name}/ [skipped]`);
91
121
  }
@@ -111,6 +141,7 @@ function collectFiles(
111
141
  hasOnlyFilters,
112
142
  rootName,
113
143
  txtIgnore,
144
+ force,
114
145
  },
115
146
  );
116
147
 
@@ -123,18 +154,18 @@ function collectFiles(
123
154
  filePaths.push(...child.filePaths);
124
155
  }
125
156
  } else {
126
- if (ignoreFiles.has(entry.name)) return;
157
+ if (!force && ignoreFiles.has(entry.name)) return;
127
158
 
128
159
  // Get relative path for .txtignore pattern matching
129
160
  const relPathForIgnore = path.relative(rootDir, path.join(dir, entry.name)).split(path.sep).join("/");
130
161
 
131
- // Check against .txtignore patterns (both filename and relative path)
132
- if (txtIgnore.has(entry.name) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`/${relPathForIgnore}`)) {
162
+ // Check against .txtignore patterns (both filename and relative path) unless force is enabled
163
+ if (!force && (txtIgnore.has(entry.name) || txtIgnore.has(relPathForIgnore) || txtIgnore.has(`/${relPathForIgnore}`))) {
133
164
  return;
134
165
  }
135
166
 
136
- // Ignore .txt files that match the folder name (e.g., foldername.txt)
137
- if (entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
167
+ // Ignore .txt files that match the folder name (e.g., foldername.txt) unless force is enabled
168
+ if (!force && entry.name.endsWith('.txt') && entry.name === `${rootName}.txt`) return;
138
169
 
139
170
  const shouldIncludeFile = !hasOnlyFilters || inSelectedFolder || onlyFiles.has(entry.name);
140
171
  if (!shouldIncludeFile) return;
@@ -148,12 +179,12 @@ function collectFiles(
148
179
  return { lines, filePaths, hasIncluded: filePaths.length > 0 || lines.length > 0 };
149
180
  }
150
181
 
151
- function readContent(absPath) {
182
+ function readContent(absPath, force = false) {
152
183
  const ext = path.extname(absPath).toLowerCase();
153
- if (BINARY_EXTS.has(ext)) return "[binary / skipped]";
184
+ if (!force && BINARY_EXTS.has(ext)) return "[binary / skipped]";
154
185
  try {
155
186
  const stat = fs.statSync(absPath);
156
- if (stat.size > 500 * 1024) {
187
+ if (!force && stat.size > 500 * 1024) {
157
188
  return `[file too large: ${(stat.size / 1024).toFixed(1)} KB – skipped]`;
158
189
  }
159
190
  return fs.readFileSync(absPath, "utf8");
@@ -166,6 +197,205 @@ function readContent(absPath) {
166
197
 
167
198
  const args = process.argv.slice(2);
168
199
 
200
+ // Check if completion is already installed, install if not
201
+ function checkAndInstallCompletion() {
202
+ const { execSync } = require('child_process');
203
+ const path = require('path');
204
+ const os = require('os');
205
+ const fs = require('fs');
206
+
207
+ try {
208
+ const homeDir = os.homedir();
209
+ const shell = process.env.SHELL || '';
210
+ const platform = process.platform;
211
+ let completionInstalled = false;
212
+
213
+ if (platform === 'win32') {
214
+ // Check PowerShell completion - try multiple profile locations
215
+ const profilePaths = [
216
+ path.join(homeDir, 'Documents', 'WindowsPowerShell', 'Microsoft.PowerShell_profile.ps1'),
217
+ path.join(homeDir, 'Documents', 'PowerShell', 'Microsoft.PowerShell_profile.ps1'),
218
+ path.join(homeDir, '.config', 'powershell', 'Microsoft.PowerShell_profile.ps1')
219
+ ];
220
+
221
+ for (const profilePath of profilePaths) {
222
+ if (fs.existsSync(profilePath)) {
223
+ const profileContent = fs.readFileSync(profilePath, 'utf8');
224
+ if (profileContent.includes('make-folder-txt-completion')) {
225
+ completionInstalled = true;
226
+ break;
227
+ }
228
+ }
229
+ }
230
+ } else if (shell.includes('zsh')) {
231
+ // Check zsh completion
232
+ const zshrc = path.join(homeDir, '.zshrc');
233
+ if (fs.existsSync(zshrc)) {
234
+ const zshrcContent = fs.readFileSync(zshrc, 'utf8');
235
+ if (zshrcContent.includes('make-folder-txt')) {
236
+ completionInstalled = true;
237
+ }
238
+ }
239
+ } else {
240
+ // Check bash completion
241
+ const bashrc = path.join(homeDir, '.bashrc');
242
+ if (fs.existsSync(bashrc)) {
243
+ const bashrcContent = fs.readFileSync(bashrc, 'utf8');
244
+ if (bashrcContent.includes('make-folder-txt-completion')) {
245
+ completionInstalled = true;
246
+ }
247
+ }
248
+ }
249
+
250
+ // If completion is not installed, install it automatically
251
+ if (!completionInstalled) {
252
+ console.log('🔧 Installing shell autocompletion for make-folder-txt...');
253
+
254
+ if (platform === 'win32') {
255
+ // Windows PowerShell
256
+ try {
257
+ execSync('powershell -Command "Get-Host"', { stdio: 'ignore' });
258
+ const installScript = path.join(__dirname, '..', 'completion', 'install-powershell-completion.ps1');
259
+ execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'ignore' });
260
+ console.log('✅ PowerShell completion installed!');
261
+ } catch (err) {
262
+ // Silent fail for PowerShell
263
+ }
264
+ } else if (shell.includes('zsh')) {
265
+ // zsh
266
+ try {
267
+ const zshrc = path.join(homeDir, '.zshrc');
268
+ const completionDir = path.join(homeDir, '.zsh', 'completions');
269
+ execSync(`mkdir -p "${completionDir}"`, { stdio: 'ignore' });
270
+ const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.zsh');
271
+ execSync(`cp "${completionPath}" "${completionDir}/_make-folder-txt"`, { stdio: 'ignore' });
272
+
273
+ try {
274
+ const zshrcContent = fs.readFileSync(zshrc, 'utf8');
275
+ if (!zshrcContent.includes('fpath+=~/.zsh/completions')) {
276
+ fs.appendFileSync(zshrc, '\n# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
277
+ }
278
+ } catch (e) {
279
+ fs.writeFileSync(zshrc, '# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
280
+ }
281
+ console.log('✅ Zsh completion installed!');
282
+ } catch (err) {
283
+ // Silent fail for zsh
284
+ }
285
+ } else {
286
+ // bash
287
+ try {
288
+ const bashrc = path.join(homeDir, '.bashrc');
289
+ const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.bash');
290
+ try {
291
+ const bashrcContent = fs.readFileSync(bashrc, 'utf8');
292
+ if (!bashrcContent.includes('make-folder-txt-completion.bash')) {
293
+ fs.appendFileSync(bashrc, `\n# make-folder-txt completion\nsource "${completionPath}"\n`);
294
+ }
295
+ } catch (e) {
296
+ fs.writeFileSync(bashrc, `# make-folder-txt completion\nsource "${completionPath}"\n`);
297
+ }
298
+ console.log('✅ Bash completion installed!');
299
+ } catch (err) {
300
+ // Silent fail for bash
301
+ }
302
+ }
303
+
304
+ console.log('💡 Restart your terminal to enable autocompletion');
305
+ console.log('');
306
+ }
307
+ } catch (err) {
308
+ // Silent fail - don't interrupt the main functionality
309
+ }
310
+ }
311
+
312
+ // Run completion check on first run (but not for help/version commands)
313
+ if (!args.includes("--help") && !args.includes("-h") &&
314
+ !args.includes("--version") && !args.includes("-v") &&
315
+ !args.includes("--install-completion")) {
316
+ checkAndInstallCompletion();
317
+ }
318
+
319
+ if (args.includes("--install-completion")) {
320
+ const { execSync } = require('child_process');
321
+ const path = require('path');
322
+ const os = require('os');
323
+
324
+ try {
325
+ const homeDir = os.homedir();
326
+ const shell = process.env.SHELL || '';
327
+ const platform = process.platform;
328
+
329
+ if (platform === 'win32') {
330
+ // Windows - Check if PowerShell is available
331
+ try {
332
+ execSync('powershell -Command "Get-Host"', { stdio: 'ignore' });
333
+
334
+ // Install PowerShell completion
335
+ const completionScript = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.ps1');
336
+ const installScript = path.join(__dirname, '..', 'completion', 'install-powershell-completion.ps1');
337
+
338
+ // Run the PowerShell installation script
339
+ execSync(`powershell -ExecutionPolicy Bypass -File "${installScript}"`, { stdio: 'inherit' });
340
+
341
+ } catch (err) {
342
+ console.error('❌ Failed to install PowerShell completion:', err.message);
343
+ process.exit(1);
344
+ }
345
+
346
+ } else if (shell.includes('zsh')) {
347
+ // Install for zsh
348
+ const zshrc = path.join(homeDir, '.zshrc');
349
+ const completionDir = path.join(homeDir, '.zsh', 'completions');
350
+
351
+ // Create completions directory if it doesn't exist
352
+ try {
353
+ execSync(`mkdir -p "${completionDir}"`, { stdio: 'ignore' });
354
+ } catch (e) {}
355
+
356
+ // Copy completion file
357
+ const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.zsh');
358
+ execSync(`cp "${completionPath}" "${completionDir}/_make-folder-txt"`, { stdio: 'ignore' });
359
+
360
+ // Add to .zshrc if not already there
361
+ try {
362
+ const zshrcContent = fs.readFileSync(zshrc, 'utf8');
363
+ if (!zshrcContent.includes('fpath+=~/.zsh/completions')) {
364
+ fs.appendFileSync(zshrc, '\n# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
365
+ }
366
+ } catch (e) {
367
+ // .zshrc doesn't exist, create it
368
+ fs.writeFileSync(zshrc, '# make-folder-txt completion\nfpath+=~/.zsh/completions\nautoload -U compinit && compinit\n');
369
+ }
370
+
371
+ console.log('✅ Zsh completion installed! Restart your terminal or run: source ~/.zshrc');
372
+
373
+ } else {
374
+ // Install for bash
375
+ const bashrc = path.join(homeDir, '.bashrc');
376
+ const completionPath = path.join(__dirname, '..', 'completion', 'make-folder-txt-completion.bash');
377
+
378
+ // Add to .bashrc if not already there
379
+ try {
380
+ const bashrcContent = fs.readFileSync(bashrc, 'utf8');
381
+ if (!bashrcContent.includes('make-folder-txt-completion.bash')) {
382
+ fs.appendFileSync(bashrc, `\n# make-folder-txt completion\nsource "${completionPath}"\n`);
383
+ }
384
+ } catch (e) {
385
+ // .bashrc doesn't exist, create it
386
+ fs.writeFileSync(bashrc, `# make-folder-txt completion\nsource "${completionPath}"\n`);
387
+ }
388
+
389
+ console.log('✅ Bash completion installed! Restart your terminal or run: source ~/.bashrc');
390
+ }
391
+ } catch (err) {
392
+ console.error('❌ Failed to install completion:', err.message);
393
+ process.exit(1);
394
+ }
395
+
396
+ process.exit(0);
397
+ }
398
+
169
399
  if (args.includes("-v") || args.includes("--version")) {
170
400
  console.log(`v${version}`);
171
401
  console.log("Built by Muhammad Saad Amin");
@@ -181,19 +411,29 @@ Dump an entire project folder into a single readable .txt file.
181
411
  make-folder-txt [options]
182
412
 
183
413
  \x1b[33mOPTIONS\x1b[0m
184
- --ignore-folder <names...> Ignore specific folders by name
185
- --ignore-file <names...> Ignore specific files by name
186
- --only-folder <names...> Include only specific folders
187
- --only-file <names...> Include only specific files
188
- --help, -h Show this help message
189
- --version, -v Show version information
414
+ --ignore-folder, -ifo <names...> Ignore specific folders by name
415
+ --ignore-file, -ifi <names...> Ignore specific files by name
416
+ --only-folder, -ofo <names...> Include only specific folders
417
+ --only-file, -ofi <names...> Include only specific files
418
+ --copy Copy output to clipboard
419
+ --force Include everything (overrides all ignore patterns)
420
+ --install-completion Install shell autocompletion (bash/zsh/PowerShell) - usually automatic
421
+ --help, -h Show this help message
422
+ --version, -v Show version information
190
423
 
191
424
  \x1b[33mEXAMPLES\x1b[0m
192
425
  make-folder-txt
426
+ make-folder-txt --copy
427
+ make-folder-txt --force
428
+ make-folder-txt --install-completion
193
429
  make-folder-txt --ignore-folder node_modules dist
430
+ make-folder-txt -ifo node_modules dist
194
431
  make-folder-txt --ignore-file .env .env.local
432
+ make-folder-txt -ifi .env .env.local
195
433
  make-folder-txt --only-folder src docs
434
+ make-folder-txt -ofo src docs
196
435
  make-folder-txt --only-file package.json README.md
436
+ make-folder-txt -ofi package.json README.md
197
437
 
198
438
  \x1b[33m.TXTIGNORE FILE\x1b[0m
199
439
  Create a .txtignore file in your project root to specify files/folders to ignore.
@@ -214,11 +454,23 @@ const ignoreFiles = new Set(IGNORE_FILES);
214
454
  const onlyFolders = new Set();
215
455
  const onlyFiles = new Set();
216
456
  let outputArg = null;
457
+ let copyToClipboardFlag = false;
458
+ let forceFlag = false;
217
459
 
218
460
  for (let i = 0; i < args.length; i += 1) {
219
461
  const arg = args[i];
220
462
 
221
- if (arg === "--ignore-folder") {
463
+ if (arg === "--copy") {
464
+ copyToClipboardFlag = true;
465
+ continue;
466
+ }
467
+
468
+ if (arg === "--force") {
469
+ forceFlag = true;
470
+ continue;
471
+ }
472
+
473
+ if (arg === "--ignore-folder" || arg === "-ifo") {
222
474
  let consumed = 0;
223
475
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
224
476
  ignoreDirs.add(args[i + 1]);
@@ -232,8 +484,10 @@ for (let i = 0; i < args.length; i += 1) {
232
484
  continue;
233
485
  }
234
486
 
235
- if (arg.startsWith("--ignore-folder=")) {
236
- const value = arg.slice("--ignore-folder=".length);
487
+ if (arg.startsWith("--ignore-folder=") || arg.startsWith("-ifo=")) {
488
+ const value = arg.startsWith("--ignore-folder=")
489
+ ? arg.slice("--ignore-folder=".length)
490
+ : arg.slice("-ifo=".length);
237
491
  if (!value) {
238
492
  console.error("Error: --ignore-folder requires a folder name.");
239
493
  process.exit(1);
@@ -242,7 +496,7 @@ for (let i = 0; i < args.length; i += 1) {
242
496
  continue;
243
497
  }
244
498
 
245
- if (arg === "--ignore-file") {
499
+ if (arg === "--ignore-file" || arg === "-ifi") {
246
500
  let consumed = 0;
247
501
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
248
502
  ignoreFiles.add(args[i + 1]);
@@ -256,8 +510,10 @@ for (let i = 0; i < args.length; i += 1) {
256
510
  continue;
257
511
  }
258
512
 
259
- if (arg.startsWith("--ignore-file=")) {
260
- const value = arg.slice("--ignore-file=".length);
513
+ if (arg.startsWith("--ignore-file=") || arg.startsWith("-ifi=")) {
514
+ const value = arg.startsWith("--ignore-file=")
515
+ ? arg.slice("--ignore-file=".length)
516
+ : arg.slice("-ifi=".length);
261
517
  if (!value) {
262
518
  console.error("Error: --ignore-file requires a file name.");
263
519
  process.exit(1);
@@ -266,7 +522,7 @@ for (let i = 0; i < args.length; i += 1) {
266
522
  continue;
267
523
  }
268
524
 
269
- if (arg === "--only-folder") {
525
+ if (arg === "--only-folder" || arg === "-ofo") {
270
526
  let consumed = 0;
271
527
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
272
528
  onlyFolders.add(args[i + 1]);
@@ -280,8 +536,10 @@ for (let i = 0; i < args.length; i += 1) {
280
536
  continue;
281
537
  }
282
538
 
283
- if (arg.startsWith("--only-folder=")) {
284
- const value = arg.slice("--only-folder=".length);
539
+ if (arg.startsWith("--only-folder=") || arg.startsWith("-ofo=")) {
540
+ const value = arg.startsWith("--only-folder=")
541
+ ? arg.slice("--only-folder=".length)
542
+ : arg.slice("-ofo=".length);
285
543
  if (!value) {
286
544
  console.error("Error: --only-folder requires a folder name.");
287
545
  process.exit(1);
@@ -290,7 +548,7 @@ for (let i = 0; i < args.length; i += 1) {
290
548
  continue;
291
549
  }
292
550
 
293
- if (arg === "--only-file") {
551
+ if (arg === "--only-file" || arg === "-ofi") {
294
552
  let consumed = 0;
295
553
  while (i + 1 < args.length && !args[i + 1].startsWith("-")) {
296
554
  onlyFiles.add(args[i + 1]);
@@ -304,8 +562,10 @@ for (let i = 0; i < args.length; i += 1) {
304
562
  continue;
305
563
  }
306
564
 
307
- if (arg.startsWith("--only-file=")) {
308
- const value = arg.slice("--only-file=".length);
565
+ if (arg.startsWith("--only-file=") || arg.startsWith("-ofi=")) {
566
+ const value = arg.startsWith("--only-file=")
567
+ ? arg.slice("--only-file=".length)
568
+ : arg.slice("-ofi=".length);
309
569
  if (!value) {
310
570
  console.error("Error: --only-file requires a file name.");
311
571
  process.exit(1);
@@ -348,7 +608,7 @@ const { lines: treeLines, filePaths } = collectFiles(
348
608
  ignoreFiles,
349
609
  onlyFolders,
350
610
  onlyFiles,
351
- { hasOnlyFilters, rootName, txtIgnore },
611
+ { hasOnlyFilters, rootName, txtIgnore, force: forceFlag },
352
612
  );
353
613
 
354
614
  // ── build output ──────────────────────────────────────────────────────────────
@@ -380,7 +640,7 @@ filePaths.forEach(({ abs, rel }) => {
380
640
  out.push(subDivider);
381
641
  out.push(`FILE: ${rel}`);
382
642
  out.push(subDivider);
383
- out.push(readContent(abs));
643
+ out.push(readContent(abs, forceFlag));
384
644
  });
385
645
 
386
646
  out.push("");
@@ -394,4 +654,14 @@ const sizeKB = (fs.statSync(outputFile).size / 1024).toFixed(1);
394
654
  console.log(`✅ Done!`);
395
655
  console.log(`📄 Output : ${outputFile}`);
396
656
  console.log(`📊 Size : ${sizeKB} KB`);
397
- console.log(`🗂️ Files : ${filePaths.length}\n`);
657
+ console.log(`🗂️ Files : ${filePaths.length}`);
658
+
659
+ if (copyToClipboardFlag) {
660
+ const content = fs.readFileSync(outputFile, 'utf8');
661
+ const success = copyToClipboard(content);
662
+ if (success) {
663
+ console.log(`📋 Copied to clipboard!`);
664
+ }
665
+ }
666
+
667
+ console.log('');
@@ -0,0 +1,75 @@
1
+ # PowerShell completion installation script for make-folder-txt
2
+
3
+ function Install-MakeFolderTxtCompletion {
4
+ param(
5
+ [switch]$Force,
6
+ [switch]$CurrentUser
7
+ )
8
+
9
+ $ErrorActionPreference = 'Stop'
10
+
11
+ # Determine PowerShell profile path
12
+ if ($CurrentUser) {
13
+ $profilePath = $PROFILE.CurrentUserCurrentHost
14
+ } else {
15
+ $profilePath = $PROFILE.AllUsersCurrentHost
16
+ }
17
+
18
+ # Create profile directory if it doesn't exist
19
+ $profileDir = Split-Path $profilePath -Parent
20
+ if (-not (Test-Path $profileDir)) {
21
+ try {
22
+ New-Item -ItemType Directory -Path $profileDir -Force | Out-Null
23
+ Write-Host "Created profile directory: $profileDir" -ForegroundColor Green
24
+ } catch {
25
+ Write-Error "Failed to create profile directory: $profileDir"
26
+ return
27
+ }
28
+ }
29
+
30
+ # Get the completion script content
31
+ $completionScriptPath = Join-Path $PSScriptRoot 'make-folder-txt-completion.ps1'
32
+ if (-not (Test-Path $completionScriptPath)) {
33
+ Write-Error "Completion script not found: $completionScriptPath"
34
+ return
35
+ }
36
+
37
+ $completionContent = Get-Content $completionScriptPath -Raw
38
+
39
+ # Check if completion is already installed
40
+ if (Test-Path $profilePath) {
41
+ $profileContent = Get-Content $profilePath -Raw
42
+ if ($profileContent -match 'make-folder-txt.*completion') {
43
+ if (-not $Force) {
44
+ Write-Host "make-folder-txt completion is already installed in $profilePath" -ForegroundColor Yellow
45
+ Write-Host "Use -Force to reinstall" -ForegroundColor Yellow
46
+ return
47
+ }
48
+ Write-Host "Removing existing completion..." -ForegroundColor Yellow
49
+ # Remove existing completion
50
+ $profileContent = $profileContent -replace '(?s)# make-folder-txt completion.*?Register-ArgumentComplester.*?Export-ModuleMember.*?\n', ''
51
+ Set-Content $profilePath $profileContent -Force
52
+ }
53
+ }
54
+
55
+ # Add completion to profile
56
+ $completionBlock = @"
57
+
58
+ # make-folder-txt completion
59
+ $completionContent
60
+ "@
61
+
62
+ try {
63
+ Add-Content $profilePath $completionBlock -Force
64
+ Write-Host "✅ PowerShell completion installed successfully!" -ForegroundColor Green
65
+ Write-Host "Added to: $profilePath" -ForegroundColor Cyan
66
+ Write-Host "Restart PowerShell or run: . `$profile" -ForegroundColor Cyan
67
+ } catch {
68
+ Write-Error "Failed to install completion: $_"
69
+ }
70
+ }
71
+
72
+ # Auto-install if script is run directly
73
+ if ($MyInvocation.InvocationName -eq $MyInvocation.MyCommand.Name) {
74
+ Install-MakeFolderTxtCompletion -CurrentUser
75
+ }
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ # make-folder-txt bash completion
3
+
4
+ _make_folder_txt_completion() {
5
+ local cur prev opts
6
+ COMPREPLY=()
7
+ cur="${COMP_WORDS[COMP_CWORD]}"
8
+ prev="${COMP_WORDS[COMP_CWORD-1]}"
9
+
10
+ opts="--ignore-folder -ifo --ignore-file -ifi --only-folder -ofo --only-file -ofi --copy --force --help --version -h -v"
11
+
12
+ case "${prev}" in
13
+ --ignore-folder|-ifo)
14
+ # Complete with folder names in current directory
15
+ local folders=$(ls -d */ 2>/dev/null | sed 's|/||g')
16
+ COMPREPLY=( $(compgen -W "${folders}" -- ${cur}) )
17
+ return 0
18
+ ;;
19
+ --ignore-file|-ifi|--only-file|-ofi)
20
+ # Complete with file names in current directory
21
+ local files=$(ls -p 2>/dev/null | grep -v /)
22
+ COMPREPLY=( $(compgen -W "${files}" -- ${cur}) )
23
+ return 0
24
+ ;;
25
+ --only-folder|-ofo)
26
+ # Complete with folder names in current directory
27
+ local folders=$(ls -d */ 2>/dev/null | sed 's|/||g')
28
+ COMPREPLY=( $(compgen -W "${folders}" -- ${cur}) )
29
+ return 0
30
+ ;;
31
+ *)
32
+ ;;
33
+ esac
34
+
35
+ if [[ ${cur} == -* ]]; then
36
+ COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) )
37
+ return 0
38
+ fi
39
+ }
40
+
41
+ complete -F _make_folder_txt_completion make-folder-txt
@@ -0,0 +1,67 @@
1
+ # make-folder-txt PowerShell completion script
2
+
3
+ Register-ArgumentCompleter -Native -CommandName 'make-folder-txt' -ScriptBlock {
4
+ param($commandName, $wordToComplete, $commandAst, $fakeBoundParameters)
5
+
6
+ # Get the current argument being completed
7
+ $currentArgument = $wordToComplete
8
+
9
+ # Define available options
10
+ $options = @(
11
+ '--ignore-folder', '-ifo',
12
+ '--ignore-file', '-ifi',
13
+ '--only-folder', '-ofo',
14
+ '--only-file', '-ofi',
15
+ '--copy',
16
+ '--force',
17
+ '--install-completion',
18
+ '--help', '-h',
19
+ '--version', '-v'
20
+ )
21
+
22
+ # Get the previous parameter to determine context
23
+ $previousParameter = if ($commandAst.CommandElements.Count -gt 1) {
24
+ $commandAst.CommandElements[-2].Extent.Text
25
+ } else {
26
+ ''
27
+ }
28
+
29
+ switch ($previousParameter) {
30
+ { $_ -in '--ignore-folder', '-ifo', '--only-folder', '-ofo' } {
31
+ # Complete with folder names
32
+ try {
33
+ $folders = Get-ChildItem -Directory -Name | Where-Object { $_ -like "*$currentArgument*" }
34
+ return $folders | ForEach-Object {
35
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Folder: $_")
36
+ }
37
+ } catch {
38
+ return @()
39
+ }
40
+ }
41
+ { $_ -in '--ignore-file', '-ifi', '--only-file', '-ofi' } {
42
+ # Complete with file names
43
+ try {
44
+ $files = Get-ChildItem -File -Name | Where-Object { $_ -like "*$currentArgument*" }
45
+ return $files | ForEach-Object {
46
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "File: $_")
47
+ }
48
+ } catch {
49
+ return @()
50
+ }
51
+ }
52
+ default {
53
+ # Complete with options
54
+ if ($currentArgument -like '-*') {
55
+ $matchingOptions = $options | Where-Object { $_ -like "*$currentArgument*" }
56
+ return $matchingOptions | ForEach-Object {
57
+ [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', "Option: $_")
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ return @()
64
+ }
65
+
66
+ # Export the completion function
67
+ Export-ModuleMember -Function *
@@ -0,0 +1,26 @@
1
+ #compdef make-folder-txt
2
+ # make-folder-txt zsh completion
3
+
4
+ _make_folder_txt() {
5
+ local -a arguments
6
+ arguments=(
7
+ '--ignore-folder[Ignore specific folders by name]:folder:_directories'
8
+ '-ifo[Ignore specific folders by name]:folder:_directories'
9
+ '--ignore-file[Ignore specific files by name]:file:_files'
10
+ '-ifi[Ignore specific files by name]:file:_files'
11
+ '--only-folder[Include only specific folders]:folder:_directories'
12
+ '-ofo[Include only specific folders]:folder:_directories'
13
+ '--only-file[Include only specific files]:file:_files'
14
+ '-ofi[Include only specific files]:file:_files'
15
+ '--copy[Copy output to clipboard]'
16
+ '--force[Include everything (overrides all ignore patterns)]'
17
+ '--help[Show help message]'
18
+ '--version[Show version information]'
19
+ '-h[Show help message]'
20
+ '-v[Show version information]'
21
+ )
22
+
23
+ _arguments -s -S $arguments && return 0
24
+ }
25
+
26
+ _make_folder_txt "$@"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "make-folder-txt",
3
- "version": "1.4.4",
3
+ "version": "2.0.1",
4
4
  "description": "Generate a single .txt file containing the full folder structure and file contents of any project, ignoring node_modules and other junk.",
5
5
  "main": "bin/make-folder-txt.js",
6
6
  "bin": {