const BASE = '/api' async function get(path: string): Promise { const res = await fetch(`${BASE}${path}`) if (!res.ok) throw new Error(`${res.status} ${res.statusText}`) return res.json() } async function post(path: string, body: unknown): Promise { const res = await fetch(`${BASE}${path}`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(body), }) if (!res.ok) throw new Error(`${res.status} ${res.statusText}`) return res.json() } export interface Project { id: string name: string path: string status: string priority: number tech_stack: string[] | null created_at: string total_tasks: number done_tasks: number active_tasks: number blocked_tasks: number review_tasks: number } export interface ProjectDetail extends Project { tasks: Task[] modules: Module[] decisions: Decision[] } export interface Task { id: string project_id: string title: string status: string priority: number assigned_role: string | null parent_task_id: string | null brief: Record | null spec: Record | null created_at: string updated_at: string } export interface Decision { id: number project_id: string task_id: string | null type: string category: string | null title: string description: string tags: string[] | null created_at: string } export interface Module { id: number project_id: string name: string type: string path: string description: string | null owner_role: string | null dependencies: string[] | null } export interface PipelineStep { id: number agent_role: string action: string output_summary: string | null success: boolean | number duration_seconds: number | null tokens_used: number | null model: string | null cost_usd: number | null created_at: string } export interface TaskFull extends Task { pipeline_steps: PipelineStep[] related_decisions: Decision[] } export interface PendingAction { type: string description: string original_item: Record options: string[] } export interface CostEntry { project_id: string project_name: string runs: number total_tokens: number total_cost_usd: number total_duration_seconds: number } export interface AuditItem { id: string reason: string } export interface AuditResult { success: boolean already_done: AuditItem[] still_pending: AuditItem[] unclear: AuditItem[] duration_seconds?: number cost_usd?: number error?: string } export const api = { projects: () => get('/projects'), project: (id: string) => get(`/projects/${id}`), task: (id: string) => get(`/tasks/${id}`), taskFull: (id: string) => get(`/tasks/${id}/full`), taskPipeline: (id: string) => get(`/tasks/${id}/pipeline`), cost: (days = 7) => get(`/cost?days=${days}`), createProject: (data: { id: string; name: string; path: string; tech_stack?: string[]; priority?: number }) => post('/projects', data), createTask: (data: { project_id: string; title: string; priority?: number; route_type?: string }) => post('/tasks', data), approveTask: (id: string, data?: { decision_title?: string; decision_description?: string; decision_type?: string; create_followups?: boolean }) => post<{ status: string; followup_tasks: Task[]; needs_decision: boolean; pending_actions: PendingAction[] }>(`/tasks/${id}/approve`, data || {}), resolveAction: (id: string, action: PendingAction, choice: string) => post<{ choice: string; result: unknown }>(`/tasks/${id}/resolve`, { action, choice }), rejectTask: (id: string, reason: string) => post<{ status: string }>(`/tasks/${id}/reject`, { reason }), runTask: (id: string, allowWrite = false) => post<{ status: string }>(`/tasks/${id}/run`, { allow_write: allowWrite }), bootstrap: (data: { path: string; id: string; name: string }) => post<{ project: Project }>('/bootstrap', data), auditProject: (projectId: string) => post(`/projects/${projectId}/audit`, {}), auditApply: (projectId: string, taskIds: string[]) => post<{ updated: string[]; count: number }>(`/projects/${projectId}/audit/apply`, { task_ids: taskIds }), }