import { mkdirSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core"; import type { LlmProvider } from "@uncaged/nerve-workflow-utils"; import { createHermesRole, isDryRun } from "@uncaged/nerve-workflow-utils"; import { z } from "zod"; import { buildPublishPrompt } from "./prompt.js"; export const publishMetaSchema = z.object({ success: z.boolean().describe("true if git push and tea pr create both succeeded"), }); export type PublishMeta = z.infer; export type BuildPublishDeps = { provider: LlmProvider; nerveRoot: string; }; function logPath(nerveRoot: string): string { return join(nerveRoot, "logs", `solve-issue-publish-${Date.now()}.log`); } export function buildPublishRole({ provider, nerveRoot }: BuildPublishDeps): Role { const hermes = createHermesRole({ prompt: async (threadId) => buildPublishPrompt({ threadId, nerveRoot }), extract: { provider, schema: publishMetaSchema }, }); return async (start: StartStep, messages: WorkflowMessage[]): Promise> => { const file = logPath(nerveRoot); mkdirSync(join(file, ".."), { recursive: true }); if (isDryRun(start)) { const msg = "[dry-run] publish skipped (no git push / PR)"; writeFileSync(file, `${msg}\n`, "utf-8"); return { content: `[dry-run] publish skipped — log: ${file}`, meta: { success: true }, }; } try { return await hermes(start, messages); } catch (e) { const msg = e instanceof Error ? e.message : String(e); const body = `publish failed: ${msg}\n`; writeFileSync(file, body, "utf-8"); return { content: `publish failed: ${msg}\nLog: ${file}`, meta: { success: false }, }; } }; }