98 lines
3.9 KiB
TypeScript
98 lines
3.9 KiB
TypeScript
|
|
import type { AgentTool } from "@jaeswift/jae-agent-core";
|
|
import { Type } from "@sinclair/typebox";
|
|
import type { ToolResultMessage } from "@jaeswift/jae-ai";
|
|
import { html } from "lit";
|
|
import { GitCompare } from "lucide";
|
|
import { registerToolRenderer, renderHeader } from "./renderer-registry.js";
|
|
import type { ToolRenderer, ToolRenderResult } from "./types.js";
|
|
|
|
export interface DiffDetails {
|
|
original: string;
|
|
modified: string;
|
|
filename?: string;
|
|
}
|
|
|
|
interface DiffParams {
|
|
original: string;
|
|
modified: string;
|
|
filename?: string;
|
|
}
|
|
|
|
const diffSchema = Type.Object({
|
|
original: Type.String({ description: "Original file content" }),
|
|
modified: Type.String({ description: "Modified file content" }),
|
|
filename: Type.Optional(Type.String({ description: "Filename for display" })),
|
|
});
|
|
|
|
function computeLineDiff(original: string, modified: string): Array<{ type: "add" | "remove" | "same"; line: string }> {
|
|
const oldLines = original.split("\n");
|
|
const newLines = modified.split("\n");
|
|
const result: Array<{ type: "add" | "remove" | "same"; line: string }> = [];
|
|
const maxLen = Math.max(oldLines.length, newLines.length);
|
|
for (let i = 0; i < maxLen; i++) {
|
|
if (i >= oldLines.length) { result.push({ type: "add", line: newLines[i] }); }
|
|
else if (i >= newLines.length) { result.push({ type: "remove", line: oldLines[i] }); }
|
|
else if (oldLines[i] === newLines[i]) { result.push({ type: "same", line: oldLines[i] }); }
|
|
else {
|
|
result.push({ type: "remove", line: oldLines[i] });
|
|
result.push({ type: "add", line: newLines[i] });
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
export const diffTool: AgentTool<typeof diffSchema, DiffDetails> = {
|
|
name: "show_diff",
|
|
label: "Show Diff",
|
|
description: "Show a diff between two versions of code or text",
|
|
parameters: diffSchema,
|
|
async execute(toolCallId, params, signal) {
|
|
return {
|
|
content: [{ type: "text", text: `Diff shown for: ${params.filename || "file"}` }],
|
|
details: { original: params.original, modified: params.modified, filename: params.filename },
|
|
};
|
|
},
|
|
};
|
|
|
|
class DiffRenderer implements ToolRenderer<DiffParams, DiffDetails> {
|
|
render(params: DiffParams | undefined, result: ToolResultMessage<DiffDetails> | undefined): ToolRenderResult {
|
|
const state = result ? (result.isError ? "error" : "complete") : "inprogress";
|
|
if (!result?.details) {
|
|
return { content: renderHeader(state, GitCompare, `Diff: ${params?.filename || "file"}`), isCustom: false };
|
|
}
|
|
const { original, modified, filename } = result.details;
|
|
const diffLines = computeLineDiff(original, modified);
|
|
const adds = diffLines.filter(l => l.type === "add").length;
|
|
const removes = diffLines.filter(l => l.type === "remove").length;
|
|
|
|
return {
|
|
content: html`
|
|
<div class="flex flex-col gap-3">
|
|
${renderHeader(state, GitCompare, html`Diff: ${filename || "file"} <span class="text-green-500 ml-2">+${adds}</span><span class="text-red-500 ml-1">-${removes}</span>`)}
|
|
<div class="rounded border border-border overflow-auto max-h-96 text-xs font-mono">
|
|
${diffLines.map((l, i) => html`
|
|
<div class="flex gap-0 ${
|
|
l.type === "add" ? "bg-green-500/10 text-green-700 dark:text-green-400" :
|
|
l.type === "remove" ? "bg-red-500/10 text-red-700 dark:text-red-400" :
|
|
"text-muted-foreground"
|
|
}">
|
|
<span class="w-6 text-center shrink-0 select-none border-r border-border px-1">${i + 1}</span>
|
|
<span class="px-2 whitespace-pre">${
|
|
l.type === "add" ? "+ " : l.type === "remove" ? "- " : " "
|
|
}${l.line}</span>
|
|
</div>
|
|
`)}
|
|
</div>
|
|
</div>
|
|
`,
|
|
isCustom: false,
|
|
};
|
|
}
|
|
}
|
|
|
|
registerToolRenderer("show_diff", new DiffRenderer());
|
|
|
|
export function createDiffTool(): AgentTool<typeof diffSchema, DiffDetails> {
|
|
return diffTool;
|
|
}
|