import * as fs from "node:fs"; import * as path from "node:path"; import { writeFile, readFile, ensureDir } from "../utils/file.js"; const CI_DIR = ".ciagent"; export interface ProjectManifest { name: string; objective: string; created_at: string; phases: PhaseInfo[]; current_phase: number; status: "initializing" | "researching" | "planning" | "executing" | "verifying" | "complete" | "error"; } export interface PhaseInfo { id: number; name: string; status: "pending" | "active" | "complete" | "failed"; started_at?: string; completed_at?: string; } export interface DecisionsManifest { decisions: Array<{ id: string; decision: string; rationale: string; confidence: number; category: string; timestamp: string; }>; } export interface StateManifest { current_phase: number; current_stage: string; last_agent: string; last_action: string; updated_at: string; pipeline_progress: Record; } export class ArtifactManager { private projectPath: string; constructor(projectPath: string) { this.projectPath = projectPath; } private get ciDir(): string { return path.join(this.projectPath, CI_DIR); } ensureStructure(): void { ensureDir(this.ciDir); ensureDir(path.join(this.ciDir, "phases")); ensureDir(path.join(this.ciDir, "audit")); } isInitialized(): boolean { return fs.existsSync(path.join(this.ciDir, "PROJECT.md")); } writeProject(manifest: ProjectManifest): void { const lines = [ `# ${manifest.name}`, "", `**Objective**: ${manifest.objective}`, `**Created**: ${manifest.created_at}`, `**Status**: ${manifest.status}`, `**Current Phase**: ${manifest.current_phase}`, "", "## Phases", "", ]; for (const phase of manifest.phases) { lines.push( `- Phase ${phase.id}: ${phase.name} [${phase.status}]` ); } lines.push(""); writeFile(path.join(this.ciDir, "PROJECT.md"), lines.join("\n")); } writeDecisions(decisions: DecisionsManifest): void { const lines = [ "# Decisions Log", "", `Total decisions: ${decisions.decisions.length}`, "", ]; for (const d of decisions.decisions) { lines.push(`## ${d.id}: ${d.decision}`); lines.push(`- **Category**: ${d.category}`); lines.push(`- **Confidence**: ${(d.confidence * 100).toFixed(0)}%`); lines.push(`- **Rationale**: ${d.rationale}`); lines.push(`- **Timestamp**: ${d.timestamp}`); lines.push(""); } writeFile(path.join(this.ciDir, "DECISIONS.md"), lines.join("\n")); } writeState(state: StateManifest): void { writeJSON(path.join(this.ciDir, "STATE.md.json"), state); const lines = [ "# Project State", "", `**Current Phase**: ${state.current_phase}`, `**Current Stage**: ${state.current_stage}`, `**Last Agent**: ${state.last_agent}`, `**Last Action**: ${state.last_action}`, `**Updated**: ${state.updated_at}`, "", "## Pipeline Progress", "", ]; for (const [stage, complete] of Object.entries( state.pipeline_progress )) { lines.push(`- ${stage}: ${complete ? "✓" : "○"}`); } lines.push(""); writeFile(path.join(this.ciDir, "STATE.md"), lines.join("\n")); } readState(): StateManifest | null { const filePath = path.join(this.ciDir, "STATE.md.json"); if (!fs.existsSync(filePath)) return null; return JSON.parse(fs.readFileSync(filePath, "utf-8")); } savePipelineState(state: Record, currentPhase: number, currentStage: string, lastAgent: string, lastAction: string): void { const manifest: StateManifest = { current_phase: currentPhase, current_stage: currentStage, last_agent: lastAgent, last_action: lastAction, updated_at: new Date().toISOString(), pipeline_progress: state, }; this.writeState(manifest); } writePhaseArtifact( phase: number, artifactName: string, content: string ): void { const phaseDir = path.join(this.ciDir, "phases", `phase-${phase}`); ensureDir(phaseDir); writeFile(path.join(phaseDir, artifactName), content); } readPhaseArtifact( phase: number, artifactName: string ): string | null { const filePath = path.join( this.ciDir, "phases", `phase-${phase}`, artifactName ); return readFile(filePath); } } function writeJSON(filePath: string, data: unknown): void { writeFile(filePath, JSON.stringify(data, null, 2)); }