Skip to content

Instantly share code, notes, and snippets.

@RahulDas-dev
Created June 28, 2026 10:08
Show Gist options
  • Select an option

  • Save RahulDas-dev/0b5fc07d7a24429fd48c75d26e81b94b to your computer and use it in GitHub Desktop.

Select an option

Save RahulDas-dev/0b5fc07d7a24429fd48c75d26e81b94b to your computer and use it in GitHub Desktop.
Fully functional, and production-ready unpack script that undo the remix packing
const fs = require('fs');
const path = require('path');
// Configuration: The script will search for these common output names
const DEFAULT_INPUT_FILES = ['repomix-output.xml', 'repomix-output.md', 'repopack-output.xml', 'repopack-output.md'];
function findInputFile() {
for (const file of DEFAULT_INPUT_FILES) {
if (fs.existsSync(file)) {
return file;
}
}
return null;
}
function unpack() {
const inputFile = findInputFile();
if (!inputFile) {
console.error('❌ Error: No packed file found in the current directory.');
console.error(`Looking for one of: ${DEFAULT_INPUT_FILES.join(', ')}`);
process.exit(1);
}
console.log(`πŸ“– Reading ${inputFile}...`);
const content = fs.readFileSync(inputFile, 'utf-8');
let fileCount = 0;
console.log('πŸ—οΈ Analyzing format and reconstructing...');
// Identify whether it is XML or Markdown
if (content.includes('<file filePath=') || content.includes('<file path=')) {
fileCount = parseXML(content);
} else if (content.includes('### File:')) {
fileCount = parseMarkdown(content);
} else {
console.error('❌ Error: Could not determine the packing format. The file does not seem to contain Repomix/Repopack structure.');
process.exit(1);
}
if (fileCount > 0) {
console.log(`\nπŸŽ‰ Success! Successfully recreated ${fileCount} files.`);
} else {
console.log('\n⚠️ No files were extracted. Ensure the packed file contains intact file tags.');
}
}
/**
* Parses XML style output: <file filePath="path/to/file">...</file> or <file path="path/to/file">...</file>
* Uses string indices instead of RegExp to prevent catastrophic backtracking on large files.
*/
function parseXML(content) {
let count = 0;
let currentIndex = 0;
while (true) {
// Look for file opening tag
let startIdx = content.indexOf('<file filePath="', currentIndex);
let pathAttrLength = 16; // Length of '<file filePath="'
if (startIdx === -1) {
// Fallback to path="..." which is the format in your uploaded file!
startIdx = content.indexOf('<file path="', currentIndex);
pathAttrLength = 12; // Length of '<file path="'
}
if (startIdx === -1) break; // No more files found
// Extract the filepath
const pathStart = startIdx + pathAttrLength;
const pathEnd = content.indexOf('"', pathStart);
if (pathEnd === -1) break;
const relativePath = content.substring(pathStart, pathEnd);
// Find the end of the opening tag (could have custom attributes or just close with >)
const tagEnd = content.indexOf('>', pathEnd);
if (tagEnd === -1) break;
// Find the closing tag
const closeTagIdx = content.indexOf('</file>', tagEnd);
if (closeTagIdx === -1) break;
let fileContent = content.substring(tagEnd + 1, closeTagIdx);
// Clean up structural formatting spacing added by Repomix
if (fileContent.startsWith('\n')) fileContent = fileContent.slice(1);
if (fileContent.endsWith('\n')) fileContent = fileContent.slice(0, -1);
writeFile(relativePath, fileContent);
count++;
// Advance search index
currentIndex = closeTagIdx + 7; // 7 is length of '</file>'
}
return count;
}
/**
* Parses Markdown style output:
* ### File: path/to/file
* ```
* content
* ```
*/
function parseMarkdown(content) {
let count = 0;
const lines = content.split(/\r?\n/);
let currentFile = null;
let currentContent = [];
let insideCodeBlock = false;
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.startsWith('### File:')) {
// Save prior file if any
if (currentFile) {
saveMarkdownFile(currentFile, currentContent);
count++;
}
currentFile = line.replace('### File:', '').trim();
currentContent = [];
insideCodeBlock = false;
continue;
}
if (currentFile !== null) {
// Manage skipping markdown codefence wrappers (```javascript ... ```)
if (line.startsWith('```')) {
if (!insideCodeBlock) {
insideCodeBlock = true;
continue; // Skip the opening code fence line
} else {
// Saving current file content on closing fence
saveMarkdownFile(currentFile, currentContent);
count++;
currentFile = null;
currentContent = [];
insideCodeBlock = false;
continue;
}
}
currentContent.push(line);
}
}
// Edgecase cleanup for files without formal closing fences
if (currentFile && currentContent.length > 0) {
saveMarkdownFile(currentFile, currentContent);
count++;
}
return count;
}
function saveMarkdownFile(relativePath, lines) {
let fileContent = lines.join('\n');
writeFile(relativePath, fileContent);
}
function writeFile(relativePath, fileContent) {
// Guard against path traversal attacks (prevent writing files outside working directory)
const safeRelativePath = path.normalize(relativePath).replace(/^(\.\.(\/|\\))+/, '');
const targetPath = path.resolve(process.cwd(), safeRelativePath);
const dirName = path.dirname(targetPath);
try {
if (!fs.existsSync(dirName)) {
fs.mkdirSync(dirName, { recursive: true });
}
fs.writeFileSync(targetPath, fileContent, 'utf-8');
console.log(` + Created: ${safeRelativePath}`);
} catch (err) {
console.error(`❌ Failed to write file: ${safeRelativePath}`, err.message);
}
}
unpack();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment