feat(agent-api): GET /api/agent/cards/:cardId
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
9b6c031e60
commit
a297fe5f15
1 changed files with 88 additions and 0 deletions
88
src/app/api/agent/cards/[cardId]/route.ts
Normal file
88
src/app/api/agent/cards/[cardId]/route.ts
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
import { getAdminPb } from "@/lib/pb/admin";
|
||||
import { requireAgentToken } from "@/lib/agent/auth";
|
||||
import { jsonError, jsonOk } from "@/lib/http/json";
|
||||
import type {
|
||||
CardRecord,
|
||||
ColumnRecord,
|
||||
CommentRecord,
|
||||
CommitRecord,
|
||||
SubtaskRecord,
|
||||
UserRecord,
|
||||
} from "@/lib/pb/types";
|
||||
|
||||
/**
|
||||
* GET /api/agent/cards/:cardId — one card's full detail: everything the list
|
||||
* endpoint returns, plus its existing checklist, existing comments (with
|
||||
* author names resolved), and its 10 most recent linked commits — enough for
|
||||
* an agent to know what has already touched this card before drafting a plan
|
||||
* for it, without pulling full commit history.
|
||||
*/
|
||||
export async function GET(
|
||||
request: Request,
|
||||
context: { params: Promise<{ cardId: string }> }
|
||||
): Promise<Response> {
|
||||
const denied = requireAgentToken(request);
|
||||
if (denied) return denied;
|
||||
|
||||
const { cardId } = await context.params;
|
||||
const pb = await getAdminPb();
|
||||
|
||||
const card = await pb.collection("cards").getOne<CardRecord>(cardId).catch(() => null);
|
||||
if (!card || card.archived) return jsonError("Card not found", 404);
|
||||
|
||||
const column = await pb
|
||||
.collection("columns")
|
||||
.getOne<ColumnRecord>(card.column)
|
||||
.catch(() => null);
|
||||
|
||||
const subtasks = await pb.collection("subtasks").getFullList<SubtaskRecord>({
|
||||
filter: `card = "${cardId}"`,
|
||||
sort: "position",
|
||||
});
|
||||
|
||||
const comments = await pb.collection("comments").getFullList<CommentRecord>({
|
||||
filter: `card = "${cardId}"`,
|
||||
sort: "created",
|
||||
});
|
||||
|
||||
const userIds = new Set(comments.map((c) => c.author).filter(Boolean));
|
||||
const names: Record<string, string> = {};
|
||||
for (const id of userIds) {
|
||||
const user = await pb.collection("users").getOne<UserRecord>(id).catch(() => null);
|
||||
names[id] = user?.name || user?.email || "Someone";
|
||||
}
|
||||
|
||||
const recentCommits = await pb.collection("commits").getList<CommitRecord>(1, 10, {
|
||||
filter: `card = "${cardId}" && is_merge = false`,
|
||||
sort: "-authored_at",
|
||||
});
|
||||
|
||||
return jsonOk({
|
||||
id: card.id,
|
||||
number: card.number,
|
||||
title: card.title,
|
||||
description: card.description,
|
||||
priority: card.priority,
|
||||
target_date: card.target_date,
|
||||
size_band: card.size_band,
|
||||
column: column
|
||||
? {
|
||||
id: column.id,
|
||||
name: column.name,
|
||||
is_inbox: column.is_inbox,
|
||||
is_done: column.is_done,
|
||||
}
|
||||
: null,
|
||||
subtasks: subtasks.map((s) => ({ id: s.id, title: s.title, done: s.done })),
|
||||
comments: comments.map((c) => ({
|
||||
id: c.id,
|
||||
author: names[c.author] ?? "Someone",
|
||||
body: c.body,
|
||||
created: c.created,
|
||||
})),
|
||||
recentCommits: recentCommits.items.map((commit) => ({
|
||||
summary: commit.summary || commit.subject,
|
||||
authored_at: commit.authored_at,
|
||||
})),
|
||||
});
|
||||
}
|
||||
Loading…
Reference in a new issue