import { Injectable, Logger } from '@nestjs/common'; import { loadMission, getMissionStatus, getTaskStatus, parseTasksFile, type Mission, type MissionStatusSummary, type MissionTask, type TaskDetail, } from '@mosaicstack/coord'; import { promises as fs } from 'node:fs'; import path from 'node:path'; /** * File-based coord operations for agent tool consumption. * DB-backed mission CRUD is handled directly by MissionsController via Brain repos. */ @Injectable() export class CoordService { private readonly logger = new Logger(CoordService.name); async loadMission(projectPath: string): Promise { try { return await loadMission(projectPath); } catch (err) { this.logger.debug( `No coord mission at ${projectPath}: ${err instanceof Error ? err.message : String(err)}`, ); return null; } } async getMissionStatus(projectPath: string): Promise { const mission = await this.loadMission(projectPath); if (!mission) return null; try { return await getMissionStatus(mission); } catch (err) { this.logger.error( `Failed to get mission status: ${err instanceof Error ? err.message : String(err)}`, ); return null; } } async getTaskStatus(projectPath: string, taskId: string): Promise { const mission = await this.loadMission(projectPath); if (!mission) return null; try { return await getTaskStatus(mission, taskId); } catch (err) { this.logger.error( `Failed to get task status for ${taskId}: ${err instanceof Error ? err.message : String(err)}`, ); return null; } } async listTasks(projectPath: string): Promise { const mission = await this.loadMission(projectPath); if (!mission) return []; const tasksFile = path.isAbsolute(mission.tasksFile) ? mission.tasksFile : path.join(mission.projectPath, mission.tasksFile); try { const content = await fs.readFile(tasksFile, 'utf8'); return parseTasksFile(content); } catch { return []; } } }