Skip to content

Instantly share code, notes, and snippets.

@park-brian
Last active January 22, 2025 18:38
Show Gist options
  • Save park-brian/9dc5e5188eed9a5d691d4c00db8232a0 to your computer and use it in GitHub Desktop.
Save park-brian/9dc5e5188eed9a5d691d4c00db8232a0 to your computer and use it in GitHub Desktop.
complete.js
#!/usr/bin/env node
const fs = require("fs");
const path = require("path");
const vm = require("vm");
const util = require("util");
const { OpenAI } = require("openai");
const [inputFile, attemptsArg, model = "deepseek-reasoner"] = process.argv.slice(2);
const maxAttempts = parseInt(attemptsArg) || 5;
const maxTokens = 8000;
const fastModel = "deepseek-chat";
const systemPrompt = "You are an expert programmer.";
if (!inputFile) {
console.log("Usage: ai-refactor <input-file> [max-attempts] [model]");
process.exit(1);
}
const versionDir = path.join(
"versions",
`${path.basename(inputFile, ".js")}_${new Date()
.toISOString()
.replace(/[:\-.]/g, "")
.slice(0, 15)}`
);
fs.mkdirSync(versionDir, { recursive: true });
let code = fs.readFileSync(inputFile, "utf8");
const initialCode = code;
const createContext = () => {
const logs = [];
const logger =
(level) =>
(...args) =>
logs.push(`${level}: ${args.map((e) => util.format(e)).join(" ")}`);
return {
context: { console: Object.fromEntries(["log", "error", "warn", "debug", "info"].map((l) => [l, logger(l)])) },
getLogs: () => logs.join("\n"),
};
};
const getCodeBlock = (text) => (text.match(/```(?:javascript|js)?\s*([\s\S]*?)```/)?.[1] || text)?.trim();
const getPartialCodeBlock = (text) => (text.match(/```(?:javascript|js)?\s*([\s\S]*)/)?.[1] || text)?.trim();
async function runModel(prompt, options = { model, max_tokens: maxTokens }) {
const openai = new OpenAI();
let messages = [
{ role: "system", content: systemPrompt },
{ role: "user", content: prompt },
{ role: "assistant", content: "```javascript\n", prefix: true },
];
while (true) {
const response = await openai.chat.completions.create({
messages,
stop: ["```"],
...options,
});
const content = response.choices[0].message.content;
const codeBlock = getCodeBlock(content);
if (codeBlock) return codeBlock;
const partialCodeBlock = getPartialCodeBlock(content);
messages = [messages[0], messages[1], { role: "assistant", content: partialCodeBlock, prefix: true }];
}
}
(async () => {
const versionFile = (attempt, ext) => path.join(versionDir, `attempt_${attempt}${ext}`);
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
const { context, getLogs } = createContext();
try {
fs.writeFileSync(versionFile(attempt, ".js"), code);
vm.runInNewContext(code, context);
fs.writeFileSync(versionFile(attempt, "_success_logs.txt"), getLogs());
console.log("✅ Tests passed!");
process.exit(0);
} catch (err) {
const error = err.stack || err.message;
const logs = getLogs();
fs.writeFileSync(versionFile(attempt, "_logs.txt"), `${logs}\n\n${error}`);
console.error(`Attempt ${attempt} failed:\n${logs}\n${error}`);
const prompt = `
Fix the entire codebase to resolve the error. Return the ENTIRE codebase.
Error: ${error}
Logs: ${logs}
Current Code:\n\`\`\`javascript\n${code}\n\`\`\`
${code !== initialCode ? `Initial Code:\n\`\`\`javascript\n${initialCode}\n\`\`\`` : ""}
`.replace(/^\s+/gm, "");
let newCode = await runModel(prompt);
if (newCode.length < code.length * 0.6) {
newCode = await runModel(
`
Combine these changes with existing codebase:\n
Proposed Changes:\n\`\`\`javascript\n${newCode}\n\`\`\`
Current Code:\n\`\`\`javascript\n${code}\n\`\`\`
`.replace(/^\s+/gm, ""),
{ model: fastModel, max_tokens: maxTokens }
);
}
if (!newCode || newCode === code) break;
code = newCode;
}
}
console.log("❌ All attempts failed");
process.exit(1);
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment