import type { AgentFn, Role, ThreadContext } from "@uncaged/nerve-core"; import type { LlmExtractorConfig } from "@uncaged/nerve-workflow-utils"; import { createRole } from "@uncaged/nerve-workflow-utils"; import { z } from "zod"; function reviewPrompt({ threadId, nerveRoot }: { threadId: string; nerveRoot: string }): string { return `You are a **code reviewer** (Hermes). You run after implement and before test. Read Nerve workspace conventions: \`cat ${nerveRoot}/CONVENTIONS.md\` Read workflow context: \`nerve thread show ${threadId}\` Find **repo path** from \`---SOLVE_ISSUE_REPO--- path:\` in the thread (prepare step). \`cd\` there before any git commands. ## Static analysis Run: 1. \`cd && git diff --stat\` 2. \`cd && git diff\` 3. \`cd && git status --short\` ## Checklist Reject (**approved: false**) if you find: - Garbage files, secrets/credentials, unrelated changes - Violations of CONVENTIONS.md (e.g. \`interface\` vs \`type\`, dynamic \`import()\`) Approve (**approved: true**) if the diff is clean and focused. End with: \`\`\`json { "approved": true } \`\`\` or \`\`\`json { "approved": false } \`\`\``; } export const reviewMetaSchema = z.object({ approved: z.boolean().describe("true if diff is clean and ready for tests"), }); export type ReviewMeta = z.infer; export function createReviewRole( adapter: AgentFn, extract: LlmExtractorConfig, nerveRoot: string, ): Role { return createRole( adapter, async (ctx: ThreadContext) => reviewPrompt({ threadId: ctx.start.meta.threadId, nerveRoot }), reviewMetaSchema, extract, ); }