export function proposedPlanTitle(planMarkdown: string): string | null { const heading = planMarkdown.match(/^\s{0,3}#{1,6}\s+(.+)$/m)?.[1]?.trim(); return heading && heading.length > 0 ? heading : null;} function sanitizePlanFileSegment(input: string): string { const sanitized = input .toLowerCase() .replace(/[`'".,!?()[\]{}]+/g, "") .replace(/[^a-z0-9]+/g, "-") .replace(/^-+|-+$/g, ""); return sanitized.length > 0 ? sanitized : "plan";} export function buildPlanImplementationPrompt(planMarkdown: string): string { return `PLEASE IMPLEMENT THIS PLAN:\n${planMarkdown.trim()}`;} export function resolvePlanFollowUpSubmission(input: { draftText: string; planMarkdown: string;}): { text: string; interactionMode: "default" | "plan";} { const trimmedDraftText = input.draftText.trim(); if (trimmedDraftText.length > 0) { return { text: trimmedDraftText, interactionMode: "plan", }; } return { text: buildPlanImplementationPrompt(input.planMarkdown), interactionMode: "default", };} export function buildPlanImplementationThreadTitle(planMarkdown: string): string { const title = proposedPlanTitle(planMarkdown); if (!title) { return "Implement plan"; } return `Implement ${title}`;} export function buildProposedPlanMarkdownFilename(planMarkdown: string): string { const title = proposedPlanTitle(planMarkdown); return `${sanitizePlanFileSegment(title ?? "plan")}.md`;} export function normalizePlanMarkdownForExport(planMarkdown: string): string { return `${planMarkdown.trimEnd()}\n`;} export function downloadPlanAsTextFile(filename: string, contents: string): void { const blob = new Blob([contents], { type: "text/markdown;charset=utf-8" }); const url = URL.createObjectURL(blob); const anchor = document.createElement("a"); anchor.href = url; anchor.download = filename; anchor.click(); window.setTimeout(() => { URL.revokeObjectURL(url); }, 0);}
export function proposedPlanTitle(planMarkdown: string): string | null {
const heading = planMarkdown.match(/^\s{0,3}#{1,6}\s+(.+)$/m)?.[1]?.trim();
return heading && heading.length > 0 ? heading : null;
}
function sanitizePlanFileSegment(input: string): string {
const sanitized = input
.toLowerCase()
.replace(/[`'".,!?()[\]{}]+/g, "")
.replace(/[^a-z0-9]+/g, "-")
.replace(/^-+|-+$/g, "");
return sanitized.length > 0 ? sanitized : "plan";
}
export function buildPlanImplementationPrompt(planMarkdown: string): string {
return `PLEASE IMPLEMENT THIS PLAN:\n${planMarkdown.trim()}`;
}
export function resolvePlanFollowUpSubmission(input: {
draftText: string;
planMarkdown: string;
}): {
text: string;
interactionMode: "default" | "plan";
} {
const trimmedDraftText = input.draftText.trim();
if (trimmedDraftText.length > 0) {
return {
text: trimmedDraftText,
interactionMode: "plan",
};
}
return {
text: buildPlanImplementationPrompt(input.planMarkdown),
interactionMode: "default",
};
}
export function buildPlanImplementationThreadTitle(planMarkdown: string): string {
const title = proposedPlanTitle(planMarkdown);
if (!title) {
return "Implement plan";
}
return `Implement ${title}`;
}
export function buildProposedPlanMarkdownFilename(planMarkdown: string): string {
const title = proposedPlanTitle(planMarkdown);
return `${sanitizePlanFileSegment(title ?? "plan")}.md`;
}
export function normalizePlanMarkdownForExport(planMarkdown: string): string {
return `${planMarkdown.trimEnd()}\n`;
}
export function downloadPlanAsTextFile(filename: string, contents: string): void {
const blob = new Blob([contents], { type: "text/markdown;charset=utf-8" });
const url = URL.createObjectURL(blob);
const anchor = document.createElement("a");
anchor.href = url;
anchor.download = filename;
anchor.click();
window.setTimeout(() => {
URL.revokeObjectURL(url);
}, 0);
}