Files
kandev/apps/web/lib/walkthrough-request.ts
T
José Almeida 3e237e177a feat: add agent-authored code walkthroughs (#1647)
* 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
2026-07-10 08:34:57 +01:00

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");
}