mkctx 5.0.1 → 6.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +46 -9
  2. package/bin/mkctx.js +75 -14
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -24,6 +24,7 @@ A powerful command-line tool that generates comprehensive context files from you
24
24
  - šŸŽØ **Syntax Highlighting** — Proper language detection for code blocks
25
25
  - šŸ”„ **Dual Mode** — Interactive menu or fully non-interactive via CLI flags
26
26
  - šŸ“Š **Context Statistics** — Token estimation and file analysis
27
+ - šŸ—œļø **ZIP Export** — Bundle original files preserving the full directory structure
27
28
 
28
29
  ## Installation
29
30
 
@@ -76,11 +77,12 @@ After scanning, you choose a format and optionally a filename:
76
77
  Size: 156.23 KB
77
78
 
78
79
  ? Select output format:
79
- āÆ šŸ“¦ All formats (MD, JSON, TOON, XML)
80
+ āÆ šŸ“¦ All formats (MD, JSON, TOON, XML, ZIP)
80
81
  šŸ“ Markdown (.md)
81
82
  šŸ”§ JSON (.json) - Simple array
82
83
  šŸŽ’ TOON (.toon) - Token-optimized
83
84
  šŸ“„ XML (.xml)
85
+ šŸ—œļø ZIP (.zip) - Original files bundled
84
86
 
85
87
  ? Enter a name for the output files: (context)
86
88
  ```
@@ -99,7 +101,7 @@ When any of the following flags are passed, mkctx skips all prompts and runs dir
99
101
  | `--first-comment <text>` | | Override the first comment header | |
100
102
  | `--last-comment <text>` | | Override the last comment footer | |
101
103
 
102
- **Format values:** `md`, `json`, `toon`, `xml`, `all`, or comma-separated combinations.
104
+ **Format values:** `md`, `json`, `toon`, `xml`, `zip`, `all`, or comma-separated combinations.
103
105
 
104
106
  ```bash
105
107
  # Single format
@@ -111,6 +113,12 @@ mkctx --src . --format md,json --name snapshot
111
113
  # All formats with custom output directory
112
114
  mkctx --src . --format all --name my-project --output ./docs
113
115
 
116
+ # ZIP only — bundle original files
117
+ mkctx --src ./app --format zip --name snapshot
118
+
119
+ # ZIP combined with markdown
120
+ mkctx --src . --format md,zip --name my-project
121
+
114
122
  # Using short aliases
115
123
  mkctx -s ./src -f toon -n snapshot
116
124
 
@@ -164,32 +172,33 @@ The following are always ignored automatically: `.git`, `.svn`, `.hg`, `node_mod
164
172
  | `json` | `.json` | Simple JSON array of file objects |
165
173
  | `toon` | `.toon` | Token-Oriented Object Notation — compact, LLM-optimized |
166
174
  | `xml` | `.xml` | XML with CDATA sections |
175
+ | `zip` | `.zip` | Original files bundled, preserving directory structure |
167
176
 
168
177
  ### Markdown output example
169
178
 
170
179
  ````markdown
171
- /_ Project Context _/
180
+ /* Project Context */
172
181
 
173
182
  ## Project Structure
174
183
 
175
- \```
184
+ ```
176
185
  šŸ“ src/
177
186
  šŸ“ src/components/
178
187
 
179
188
  42 files total
180
- \```
189
+ ```
181
190
 
182
191
  ## Source Files
183
192
 
184
193
  ### src/index.ts
185
194
 
186
- \```typescript
195
+ ```typescript
187
196
  import { App } from './app';
188
197
  const app = new App();
189
198
  app.start();
190
- \```
199
+ ```
191
200
 
192
- /_ End of Context _/
201
+ /* End of Context */
193
202
  ````
194
203
 
195
204
  ### JSON output example
@@ -208,6 +217,27 @@ app.start();
208
217
  ]
209
218
  ```
210
219
 
220
+ ### ZIP output
221
+
222
+ The `zip` format bundles every scanned file into a `.zip` archive, preserving the full relative directory structure. This is useful for sharing a clean snapshot of your project or feeding files directly to tools that accept archives.
223
+
224
+ ```
225
+ context.zip
226
+ ā”œā”€ā”€ src/
227
+ │ ā”œā”€ā”€ index.ts
228
+ │ └── components/
229
+ │ └── App.tsx
230
+ ā”œā”€ā”€ package.json
231
+ └── README.md
232
+ ```
233
+
234
+ The ZIP format can be combined freely with other formats in the same run:
235
+
236
+ ```bash
237
+ mkctx --src . --format md,zip --name my-project
238
+ # Outputs: my-project.md + my-project.zip
239
+ ```
240
+
211
241
  ## Supported languages
212
242
 
213
243
  | Category | Extensions |
@@ -232,6 +262,7 @@ app.start();
232
262
  - **Onboarding** — Help new developers get oriented quickly
233
263
  - **Documentation** — Generate a versioned snapshot of your codebase
234
264
  - **CI/CD pipelines** — Use non-interactive flags to automate context generation
265
+ - **Project sharing** — Use the `zip` format to hand off a clean copy of your source files
235
266
 
236
267
  ## Platform support
237
268
 
@@ -259,6 +290,12 @@ Or fix npm permissions: https://docs.npmjs.com/resolving-eacces-permissions-erro
259
290
 
260
291
  ## Changelog
261
292
 
293
+ ### v6.0.0
294
+
295
+ - šŸ—œļø Added `zip` output format — bundles original files preserving directory structure
296
+ - šŸ“¦ `--format all` now includes `zip`
297
+ - āž• New dependency: [`archiver`](https://www.npmjs.com/package/archiver) (pure JS, OS-agnostic)
298
+
262
299
  ### v5.0.0
263
300
 
264
301
  - ✨ Added non-interactive CLI flags (`--src`, `--format`, `--output`, `--name`, `--ignore`, etc.)
@@ -291,4 +328,4 @@ Contributions are welcome! Feel free to open issues or submit pull requests.
291
328
 
292
329
  ## License
293
330
 
294
- MIT License — see [LICENSE](LICENSE) for details.
331
+ MIT License — see [LICENSE](LICENSE) for details.
package/bin/mkctx.js CHANGED
@@ -9,7 +9,7 @@ const path = require('path');
9
9
  // LAZY-LOADED DEPENDENCIES
10
10
  // ============================================
11
11
 
12
- let inquirer, chalk, ora;
12
+ let inquirer, chalk, ora, archiver;
13
13
 
14
14
  function loadDependencies() {
15
15
  if (inquirer) return;
@@ -18,6 +18,11 @@ function loadDependencies() {
18
18
  ora = require('ora');
19
19
  }
20
20
 
21
+ function loadArchiver() {
22
+ if (archiver) return;
23
+ archiver = require('archiver');
24
+ }
25
+
21
26
  // ============================================
22
27
  // CONSTANTS
23
28
  // ============================================
@@ -32,7 +37,7 @@ const DEFAULT_CONFIG = {
32
37
  last_comment: '/* End of Context */',
33
38
  };
34
39
 
35
- const VALID_FORMATS = ['json', 'md', 'toon', 'xml'];
40
+ const VALID_FORMATS = ['json', 'md', 'toon', 'xml', 'zip'];
36
41
 
37
42
  const LANG_MAP = {
38
43
  js: 'javascript', ts: 'typescript', jsx: 'jsx', tsx: 'tsx',
@@ -449,7 +454,7 @@ function toXml(files) {
449
454
  const fileEntries = files.map(f => [
450
455
  ' <file>',
451
456
  ` <path>${escapeXml(f.path)}</path>`,
452
- ` <name>${escapeXml(f.name)}</name>`,
457
+ ` <n>${escapeXml(f.name)}</n>`,
453
458
  ` <extension>${escapeXml(f.extension || '')}</extension>`,
454
459
  ` <language>${escapeXml(f.language)}</language>`,
455
460
  ` <lines>${f.lines}</lines>`,
@@ -510,6 +515,46 @@ function printSummary(stats) {
510
515
  console.log(chalk.white(` Size: ${formatSize(stats.totalSize)}`));
511
516
  }
512
517
 
518
+ // ============================================
519
+ // SAVE AS ZIP
520
+ // ============================================
521
+
522
+ async function saveAsZip(result, fileName) {
523
+ loadArchiver();
524
+
525
+ const outputDir = result.config.output || './mkctx';
526
+ fs.mkdirSync(outputDir, { recursive: true });
527
+
528
+ const zipPath = path.join(outputDir, `${fileName}.zip`);
529
+ const output = fs.createWriteStream(zipPath);
530
+ const archive = archiver('zip', { zlib: { level: 9 } });
531
+
532
+ return new Promise((resolve, reject) => {
533
+ output.on('close', () => {
534
+ const size = archive.pointer();
535
+ console.log(chalk.green('\nāœ… ZIP saved:\n'));
536
+ console.log(chalk.white(` ${chalk.cyan('ZIP ')} → ${chalk.yellow(zipPath)}`));
537
+ console.log(chalk.gray(` ${formatSize(size)} | ${result.files.length} files\n`));
538
+ resolve({ format: 'zip', file: zipPath, size, tokens: 0 });
539
+ });
540
+
541
+ archive.on('warning', err => {
542
+ if (err.code !== 'ENOENT') reject(err);
543
+ });
544
+
545
+ archive.on('error', reject);
546
+
547
+ archive.pipe(output);
548
+
549
+ // Each scanned file is added at its relative path, preserving directory structure
550
+ for (const file of result.files) {
551
+ archive.append(file.content, { name: file.path });
552
+ }
553
+
554
+ archive.finalize();
555
+ });
556
+ }
557
+
513
558
  // ============================================
514
559
  // SAVE CONTEXT
515
560
  // ============================================
@@ -531,7 +576,12 @@ async function saveContext(result, formats, fileName) {
531
576
 
532
577
  const savedFiles = [];
533
578
 
534
- for (const format of formats) {
579
+ // Separate zip from the rest of the formats
580
+ const zipRequested = formats.includes('zip');
581
+ const regularFormats = formats.filter(f => f !== 'zip');
582
+
583
+ // Handle regular text-based formats
584
+ for (const format of regularFormats) {
535
585
  const { content, ext } = renderFormat(format, result.files, result.stats, result.config);
536
586
  const outputPath = path.join(outputDir, `${fileName}.${ext}`);
537
587
  fs.writeFileSync(outputPath, content);
@@ -543,10 +593,18 @@ async function saveContext(result, formats, fileName) {
543
593
  });
544
594
  }
545
595
 
546
- console.log(chalk.green('\nāœ… Context saved:\n'));
547
- for (const { format, file, size, tokens } of savedFiles) {
548
- console.log(chalk.white(` ${chalk.cyan(format.toUpperCase().padEnd(4))} → ${chalk.yellow(file)}`));
549
- console.log(chalk.gray(` ${formatSize(size)} | ~${tokens.toLocaleString()} tokens\n`));
596
+ if (savedFiles.length > 0) {
597
+ console.log(chalk.green('\nāœ… Context saved:\n'));
598
+ for (const { format, file, size, tokens } of savedFiles) {
599
+ console.log(chalk.white(` ${chalk.cyan(format.toUpperCase().padEnd(4))} → ${chalk.yellow(file)}`));
600
+ console.log(chalk.gray(` ${formatSize(size)} | ~${tokens.toLocaleString()} tokens\n`));
601
+ }
602
+ }
603
+
604
+ // Handle zip format
605
+ if (zipRequested) {
606
+ const zipEntry = await saveAsZip(result, fileName);
607
+ savedFiles.push(zipEntry);
550
608
  }
551
609
 
552
610
  return savedFiles;
@@ -572,12 +630,13 @@ async function promptFormat() {
572
630
  message: 'Select output format:',
573
631
  default: 'all',
574
632
  choices: [
575
- { name: chalk.magenta('šŸ“¦ All formats (MD, JSON, TOON, XML)'), value: 'all' },
633
+ { name: chalk.magenta('šŸ“¦ All formats (MD, JSON, TOON, XML, ZIP)'), value: 'all' },
576
634
  new inquirer.Separator(),
577
- { name: chalk.blue('šŸ“ Markdown (.md)'), value: 'md' },
578
- { name: chalk.green('šŸ”§ JSON (.json) - Simple array'), value: 'json' },
579
- { name: chalk.yellow('šŸŽ’ TOON (.toon) - Token-optimized'), value: 'toon' },
580
- { name: chalk.red('šŸ“„ XML (.xml)'), value: 'xml' },
635
+ { name: chalk.blue('šŸ“ Markdown (.md)'), value: 'md' },
636
+ { name: chalk.green('šŸ”§ JSON (.json) - Simple array'), value: 'json' },
637
+ { name: chalk.yellow('šŸŽ’ TOON (.toon) - Token-optimized'), value: 'toon' },
638
+ { name: chalk.red('šŸ“„ XML (.xml)'), value: 'xml' },
639
+ { name: chalk.cyan('šŸ—œļø ZIP (.zip) - Original files bundled'), value: 'zip' },
581
640
  ],
582
641
  }]);
583
642
  return resolveFormats(format);
@@ -716,7 +775,7 @@ function showHelp() {
716
775
  ${chalk.yellow('Non-interactive flags (skip all prompts):')}
717
776
  --src <path> Source directory (default: .)
718
777
  --output <path> Output directory (default: ./mkctx)
719
- --format <fmt> md, json, toon, xml, all, or comma-separated
778
+ --format <fmt> md, json, toon, xml, zip, all, or comma-separated
720
779
  --name <filename> Output file base name (default: context)
721
780
  --ignore <patterns> Comma-separated ignore patterns
722
781
  --first-comment <text> Override first comment header
@@ -729,6 +788,7 @@ function showHelp() {
729
788
  mkctx --src ./src
730
789
  mkctx --src . --format all --name my-project --output ./docs
731
790
  mkctx --src ./app --format md,json --ignore "*.test.ts,__tests__/"
791
+ mkctx --src ./app --format zip --name snapshot
732
792
  mkctx -s ./src -f toon -n snapshot
733
793
 
734
794
  ${chalk.yellow('Output Formats:')}
@@ -736,6 +796,7 @@ function showHelp() {
736
796
  ${chalk.blue('MD')} Markdown with code blocks
737
797
  ${chalk.yellow('TOON')} Token-Oriented Object Notation (LLM optimized)
738
798
  ${chalk.red('XML')} XML with CDATA sections
799
+ ${chalk.cyan('ZIP')} Original files bundled, preserving directory structure
739
800
 
740
801
  ${chalk.gray('More info: https://github.com/pnkkzero/mkctx')}
741
802
  `));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mkctx",
3
- "version": "5.0.1",
3
+ "version": "6.0.0",
4
4
  "description": "Generate markdown context files from your project code and chat with AI using Ollama",
5
5
  "main": "bin/mkctx.js",
6
6
  "bin": {
@@ -58,6 +58,7 @@
58
58
  "win32"
59
59
  ],
60
60
  "dependencies": {
61
+ "archiver": "^7.0.1",
61
62
  "chalk": "^4.1.2",
62
63
  "conf": "^10.2.0",
63
64
  "inquirer": "^8.2.6",