// API service for directive operations export interface DirectiveStepCounts { pending: number ready: number running: number completed: number failed: number skipped: number } export interface DirectiveSummary { id: string title: string goal: string status: string repositoryUrl: string prUrl: string prBranch: string createdAt: string updatedAt: string goalUpdatedAt: string stepCounts: DirectiveStepCounts version?: number pr_branch?: string | null } export interface DirectiveStep { id: string directiveId: string directive_id?: string name: string title?: string description: string | null taskPlan: string dependsOn: string[] status: string contractId: string /** @deprecated Use contractId instead */ taskId: string orderIndex: number sort_order?: number completedAt: string } export interface DirectiveWithSteps extends DirectiveSummary { steps: DirectiveStep[] reconcileMode: string } // Alias for compatibility with context-menu branch types export type Directive = DirectiveSummary async function apiFetch(path: string, options?: RequestInit): Promise { const response = await fetch(path, { ...options, headers: { 'Content-Type': 'application/json', ...options?.headers, }, }) if (!response.ok) { const body = await response.json().catch(() => ({ message: response.statusText })) throw new Error(body.message ?? body.error ?? `API error ${response.status}: ${response.statusText}`) } return response } export async function listDirectives(): Promise { const response = await apiFetch('/api/v1/directives') const data = await response.json() return data.directives || [] } export async function getDirective(id: string): Promise { const response = await apiFetch(`/api/v1/directives/${id}`) return response.json() } export async function getDirectiveSteps(id: string): Promise { const response = await apiFetch(`/api/v1/directives/${id}/steps`) return response.json() } export async function updateGoal(id: string, goal: string): Promise { const response = await apiFetch(`/api/v1/directives/${id}/goal`, { method: 'PUT', body: JSON.stringify({ goal }), }) return response.json() } export async function updateDirective( id: string, updates: { title?: string; goal?: string; version?: number }, ): Promise { const response = await apiFetch(`/api/v1/directives/${id}`, { method: 'PUT', body: JSON.stringify(updates), }) return response.json() } export async function cleanupDirective(id: string): Promise { await apiFetch(`/api/v1/directives/${id}/cleanup`, { method: 'POST' }) } export async function createPr(id: string): Promise<{ prUrl: string }> { const response = await apiFetch(`/api/v1/directives/${id}/create-pr`, { method: 'POST' }) return response.json() } export async function pickUpOrders(id: string): Promise { await apiFetch(`/api/v1/directives/${id}/pick-up-orders`, { method: 'POST' }) } export async function startDirective(id: string): Promise { const response = await apiFetch(`/api/v1/directives/${id}/start`, { method: 'POST' }) return response.json() } export async function pauseDirective(id: string): Promise { const response = await apiFetch(`/api/v1/directives/${id}/pause`, { method: 'POST' }) return response.json() } export async function getUserSetting(key: string): Promise { const response = await apiFetch(`/api/v1/user-settings/${key}`) return response.json() } export async function upsertUserSetting(key: string, value: any): Promise { await apiFetch('/api/v1/user-settings', { method: 'PUT', body: JSON.stringify({ key, value }), }) } // ---- Task control APIs ---- export async function sendTaskMessage(taskId: string, message: string): Promise { await apiFetch(`/api/v1/mesh/tasks/${taskId}/message`, { method: 'POST', body: JSON.stringify({ message }), }) } export async function stopTask(taskId: string): Promise { await apiFetch(`/api/v1/mesh/tasks/${taskId}/stop`, { method: 'POST', }) } export async function continueTask(taskId: string): Promise { await apiFetch(`/api/v1/mesh/tasks/${taskId}/continue`, { method: 'POST', }) } export async function startTask(taskId: string): Promise { await apiFetch(`/api/v1/mesh/tasks/${taskId}/start`, { method: 'POST', }) }