diff --git a/src/trpc/context.ts b/src/trpc/context.ts index 5473d7e..ccde186 100644 --- a/src/trpc/context.ts +++ b/src/trpc/context.ts @@ -7,6 +7,7 @@ import type { EventBus, DomainEvent } from '../events/types.js'; import type { AgentManager } from '../agent/types.js'; +import type { TaskRepository } from '../db/repositories/task-repository.js'; // Re-export for convenience export type { EventBus, DomainEvent }; @@ -23,6 +24,8 @@ export interface TRPCContext { processCount: number; /** Agent manager for agent lifecycle operations (optional until server wiring complete) */ agentManager?: AgentManager; + /** Task repository for task CRUD operations (optional until server wiring complete) */ + taskRepository?: TaskRepository; } /** @@ -33,6 +36,7 @@ export interface CreateContextOptions { serverStartedAt: Date | null; processCount: number; agentManager?: AgentManager; + taskRepository?: TaskRepository; } /** @@ -47,5 +51,6 @@ export function createContext(options: CreateContextOptions): TRPCContext { serverStartedAt: options.serverStartedAt, processCount: options.processCount, agentManager: options.agentManager, + taskRepository: options.taskRepository, }; } diff --git a/src/trpc/router.ts b/src/trpc/router.ts index c45762b..b1dd2ee 100644 --- a/src/trpc/router.ts +++ b/src/trpc/router.ts @@ -9,6 +9,7 @@ import { initTRPC, TRPCError } from '@trpc/server'; import { z } from 'zod'; import type { TRPCContext } from './context.js'; import type { AgentInfo, AgentResult } from '../agent/types.js'; +import type { TaskRepository } from '../db/repositories/task-repository.js'; /** * Initialize tRPC with our context type. @@ -176,6 +177,19 @@ function requireAgentManager(ctx: TRPCContext) { return ctx.agentManager; } +/** + * Helper to ensure taskRepository is available in context. + */ +function requireTaskRepository(ctx: TRPCContext): TaskRepository { + if (!ctx.taskRepository) { + throw new TRPCError({ + code: 'INTERNAL_SERVER_ERROR', + message: 'Task repository not available', + }); + } + return ctx.taskRepository; +} + // ============================================================================= // Application Router with Procedures // ============================================================================= @@ -193,6 +207,9 @@ function requireAgentManager(ctx: TRPCContext) { * - getAgentByName: Get agent by name * - resumeAgent: Resume an agent waiting for input * - getAgentResult: Get result of agent's work + * - listTasks: List tasks for a plan + * - getTask: Get task by ID + * - updateTaskStatus: Update task status */ export const appRouter = router({ /** @@ -323,6 +340,61 @@ export const appRouter = router({ const agent = await resolveAgent(ctx, input); return agentManager.getResult(agent.id); }), + + // =========================================================================== + // Task Procedures + // =========================================================================== + + /** + * List tasks for a plan. + * Returns tasks ordered by order field. + */ + listTasks: publicProcedure + .input(z.object({ planId: z.string().min(1) })) + .query(async ({ ctx, input }) => { + const taskRepository = requireTaskRepository(ctx); + return taskRepository.findByPlanId(input.planId); + }), + + /** + * Get a task by ID. + * Throws NOT_FOUND if task doesn't exist. + */ + getTask: publicProcedure + .input(z.object({ id: z.string().min(1) })) + .query(async ({ ctx, input }) => { + const taskRepository = requireTaskRepository(ctx); + const task = await taskRepository.findById(input.id); + if (!task) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: `Task '${input.id}' not found`, + }); + } + return task; + }), + + /** + * Update a task's status. + * Returns the updated task. + */ + updateTaskStatus: publicProcedure + .input(z.object({ + id: z.string().min(1), + status: z.enum(['pending', 'in_progress', 'completed', 'blocked']), + })) + .mutation(async ({ ctx, input }) => { + const taskRepository = requireTaskRepository(ctx); + // Check task exists first + const existing = await taskRepository.findById(input.id); + if (!existing) { + throw new TRPCError({ + code: 'NOT_FOUND', + message: `Task '${input.id}' not found`, + }); + } + return taskRepository.update(input.id, { status: input.status }); + }), }); /**