CLIv1.0.0· by Motion Mavericks
bundle
Pack a set of files into one Markdown document ready to paste into an AI chat.
universal#cli#context#workflow
command.md · markdown
---
name: bundle
description: Pack a set of files into one Markdown document ready to paste into an AI chat.
platforms: [universal]
tags: [cli, context, workflow]
version: 1.0.0
author: Motion Mavericks
license: MIT
---
# bundle
A zero-dependency Node script that concatenates the files you name into a single
fenced Markdown document — the fastest way to hand an AI a coherent slice of a
codebase without copy-pasting file by file.
## Usage
```bash
node bin/bundle.mjs src/index.ts src/util.ts > context.md
```
Each file is emitted with a heading and a language-tagged code fence. Paths that
do not exist are reported to stderr and skipped, so a bad glob never corrupts the
output. Exits non-zero if no file could be read. Files containing backtick fences
are wrapped in a longer fence automatically, so bundling Markdown stays valid.
## Install
Copy `bin/bundle.mjs` anywhere on your `PATH` (or into a project's `scripts/`).
Requires Node 18+.
bin/bundle.mjs · js
#!/usr/bin/env node
// bundle — pack named files into one Markdown document for pasting into an AI chat.
// Usage: node bundle.mjs <file> [<file> ...] > context.md
import { readFile } from "node:fs/promises";
import { extname, relative } from "node:path";
const EXT_LANG = {
".ts": "ts", ".tsx": "tsx", ".js": "js", ".jsx": "jsx", ".mjs": "js",
".py": "python", ".rb": "ruby", ".go": "go", ".rs": "rust", ".java": "java",
".json": "json", ".yaml": "yaml", ".yml": "yaml", ".md": "markdown",
".sh": "bash", ".css": "css", ".html": "html", ".sql": "sql",
};
const files = process.argv.slice(2);
if (files.length === 0) {
console.error("usage: bundle <file> [<file> ...]");
process.exit(1);
}
const out = [];
for (const file of files) {
try {
const body = await readFile(file, "utf8");
const lang = EXT_LANG[extname(file)] ?? "";
// Fence one backtick longer than the longest run in the body, so files that
// themselves contain ``` (every Markdown code block) don't break out.
const runs = body.match(/`+/g) ?? [];
const fence = "`".repeat(Math.max(3, ...runs.map((r) => r.length + 1)));
out.push(`## ${relative(process.cwd(), file)}\n\n${fence}${lang}\n${body.replace(/\s+$/, "")}\n${fence}\n`);
} catch (err) {
console.error(`skipped ${file}: ${err.code === "ENOENT" ? "not found" : err.message}`);
}
}
if (out.length === 0) {
console.error("bundle: no files bundled");
process.exit(1);
}
process.stdout.write(out.join("\n"));