Created
October 3, 2025 03:34
-
-
Save 7flash/4314d77a1be9ebfb911d53a7b01589d1 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env bun | |
| import * as fs from 'fs'; | |
| import * as path from 'path'; | |
| // --- Function for Usage Message --- | |
| function usage(): never { | |
| console.log(`Usage: bun run ${process.argv[1]} <input_file> [--cwd <root_directory>]`); | |
| console.log(''); | |
| console.log('Processes <input_file>, replacing file paths with their contents,'); | |
| console.log('and for directory paths, listing all files (non-recursively) with their contents.'); | |
| console.log(''); | |
| console.log('Recognized file paths:'); | |
| console.log(' - Relative paths starting with "./" or "../"'); | |
| console.log(' - Absolute paths starting with "/"'); | |
| console.log(' - Paths containing environment variables like "$HOME"'); | |
| console.log(''); | |
| console.log('Arguments:'); | |
| console.log(' <input_file> : Path to the input prompt file.'); | |
| console.log(' --cwd <root_directory> : (Optional) The directory to resolve relative file paths against.'); | |
| console.log(' If not provided, paths are resolved relative to the directory containing <input_file>.'); | |
| console.log(''); | |
| console.log('Output:'); | |
| console.log(' The output is saved to "<input_file_directory>/<input_basename>.resolved".'); | |
| console.log(' Relative paths in the output are computed relative to $HOME.'); | |
| process.exit(1); | |
| } | |
| // --- Argument Parsing --- | |
| const args = process.argv.slice(2); | |
| let inputFile = ''; | |
| let rootDir = ''; | |
| for (let i = 0; i < args.length; i++) { | |
| if (args[i] === '--cwd') { | |
| if (i + 1 < args.length) { | |
| rootDir = args[i + 1]; | |
| i++; | |
| } else { | |
| console.error('Error: --cwd requires a directory path.'); | |
| usage(); | |
| } | |
| } else if (args[i] === '-h' || args[i] === '--help') { | |
| usage(); | |
| } else if (!inputFile) { | |
| inputFile = args[i]; | |
| } else { | |
| console.error('Error: Only one input file can be specified.'); | |
| usage(); | |
| } | |
| } | |
| // --- Validate Arguments --- | |
| if (!inputFile) { | |
| console.error('Error: Input file path is required.'); | |
| usage(); | |
| } | |
| // Check if the input file exists and is readable | |
| if (!fs.existsSync(inputFile)) { | |
| console.error(`Error: Input file '${inputFile}' not found.`); | |
| process.exit(1); | |
| } | |
| try { | |
| fs.accessSync(inputFile, fs.constants.R_OK); | |
| } catch { | |
| console.error(`Error: Input file '${inputFile}' is not readable.`); | |
| process.exit(1); | |
| } | |
| // --- Validate $HOME --- | |
| const homeDir = process.env.HOME; | |
| if (!homeDir) { | |
| console.error('Error: $HOME environment variable is not defined.'); | |
| process.exit(1); | |
| } | |
| const homeDirPath = path.resolve(homeDir); | |
| if (!fs.existsSync(homeDirPath) || !fs.statSync(homeDirPath).isDirectory()) { | |
| console.error(`Error: $HOME directory '${homeDirPath}' is not a valid directory.`); | |
| process.exit(1); | |
| } | |
| // --- Determine Root Directory for Path Resolution --- | |
| let rootDirPath: string; | |
| if (rootDir) { | |
| rootDirPath = path.resolve(rootDir); | |
| if (!fs.existsSync(rootDirPath) || !fs.statSync(rootDirPath).isDirectory()) { | |
| console.error(`Error: Provided --cwd path '${rootDir}' is not a valid directory.`); | |
| process.exit(1); | |
| } | |
| console.log(`Info: Resolving "./" or "../" paths relative to specified --cwd directory: '${rootDirPath}'`); | |
| } else { | |
| rootDirPath = path.resolve(path.dirname(inputFile)); | |
| console.log(`Info: Resolving "./" or "../" paths relative to input file's directory: '${rootDirPath}'`); | |
| } | |
| // --- Set Output File Path --- | |
| const inputDir = path.dirname(inputFile); | |
| const inputBaseName = path.basename(inputFile); | |
| const outputFile = path.join(inputDir, `${inputBaseName}.resolved`); | |
| console.log('Starting processing...'); | |
| console.log(`Input file : '${inputFile}'`); | |
| console.log(`Output file: '${outputFile}'`); | |
| console.log(`Resolve dir: '${rootDirPath}'`); | |
| console.log(`Relative paths computed from: '${homeDirPath}'`); | |
| // --- Function to Expand Environment Variables in Path --- | |
| function expandEnvVars(pathString: string): string { | |
| return pathString.replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (match, envVar) => { | |
| const envValue = process.env[envVar]; | |
| return envValue !== undefined ? envValue : match; | |
| }); | |
| } | |
| // --- Function to Process Path (Handle Relative, Absolute, and Env Vars) --- | |
| function processPath(inputPath: string, rootDir: string): string { | |
| const expandedPath = expandEnvVars(inputPath); | |
| if (path.isAbsolute(expandedPath)) { | |
| return expandedPath; | |
| } | |
| return path.resolve(rootDir, expandedPath); | |
| } | |
| // --- Function to Get Relative Path from $HOME --- | |
| function getHomeRelativePath(fullPath: string): string { | |
| const relPath = path.relative(homeDirPath, fullPath).replace(/\\/g, '/'); | |
| return relPath.startsWith('.') ? relPath : `./${relPath}`; | |
| } | |
| const possibleExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mjs', '.cjs']; | |
| // --- Function to Find File with Different Extensions --- | |
| function findFileWithPossibleExtensions(basePath: string): string | null { | |
| // If the file exists as specified and is a file, return it | |
| if (fs.existsSync(basePath) && fs.statSync(basePath).isFile()) { | |
| return basePath; | |
| } | |
| // If there's already an extension, try removing it to get the base path without extension | |
| let basePathWithoutExt = basePath; | |
| const ext = path.extname(basePath); | |
| if (ext) { | |
| basePathWithoutExt = basePath.substring(0, basePath.length - ext.length); | |
| } | |
| // Try different extensions on the base path | |
| for (const extension of possibleExtensions) { | |
| const candidatePath = basePathWithoutExt + extension; | |
| if (fs.existsSync(candidatePath) && fs.statSync(candidatePath).isFile()) { | |
| return candidatePath; | |
| } | |
| } | |
| // If not found with any extension, return null | |
| return null; | |
| } | |
| // --- Process the Input File --- | |
| try { | |
| const content = fs.readFileSync(inputFile, 'utf8'); | |
| const lines = content.split('\n'); | |
| let outputContent = ''; | |
| // Process each line of the input file | |
| for (let i = 0; i < lines.length; i++) { | |
| const line = lines[i]; | |
| const trimmedLine = line.trim(); | |
| const isPathReference = trimmedLine.startsWith('./') || | |
| trimmedLine.startsWith('../') || | |
| trimmedLine.startsWith('/') || | |
| trimmedLine.startsWith('$'); | |
| if (isPathReference) { | |
| const pathFromFile = trimmedLine; | |
| const processedPath = processPath(pathFromFile, rootDirPath); | |
| if (fs.existsSync(processedPath)) { | |
| try { | |
| const stats = fs.statSync(processedPath); | |
| if (stats.isDirectory()) { | |
| // Handle directory: list all files non-recursively | |
| const entries = fs.readdirSync(processedPath, { withFileTypes: true }); | |
| for (const entry of entries) { | |
| if (entry.isFile()) { | |
| const fullPath = path.join(processedPath, entry.name); | |
| const relPath = getHomeRelativePath(fullPath); | |
| const content = fs.readFileSync(fullPath, 'utf8'); | |
| outputContent += `${relPath}\n\`\`\`\n${content}\n\`\`\`\n`; | |
| } | |
| } | |
| } else if (stats.isFile()) { | |
| // Handle file | |
| const relPath = getHomeRelativePath(processedPath); | |
| const content = fs.readFileSync(processedPath, 'utf8'); | |
| outputContent += `${relPath}\n\`\`\`\n${content}\n\`\`\`\n`; | |
| } else { | |
| console.error(`Warning: Path '${processedPath}' is neither a file nor a directory. Keeping original line.`); | |
| outputContent += line + '\n'; | |
| } | |
| } catch (err) { | |
| console.error(`Warning: Could not access path '${processedPath}': ${err.message}. Keeping original line.`); | |
| outputContent += line + '\n'; | |
| } | |
| } else { | |
| // Try to find file with possible extensions | |
| const actualFilePath = findFileWithPossibleExtensions(processedPath); | |
| if (actualFilePath) { | |
| try { | |
| fs.accessSync(actualFilePath, fs.constants.R_OK); | |
| const relPath = getHomeRelativePath(actualFilePath); | |
| const content = fs.readFileSync(actualFilePath, 'utf8'); | |
| outputContent += `${relPath}\n\`\`\`\n${content}\n\`\`\`\n`; | |
| } catch (err) { | |
| console.error(`Warning: File path '${actualFilePath}' not readable. Keeping original line.`); | |
| outputContent += line + '\n'; | |
| } | |
| } else { | |
| console.error(`Warning: Could not find any file matching '${pathFromFile}' (processed as '${processedPath}'). Tried various extensions. Keeping original line.`); | |
| outputContent += line + '\n'; | |
| } | |
| } | |
| } else { | |
| outputContent += line + '\n'; | |
| } | |
| } | |
| // Write the entire content to the file at once | |
| fs.writeFileSync(outputFile, outputContent); | |
| // Verify output file | |
| try { | |
| const stats = fs.statSync(outputFile); | |
| console.log(`Success: Output file '${outputFile}' created successfully.`); | |
| console.log(`File size: ${stats.size} bytes`); | |
| if (stats.size === 0) { | |
| console.warn(`Warning: Output file is empty. No content was processed.`); | |
| } | |
| } catch (err) { | |
| console.error(`Error: Failed to verify output file creation: ${err.message}`); | |
| } | |
| } catch (err) { | |
| console.error(`Error: Failed to process input file '${inputFile}': ${err.message}`); | |
| if (fs.existsSync(outputFile)) { | |
| try { | |
| fs.unlinkSync(outputFile); | |
| } catch (cleanupErr) { | |
| console.error(`Error during cleanup: ${cleanupErr.message}`); | |
| } | |
| } | |
| process.exit(1); | |
| } | |
| // --- Clipboard Instructions --- | |
| console.log(`Processing finished. Output saved to '${outputFile}'.`); | |
| console.log(''); | |
| console.log('--- Clipboard Instructions ---'); | |
| console.log(`To copy the contents of '${outputFile}' to your clipboard, use one of:`); | |
| if (process.platform === 'darwin') { | |
| console.log(` macOS: cat '${outputFile}' | pbcopy`); | |
| } | |
| if (process.platform === 'linux') { | |
| if (fs.existsSync('/usr/bin/xclip')) { | |
| console.log(` Linux X11: cat '${outputFile}' | xclip -selection clipboard`); | |
| } | |
| if (fs.existsSync('/usr/bin/wl-copy')) { | |
| console.log(` Linux Wayland: cat '${outputFile}' | wl-copy`); | |
| } | |
| } | |
| if (process.platform === 'win32') { | |
| console.log(` Windows: cat '${outputFile}' | clip`); | |
| } | |
| process.exit(0); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment