3e237e177a
* feat: add code-walkthrough backend (MCP tool, service, persistence, WS events) * feat: render agent code walkthroughs as line-anchored cards + editor window * test: add code-walkthrough re-emit mock-agent scenario * fix: rework code-walkthrough UX (floating card, line reveal, minimize, clickable file) * fix: improve code walkthrough editor UX * fix: backfill walkthrough launcher after websocket connect * fix: improve walkthrough requests and editor anchors * fix: clear walkthrough highlights on close * feat: add walkthrough discard and prompt customization * fix: send walkthrough requests through saved prompt * fix: support postgres built-in prompt seeding * fix: address walkthrough review feedback * fix: address walkthrough review follow-ups * fix: harden walkthrough lifecycle races * fix: reuse walkthrough notification cleanup * fix: guard stale walkthrough file opens * fix: abort stale file opens before paint * fix: stabilize github settings e2e readiness * fix: use stable github settings e2e selector
72 lines
2.6 KiB
TypeScript
72 lines
2.6 KiB
TypeScript
import { formatPromptReferenceExpansions } from "@/lib/prompts/expand-prompt-references";
|
|
|
|
export type WalkthroughPromptFile = {
|
|
path: string;
|
|
repository_name?: string;
|
|
repositoryName?: string;
|
|
source?: "uncommitted" | "committed" | "pr" | string;
|
|
};
|
|
|
|
const MAX_PROMPT_FILES = 80;
|
|
export const CHANGES_WALKTHROUGH_PROMPT_NAME = "changes-walkthrough";
|
|
const CHANGED_FILES_PLACEHOLDER = "{{changed_files}}";
|
|
const CHANGED_FILES_REFERENCE = "The changed files are listed in the user-visible message above.";
|
|
|
|
function fileKey(file: WalkthroughPromptFile): string {
|
|
return `${file.repository_name ?? file.repositoryName ?? ""}\0${file.path}\0${file.source ?? ""}`;
|
|
}
|
|
|
|
function escapePromptFilePart(value: string): string {
|
|
return value.replaceAll("\\", "\\\\").replaceAll("\r", "\\r").replaceAll("\n", "\\n");
|
|
}
|
|
|
|
function formatPromptFile(file: WalkthroughPromptFile): string {
|
|
const repo = escapePromptFilePart(file.repository_name ?? file.repositoryName ?? "");
|
|
const path = escapePromptFilePart(file.path);
|
|
const source = file.source ? ` [${escapePromptFilePart(file.source)}]` : "";
|
|
return repo ? `${repo}:${path}${source}` : `${path}${source}`;
|
|
}
|
|
|
|
export function formatChangedFilesForWalkthroughPrompt(files: WalkthroughPromptFile[]): string {
|
|
const uniqueFiles: WalkthroughPromptFile[] = [];
|
|
const seen = new Set<string>();
|
|
for (const file of files) {
|
|
if (!file.path) continue;
|
|
const key = fileKey(file);
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
uniqueFiles.push(file);
|
|
}
|
|
|
|
const shown = uniqueFiles.slice(0, MAX_PROMPT_FILES);
|
|
const omitted = uniqueFiles.length - shown.length;
|
|
return shown.length > 0
|
|
? shown.map((file) => `- ${formatPromptFile(file)}`).join("\n") +
|
|
(omitted > 0 ? `\n- ... ${omitted} more file(s) omitted from this prompt` : "")
|
|
: "- No changed files were listed by the UI; inspect the local task state before anchoring.";
|
|
}
|
|
|
|
export function buildChangesWalkthroughPrompt(
|
|
template: string,
|
|
files: WalkthroughPromptFile[],
|
|
): string {
|
|
const changedFiles = formatChangedFilesForWalkthroughPrompt(files);
|
|
const trimmedTemplate = template.trim();
|
|
const expansionContent = trimmedTemplate.includes(CHANGED_FILES_PLACEHOLDER)
|
|
? trimmedTemplate.replaceAll(CHANGED_FILES_PLACEHOLDER, CHANGED_FILES_REFERENCE)
|
|
: trimmedTemplate;
|
|
const expansionContext = formatPromptReferenceExpansions([
|
|
{ name: CHANGES_WALKTHROUGH_PROMPT_NAME, content: expansionContent },
|
|
]);
|
|
return [
|
|
`@${CHANGES_WALKTHROUGH_PROMPT_NAME}`,
|
|
"",
|
|
"Changed files:",
|
|
changedFiles,
|
|
"",
|
|
"<kandev-system>",
|
|
expansionContext,
|
|
"</kandev-system>",
|
|
].join("\n");
|
|
}
|