#!/usr/bin/env node /** * gather.js * * A CLI tool to gather code files from given directories/files, annotate them, * and copy the resulting annotated code blob into the clipboard. * * Usage: * node gather.js relative/path/to/dir relative/path/to/file.js ... * * This tool will: * - Recursively crawl directories and pick up files with common code extensions. * - Concatenate their contents into a single annotated blob. * - Copy the blob to your clipboard using `pbcopy`. * * Example prompt annotation (this will be included at the top of the blob): * * "Below is a codebase comprised of multiple files and directories. Each file is * annotated with a header so that you know its path and language. I'll use this * codebase as context for my next questions. Please carefully read through it * so that when I ask my questions, you can refer to the relevant parts of the code. * I will provide separate prompts after I paste this code." */ const fs = require('fs') const path = require('path') const { execSync } = require('child_process') // Extensions of files we consider to be "code" files const CODE_EXTENSIONS = [ '.js', '.ts', '.jsx', '.tsx', '.php', '.rb', '.py', '.java', '.cs', '.go', '.html', '.css', '.scss', '.sass', '.json', '.yml', '.yaml' ] // Recursively gather code files from a given path async function gatherFilesFromPath(rootPath) { let results = [] const stats = fs.statSync(rootPath) if (stats.isDirectory()) { const entries = fs.readdirSync(rootPath) for (const entry of entries) { const entryPath = path.join(rootPath, entry) const entryStats = fs.statSync(entryPath) if (entryStats.isDirectory()) { // Recurse into subdirectory const subResults = await gatherFilesFromPath(entryPath) results = results.concat(subResults) } else { // If it's a file, check extension const ext = path.extname(entryPath).toLowerCase() if (CODE_EXTENSIONS.includes(ext)) { results.push(entryPath) } } } } else if (stats.isFile()) { const ext = path.extname(rootPath).toLowerCase() if (CODE_EXTENSIONS.includes(ext)) { results.push(rootPath) } } return results } ;(async function main() { const args = process.argv.slice(2) if (args.length === 0) { console.error('Usage: gather.js [...]') process.exit(1) } // Gather all files from the specified arguments let allFiles = [] for (const arg of args) { const files = await gatherFilesFromPath(arg) allFiles = allFiles.concat(files) } // Deduplicate files (just in case) allFiles = [...new Set(allFiles)] // Create an annotated blob let blob = ` Below is a codebase comprised of multiple files and directories. Each file is annotated with a header so that you know its path and language. I'll use this codebase as context for my next questions. Please carefully read through it so that when I ask my questions, you can refer to the relevant parts of the code. I will provide separate prompts after I paste this code. ------------------------------------------------------------ ` for (const file of allFiles) { const relPath = path.relative(process.cwd(), file) const ext = path.extname(file).toLowerCase() const languageHint = (() => { // Simple heuristic: pick a language name based on extension for display purposes switch (ext) { case '.js': return 'JavaScript' case '.ts': return 'TypeScript' case '.jsx': return 'JavaScript (JSX)' case '.tsx': return 'TypeScript (TSX)' case '.php': return 'PHP' case '.rb': return 'Ruby' case '.py': return 'Python' case '.java': return 'Java' case '.cs': return 'C#' case '.go': return 'Go' case '.html': return 'HTML' case '.css': return 'CSS' case '.scss': case '.sass': return 'Sass/SCSS' case '.json': return 'JSON' case '.yml': case '.yaml': return 'YAML' default: return 'Code' } })() const code = fs.readFileSync(file, 'utf8') blob += `\n\n### File: ${relPath} (${languageHint})\n\`\`\`${languageHint.toLowerCase()}\n${code}\n\`\`\`\n` } blob += '\n------------------------------------------------------------\n' // Copy to clipboard using pbcopy (macOS) try { execSync('pbcopy', { input: blob }) console.log('✅ Prompt copied to clipboard') } catch (error) { console.error("Failed to copy to clipboard. Here's the blob for reference:\n") console.log(blob) } })()