Archived
106 lines
3.0 KiB
TypeScript
106 lines
3.0 KiB
TypeScript
import {
|
|
type IssueSnapshot,
|
|
type Marker,
|
|
marker,
|
|
parseMarker,
|
|
protocolVersion,
|
|
sha256,
|
|
} from "../../core/contracts.js";
|
|
import { parseAgentCommand } from "../../core/webhook.js";
|
|
import type { GiteaComment, GiteaIssue } from "./types.js";
|
|
|
|
export function createIssueSnapshot(
|
|
issue: GiteaIssue,
|
|
comments: GiteaComment[],
|
|
botLogin: string,
|
|
): IssueSnapshot {
|
|
const humanComments = comments
|
|
.filter(
|
|
(comment) =>
|
|
comment.user.login.toLowerCase() !== botLogin.toLowerCase(),
|
|
)
|
|
.filter((comment) => !parseAgentCommand(comment.body))
|
|
.map((comment) => ({
|
|
id: comment.id,
|
|
author: comment.user.login,
|
|
createdAt: comment.created_at,
|
|
body: comment.body,
|
|
}));
|
|
const canonical = JSON.stringify({
|
|
v: protocolVersion,
|
|
number: issue.number,
|
|
state: issue.state,
|
|
title: issue.title,
|
|
body: issue.body,
|
|
comments: humanComments.map(({ author, createdAt, body }) => ({
|
|
author,
|
|
createdAt,
|
|
body,
|
|
})),
|
|
});
|
|
return {
|
|
digest: sha256(canonical),
|
|
title: issue.title,
|
|
body: issue.body,
|
|
comments: humanComments,
|
|
};
|
|
}
|
|
|
|
export function findAcceptedPlan(
|
|
comments: GiteaComment[],
|
|
botLogin: string,
|
|
issueNumber: number,
|
|
):
|
|
| {
|
|
marker: Marker;
|
|
markdown: string;
|
|
comment: GiteaComment;
|
|
}
|
|
| undefined {
|
|
const candidates = comments
|
|
.filter(
|
|
(comment) =>
|
|
comment.user.login.toLowerCase() === botLogin.toLowerCase(),
|
|
)
|
|
.map((comment) => ({
|
|
comment,
|
|
found: parseMarker(comment.body, "plan"),
|
|
}))
|
|
.filter((value): value is { comment: GiteaComment; found: Marker } =>
|
|
Boolean(value.found),
|
|
)
|
|
.filter(
|
|
(value) =>
|
|
value.found.issue === issueNumber &&
|
|
value.found.status === "accepted",
|
|
)
|
|
.sort((a, b) =>
|
|
b.comment.updated_at.localeCompare(a.comment.updated_at),
|
|
);
|
|
const selected = candidates[0];
|
|
if (!selected) return undefined;
|
|
const header = "## Accepted implementation plan\n\n";
|
|
const start = selected.comment.body.indexOf(header);
|
|
const end = selected.comment.body.lastIndexOf(
|
|
"\n\n<!-- gitea-agent:plan-footer -->",
|
|
);
|
|
if (start < 0) return undefined;
|
|
const markdown = selected.comment.body
|
|
.slice(start + header.length, end < 0 ? undefined : end)
|
|
.trim();
|
|
if (
|
|
!selected.found.planDigest ||
|
|
sha256(markdown) !== selected.found.planDigest
|
|
)
|
|
return undefined;
|
|
return { marker: selected.found, markdown, comment: selected.comment };
|
|
}
|
|
|
|
export function renderStatus(input: {
|
|
marker: Marker;
|
|
heading: string;
|
|
detail: string;
|
|
}): string {
|
|
return `${marker(input.marker)}\n## ${input.heading}\n\n${input.detail}`;
|
|
}
|