Files
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

116 lines
3.1 KiB
TypeScript

export type AgentPatchChangeType = "created" | "modified" | "deleted";
export function normalizeAgentPatchText(text: string): string {
if (!text) {
return text;
}
const contentField = tryExtractContentField(text);
if (contentField) {
return normalizeAgentPatchText(contentField);
}
const expanded =
!text.includes("\n") && text.includes("\\n")
? text.replace(/\\n/g, "\n")
: text;
return expanded
.replace(/\r\n/g, "\n")
.split("\n")
.map((line) =>
line.trimStart() === "\\ No newline at end of file"
? "\\ No newline at end of file"
: line
)
.join("\n");
}
export function extractAgentPatchPath(patchText: string | null): string | null {
const normalized = normalizeAgentPatchText(patchText ?? "");
if (!normalized.trim()) {
return null;
}
const applyPatchPath = extractApplyPatchPath(normalized);
if (applyPatchPath) {
return applyPatchPath;
}
const changeType = inferAgentPatchChangeType(normalized);
const gitHeader = normalized.match(/^diff --git a\/(.+?) b\/(.+)$/m);
const oldPath = gitHeader?.[1]?.trim() ?? null;
const newPath = gitHeader?.[2]?.trim() ?? null;
if (oldPath || newPath) {
return changeType === "deleted"
? (nonNullPath(oldPath) ?? nonNullPath(newPath))
: (nonNullPath(newPath) ?? nonNullPath(oldPath));
}
if (changeType === "deleted") {
return extractOldHeaderPath(normalized);
}
return extractNewHeaderPath(normalized);
}
export function inferAgentPatchChangeType(
patchText: string | null
): AgentPatchChangeType {
const normalized = normalizeAgentPatchText(patchText ?? "").trim();
if (!normalized) {
return "modified";
}
if (
/^(\*\*\* Delete File:|deleted file mode\b|\+\+\+ \/dev\/null$)/m.test(
normalized
)
) {
return "deleted";
}
if (
/^(\*\*\* Add File:|new file mode\b|--- \/dev\/null$)/m.test(normalized)
) {
return "created";
}
return "modified";
}
function extractApplyPatchPath(text: string): string | null {
const match = text.match(/^\*\*\* (?:Add|Delete|Update) File:\s+(.+?)\s*$/m);
return match?.[1]?.trim() || null;
}
function extractOldHeaderPath(text: string): string | null {
const match = text.match(/^---\s+(?:a\/)?([^\n]+)$/m);
return nonNullPath(match?.[1]?.trim() ?? null);
}
function extractNewHeaderPath(text: string): string | null {
const match = text.match(/^\+\+\+\s+(?:b\/)?([^\n]+)$/m);
return nonNullPath(match?.[1]?.trim() ?? null);
}
function nonNullPath(path: string | null): string | null {
if (!path || path === "/dev/null" || path === "dev/null") {
return null;
}
return path;
}
function tryExtractContentField(text: string): string | null {
const trimmed = text.trim();
if (!trimmed.startsWith("{") || !trimmed.endsWith("}")) {
return null;
}
try {
const parsed = JSON.parse(trimmed) as unknown;
if (
parsed &&
typeof parsed === "object" &&
typeof (parsed as { content?: unknown }).content === "string"
) {
return (parsed as { content: string }).content;
}
return null;
} catch {
return null;
}
}