/** Propose README fixes and apply them only after you approve. */
import { execFileSync } from 'node:child_process';
import { existsSync } from 'node:fs';
import { readFile, unlink, writeFile } from 'node:fs/promises';
import { basename } from 'node:path';
import { createInterface } from 'node:readline/promises';
import OpenAI from 'openai';
const client = new OpenAI({ apiKey: process.env.ORCA_API_KEY!, baseURL: process.env.ORCA_BASE_URL! });
const agents = client.beta.agents;
const sessions = agents.sessions;
// Orca refuses to stage any one file over 16 MiB, however it was uploaded.
const STAGED_FILE_LIMIT = 16 * 1024 * 1024;
const INSTRUCTIONS = `You keep a README accurate. You work in a Linux sandbox with a shell.
The repository is in /workspace/repo.tar.gz. Extract it to /workspace/repo if it is
not there yet.
Check every concrete claim in README.md against the code: commands, flags, config keys,
file paths, function names, version numbers, and code examples.
Fix only what is wrong or missing. Keep the author's structure and voice.
Do not add marketing, badges, or sections nobody asked for.
Create /workspace/outputs if it does not exist.
Save the corrected file to /workspace/outputs/README.md, and save a list of each change,
with the file and line in the code that proves it, to /workspace/outputs/changes.md.
Reply with a one-paragraph summary. If the user rejects your edit, apply their
feedback and save both files again. Do not use emojis.`;
if (!existsSync('README.md')) {
console.error('No README.md here. Run this from the root of a Git repository.');
process.exit(1);
}
const snapshot = execFileSync('git', ['archive', '--format=tar.gz', 'HEAD'], { maxBuffer: 512 * 1024 * 1024 });
if (snapshot.length > STAGED_FILE_LIMIT) {
console.error('Snapshot is over the 16 MiB limit for one staged file. Archive a subfolder instead.');
process.exit(1);
}
/** Send one message, wait for the turn, and return its status and the reply. */
async function runTurn(sessionId: string, text: string) {
let status: string | undefined;
for await (const event of sessions.stream(sessionId, { input: text })) {
if (event.type === 'agent.session.turn.completed') status = event.turn.status;
}
for (const item of (await sessions.items.list(sessionId, { limit: 20 })).data as any[]) {
if (item.type === 'message' && item.role === 'assistant') {
return { status, reply: (item.content ?? []).map((p: any) => p.text).filter(Boolean).join(' ') };
}
}
return { status, reply: '' };
}
/** The newest artifact with this file name, as text. */
async function newest(sessionId: string, name: string) {
let latest: { id: string; created_at: number } | undefined;
for await (const artifact of sessions.artifacts.list(sessionId)) {
if (basename(artifact.path) === name && (!latest || artifact.created_at > latest.created_at)) latest = artifact;
}
if (!latest) return undefined;
return (await sessions.artifacts.content(latest.id, { session_id: sessionId })).text();
}
/** Print the proposed change as a diff. */
async function showDiff(proposed: string) {
await writeFile('README.proposed.md', proposed);
try {
execFileSync('git', ['--no-pager', 'diff', '--no-index', '--', 'README.md', 'README.proposed.md'], { stdio: 'inherit' });
} catch {
// git diff exits 1 when the files differ; the diff is already printed.
}
await unlink('README.proposed.md');
}
let uploaded: { id: string } | undefined;
let agent: { id: string } | undefined;
let session: { id: string } | undefined;
const prompt = createInterface({ input: process.stdin, output: process.stdout });
try {
uploaded = await client.files.create({ file: new File([snapshot], 'repo.tar.gz'), purpose: 'user_data' });
agent = await agents.create({ model: process.env.ORCA_MODEL!, name: 'readme-pass', instructions: INSTRUCTIONS });
session = await sessions.create({
agent_id: agent.id,
environment: {
type: 'openai_hosted',
network: { access: 'disabled' },
files: [{ type: 'file_id', file_id: uploaded.id, path: '/workspace/repo.tar.gz' }],
},
});
let message = 'Check README.md against the code and propose fixes.';
while (true) {
const { status, reply } = await runTurn(session.id, message);
const proposed = await newest(session.id, 'README.md');
if (status !== 'completed' || proposed === undefined) throw new Error(`No proposal (turn ${status}): ${reply}`);
console.log(reply, '\n');
if (proposed === (await readFile('README.md', 'utf8'))) {
console.log('README.md is already accurate.');
break;
}
await showDiff(proposed);
const answer = (await prompt.question('Allow this edit? 1 Yes 2 No: ')).trim();
if (answer === '1') {
await writeFile('README.md', proposed);
await writeFile('readme-changes.md', (await newest(session.id, 'changes.md')) ?? '');
console.log('README.md updated. Evidence in readme-changes.md.');
break;
}
message = 'I rejected that edit. ' + (await prompt.question('What should change? '));
}
} catch (error) {
console.error(error instanceof Error ? error.message : error);
process.exitCode = 1;
} finally {
prompt.close();
// Delete each resource on its own, so one failed delete does not skip the rest.
if (session) await sessions.delete(session.id).catch(() => {});
if (agent) await agents.delete(agent.id).catch(() => {});
if (uploaded) await client.files.delete(uploaded.id).catch(() => {});
}