Last active
January 16, 2025 17:02
-
-
Save stenuto/db2e61f499f7feee94c5640b0c82db82 to your computer and use it in GitHub Desktop.
A simple script to gather, annotate, and copy code to your clipboard
This file contains 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 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 <file|directory> [<file|directory>...]') | |
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) | |
} | |
})() |
Thanks for the gist. I forked it to fix the typo on line 161 (coipied => copied) but turns out you can't create a pull request for a gist π
Fixed! Thank you.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Thanks for the gist. I forked it to fix the typo on line 161 (coipied => copied) but turns out you can't create a pull request for a gist π