diff --git a/src/app/api/agent/cards/[cardId]/subtasks/route.ts b/src/app/api/agent/cards/[cardId]/subtasks/route.ts new file mode 100644 index 0000000..a9d9ae2 --- /dev/null +++ b/src/app/api/agent/cards/[cardId]/subtasks/route.ts @@ -0,0 +1,68 @@ +import { getAdminPb } from "@/lib/pb/admin"; +import { requireAgentToken } from "@/lib/agent/auth"; +import { positionBetween } from "@/lib/ordering/fractional"; +import { jsonError, jsonOk } from "@/lib/http/json"; +import type { CardRecord, SubtaskRecord } from "@/lib/pb/types"; + +/** + * POST /api/agent/cards/:cardId/subtasks — turn a plan into a checklist. + * + * Appends after the card's existing subtasks, in the order given, using the + * same fractional-position spacing every other subtask-creation path in this + * app uses (see addSubtask in src/app/actions/subtasks.ts). + */ +export async function POST( + request: Request, + context: { params: Promise<{ cardId: string }> } +): Promise { + const denied = requireAgentToken(request); + if (denied) return denied; + + const { cardId } = await context.params; + + let json: unknown; + try { + json = await request.json(); + } catch { + return jsonError("Invalid request body", 400); + } + + const titles = (json as { titles?: unknown } | null)?.titles; + if (!Array.isArray(titles)) { + return jsonError("titles must be an array of strings", 400); + } + + const cleaned = titles + .filter((t): t is string => typeof t === "string") + .map((t) => t.trim().slice(0, 200)) + .filter((t) => t.length > 0); + + if (cleaned.length === 0) { + return jsonError("At least one non-empty title is required", 400); + } + + const pb = await getAdminPb(); + const card = await pb.collection("cards").getOne(cardId).catch(() => null); + if (!card || card.archived) return jsonError("Card not found", 404); + + const last = await pb + .collection("subtasks") + .getFirstListItem(`card = "${cardId}"`, { sort: "-position" }) + .catch(() => null); + + let position: number | null = last ? last.position : null; + const created: string[] = []; + + for (const title of cleaned) { + position = positionBetween(position, null); + const subtask = await pb.collection("subtasks").create({ + card: cardId, + title, + done: false, + position, + }); + created.push(subtask.id); + } + + return jsonOk({ created }, 201); +}