fix(cli): remove side-effect from agent:end state updater (#133) #147

Merged
jason.woltje merged 3 commits from fix/tui-agent-end-state into main 2026-03-15 19:09:14 +00:00
Showing only changes of commit 8820299301 - Show all commits

View File

@@ -1,17 +1,30 @@
import {
BadRequestException,
Body,
Controller,
Delete,
Get,
HttpCode,
HttpStatus,
Inject,
NotFoundException,
Param,
Patch,
Post,
Query,
UseGuards,
} from '@nestjs/common';
import fs from 'node:fs';
import path from 'node:path';
import { AuthGuard } from '../auth/auth.guard.js';
import { CurrentUser } from '../auth/current-user.decorator.js';
import { CoordService } from './coord.service.js';
import type {
CreateDbMissionDto,
UpdateDbMissionDto,
CreateMissionTaskDto,
UpdateMissionTaskDto,
} from './coord.dto.js';
/** Walk up from cwd to find the monorepo root (has pnpm-workspace.yaml). */
function findMonorepoRoot(start: string): string {
@@ -49,6 +62,8 @@ function resolveAndValidatePath(raw: string | undefined): string {
export class CoordController {
constructor(@Inject(CoordService) private readonly coordService: CoordService) {}
// ── File-based coord endpoints (legacy) ──
@Get('status')
async missionStatus(@Query('projectPath') projectPath?: string) {
const resolvedPath = resolveAndValidatePath(projectPath);
@@ -70,4 +85,121 @@ export class CoordController {
if (!detail) throw new NotFoundException(`Task ${taskId} not found in coord mission`);
return detail;
}
// ── DB-backed mission endpoints ──
@Get('missions')
async listDbMissions(@CurrentUser() user: { id: string }) {
return this.coordService.getMissionsByUser(user.id);
}
@Get('missions/:id')
async getDbMission(@Param('id') id: string, @CurrentUser() user: { id: string }) {
const mission = await this.coordService.getMissionByIdAndUser(id, user.id);
if (!mission) throw new NotFoundException('Mission not found');
return mission;
}
@Post('missions')
async createDbMission(@Body() dto: CreateDbMissionDto, @CurrentUser() user: { id: string }) {
return this.coordService.createDbMission({
name: dto.name,
description: dto.description,
projectId: dto.projectId,
userId: user.id,
phase: dto.phase,
milestones: dto.milestones,
config: dto.config,
status: dto.status,
});
}
@Patch('missions/:id')
async updateDbMission(
@Param('id') id: string,
@Body() dto: UpdateDbMissionDto,
@CurrentUser() user: { id: string },
) {
const mission = await this.coordService.updateDbMission(id, user.id, dto);
if (!mission) throw new NotFoundException('Mission not found');
return mission;
}
@Delete('missions/:id')
@HttpCode(HttpStatus.NO_CONTENT)
async deleteDbMission(@Param('id') id: string, @CurrentUser() user: { id: string }) {
const deleted = await this.coordService.deleteDbMission(id, user.id);
if (!deleted) throw new NotFoundException('Mission not found');
}
// ── DB-backed mission task endpoints ──
@Get('missions/:missionId/mission-tasks')
async listMissionTasks(
@Param('missionId') missionId: string,
@CurrentUser() user: { id: string },
) {
const mission = await this.coordService.getMissionByIdAndUser(missionId, user.id);
if (!mission) throw new NotFoundException('Mission not found');
return this.coordService.getMissionTasksByMissionAndUser(missionId, user.id);
}
@Get('missions/:missionId/mission-tasks/:taskId')
async getMissionTask(
@Param('missionId') missionId: string,
@Param('taskId') taskId: string,
@CurrentUser() user: { id: string },
) {
const mission = await this.coordService.getMissionByIdAndUser(missionId, user.id);
if (!mission) throw new NotFoundException('Mission not found');
const task = await this.coordService.getMissionTaskByIdAndUser(taskId, user.id);
if (!task) throw new NotFoundException('Mission task not found');
return task;
}
@Post('missions/:missionId/mission-tasks')
async createMissionTask(
@Param('missionId') missionId: string,
@Body() dto: CreateMissionTaskDto,
@CurrentUser() user: { id: string },
) {
const mission = await this.coordService.getMissionByIdAndUser(missionId, user.id);
if (!mission) throw new NotFoundException('Mission not found');
return this.coordService.createMissionTask({
missionId,
taskId: dto.taskId,
userId: user.id,
status: dto.status,
description: dto.description,
notes: dto.notes,
pr: dto.pr,
});
}
@Patch('missions/:missionId/mission-tasks/:taskId')
async updateMissionTask(
@Param('missionId') missionId: string,
@Param('taskId') taskId: string,
@Body() dto: UpdateMissionTaskDto,
@CurrentUser() user: { id: string },
) {
const mission = await this.coordService.getMissionByIdAndUser(missionId, user.id);
if (!mission) throw new NotFoundException('Mission not found');
const updated = await this.coordService.updateMissionTask(taskId, user.id, dto);
if (!updated) throw new NotFoundException('Mission task not found');
return updated;
}
@Delete('missions/:missionId/mission-tasks/:taskId')
@HttpCode(HttpStatus.NO_CONTENT)
async deleteMissionTask(
@Param('missionId') missionId: string,
@Param('taskId') taskId: string,
@CurrentUser() user: { id: string },
) {
const mission = await this.coordService.getMissionByIdAndUser(missionId, user.id);
if (!mission) throw new NotFoundException('Mission not found');
const deleted = await this.coordService.deleteMissionTask(taskId, user.id);
if (!deleted) throw new NotFoundException('Mission task not found');
}
}