import fs from "node:fs"; import path from "node:path"; import { z } from "zod"; import { type CommandResult, success, failure } from "../../../lib/command-result"; import { ProgressInput, ProgressEntry, InputSchemaMap, EventType, type GitContext, } from "../events"; import { getGitContext } from "./git-context"; function findErpKitRoot(cwd: string): string { let dir = path.resolve(cwd); const { root } = path.parse(dir); while (dir !== root) { if (fs.existsSync(path.join(dir, ".erp-kit"))) { return dir; } dir = path.dirname(dir); } return cwd; } function formatValidationErrors(parsed: unknown, fallbackError: z.ZodError): string { const validEvents = EventType.options; const input = parsed as Record | null; const event = input?.event; if (typeof event === "string" && event in InputSchemaMap) { const variantResult = InputSchemaMap[event].safeParse(parsed); if (!variantResult.success) { return `Invalid progress input for event "${event}":\n${z.prettifyError(variantResult.error)}`; } } if (typeof event === "string" && !(event in InputSchemaMap)) { return `Unknown event type "${event}". Valid events: ${validEvents.join(", ")}`; } return `Invalid progress input:\n${z.prettifyError(fallbackError)}\n\nValid events: ${validEvents.join(", ")}`; } export function runAppProgressLog(jsonInput: string, dryRun: boolean, cwd: string): CommandResult { let parsed: unknown; try { parsed = JSON.parse(jsonInput); } catch { return failure("Invalid JSON input"); } const inputResult = ProgressInput.safeParse(parsed); if (!inputResult.success) { return failure(formatValidationErrors(parsed, inputResult.error)); } // Progress entries require git context, so fail clearly outside a git repo let git: GitContext; try { git = getGitContext(cwd); } catch { return failure("Failed to resolve git context: not a git repository, or git is not installed"); } const enriched = { ...inputResult.data, timestamp: new Date().toISOString(), git, }; const entryResult = ProgressEntry.safeParse(enriched); if (!entryResult.success) { return failure( `Invalid progress entry after enrichment:\n${z.prettifyError(entryResult.error)}`, ); } const line = JSON.stringify(entryResult.data); console.log(line); if (dryRun) { return success(); } const root = findErpKitRoot(cwd); const erpKitDir = path.join(root, ".erp-kit"); fs.mkdirSync(erpKitDir, { recursive: true }); fs.appendFileSync(path.join(erpKitDir, "progress.jsonl"), line + "\n", "utf-8"); return success(); }