Docs/Skills and code/Top-level code
dump-project.js
import fs from "node:fs";
import path from "node:path";
const ROOT = process.cwd();
const OUTPUT = path.join(
ROOT,
"project-dump.txt"
);
/*
* Directories that should not be dumped.
*
* These are either generated, extremely large,
* machine-specific, or not useful to an LLM
* trying to understand the project.
*/
const EXCLUDED_DIRECTORIES = new Set([
"node_modules",
".git",
".idea",
".vscode",
"data",
"dist",
"build",
"coverage"
]);
/*
* Specific files to exclude.
*/
const EXCLUDED_FILES = new Set([
"project-dump.txt",
".env"
]);
/*
* Files larger than this are skipped.
*
* This prevents accidentally dumping huge logs,
* generated files, or datasets.
*/
const MAX_FILE_SIZE =
2 * 1024 * 1024;
/*
* Extensions that are definitely binary or
* otherwise unsuitable for an LLM project dump.
*/
const BINARY_EXTENSIONS = new Set([
".db",
".sqlite",
".sqlite3",
".png",
".jpg",
".jpeg",
".gif",
".webp",
".bmp",
".ico",
".mp3",
".wav",
".ogg",
".mp4",
".mov",
".avi",
".mkv",
".zip",
".gz",
".tar",
".7z",
".pdf",
".woff",
".woff2",
".ttf",
".otf",
".exe",
".dll"
]);
function relativePath(filePath) {
return path
.relative(ROOT, filePath)
.split(path.sep)
.join("/");
}
function shouldExcludeDirectory(name) {
return EXCLUDED_DIRECTORIES.has(name);
}
function shouldExcludeFile(name) {
return (
EXCLUDED_FILES.has(name) ||
(name.startsWith(".env.") && name !== ".env.example")
);
}
function isBinaryExtension(filePath) {
const extension =
path.extname(filePath).toLowerCase();
return BINARY_EXTENSIONS.has(
extension
);
}
function isProbablyBinary(buffer) {
const sample =
buffer.subarray(
0,
Math.min(buffer.length, 8192)
);
for (const byte of sample) {
if (byte === 0) {
return true;
}
}
return false;
}
function readDirectory(directory) {
const entries =
fs.readdirSync(directory, {
withFileTypes: true
});
entries.sort((a, b) =>
a.name.localeCompare(
b.name,
undefined,
{
numeric: true,
sensitivity: "base"
}
)
);
const results = [];
for (const entry of entries) {
const fullPath =
path.join(
directory,
entry.name
);
if (entry.isDirectory()) {
if (
shouldExcludeDirectory(
entry.name
)
) {
continue;
}
results.push(
...readDirectory(fullPath)
);
continue;
}
if (!entry.isFile()) {
continue;
}
if (
shouldExcludeFile(
entry.name
)
) {
continue;
}
results.push(fullPath);
}
return results;
}
function buildTree(files) {
const root = {
type: "directory",
name: "",
children: new Map()
};
for (const filePath of files) {
const relative =
relativePath(filePath);
const parts =
relative.split("/");
let current = root;
for (
let i = 0;
i < parts.length;
i++
) {
const part = parts[i];
const isFile =
i === parts.length - 1;
if (
!current.children.has(part)
) {
current.children.set(
part,
isFile
? {
type: "file",
name: part
}
: {
type: "directory",
name: part,
children: new Map()
}
);
}
current =
current.children.get(part);
}
}
return root;
}
function renderTree(node, prefix = "") {
const entries =
[...node.children.entries()]
.sort(([nameA, valueA], [nameB, valueB]) => {
if (
valueA.type !== valueB.type
) {
return valueA.type ===
"directory"
? -1
: 1;
}
return nameA.localeCompare(
nameB,
undefined,
{
numeric: true,
sensitivity: "base"
}
);
});
const lines = [];
entries.forEach(
([name, child], index) => {
const last =
index === entries.length - 1;
const connector =
last ? "└── " : "├── ";
lines.push(
`${prefix}${connector}${name}`
);
if (
child.type === "directory"
) {
const nextPrefix =
prefix +
(last ? " " : "│ ");
lines.push(
renderTree(
child,
nextPrefix
)
);
}
}
);
return lines
.filter(Boolean)
.join("\n");
}
function readTextFile(filePath) {
const stat =
fs.statSync(filePath);
if (
stat.size >
MAX_FILE_SIZE
) {
return {
status: "too-large",
content:
`[SKIPPED: file is ${stat.size} bytes, ` +
`larger than the ${MAX_FILE_SIZE} byte limit]`
};
}
if (
isBinaryExtension(filePath)
) {
return {
status: "binary",
content:
"[SKIPPED: binary file]"
};
}
const buffer =
fs.readFileSync(filePath);
if (isProbablyBinary(buffer)) {
return {
status: "binary",
content:
"[SKIPPED: binary file]"
};
}
return {
status: "text",
content:
buffer.toString("utf8")
};
}
function addLineNumbers(content) {
const lines =
content.split(/\r?\n/);
const width =
String(lines.length).length;
return lines
.map((line, index) => {
const number =
String(index + 1)
.padStart(width, " ");
return `${number} | ${line}`;
})
.join("\n");
}
function createDump(files) {
const tree =
buildTree(files);
const output = [];
output.push(
"# PROJECT DUMP"
);
output.push("");
output.push(
`Generated: ${new Date().toISOString()}`
);
output.push(
`Root: ${ROOT}`
);
output.push(
`Files included: ${files.length}`
);
output.push("");
output.push(
"============================================================"
);
output.push(
"PROJECT STRUCTURE"
);
output.push(
"============================================================"
);
output.push("");
output.push(
path.basename(ROOT) + "/"
);
output.push(
renderTree(tree)
);
output.push("");
output.push(
"============================================================"
);
output.push(
"FILE CONTENTS"
);
output.push(
"============================================================"
);
output.push("");
for (const filePath of files) {
const relative =
relativePath(filePath);
const result =
readTextFile(filePath);
output.push(
"============================================================"
);
output.push(
`FILE: ${relative}`
);
output.push(
`STATUS: ${result.status}`
);
output.push(
"============================================================"
);
output.push("");
if (
result.status === "text"
) {
output.push(
addLineNumbers(
result.content
)
);
} else {
output.push(
result.content
);
}
output.push("");
}
return output.join("\n");
}
function main() {
console.log(
"[dump] Project root:",
ROOT
);
console.log(
"[dump] Scanning project..."
);
const files =
readDirectory(ROOT);
console.log(
`[dump] Found ${files.length} files.`
);
console.log(
"[dump] Building project dump..."
);
const dump =
createDump(files);
fs.writeFileSync(
OUTPUT,
dump,
"utf8"
);
const size =
fs.statSync(OUTPUT).size;
console.log(
"[dump] Created:",
OUTPUT
);
console.log(
`[dump] Size: ${size} bytes`
);
console.log(
"[dump] Done."
);
}
main();
Shown as it is in the repository at build time (2026-11-03); the home directory appears as <home>. If this page and the file differ, the file is right. Back to the catalogue.