Parse proposal from latest chat message (w/o security parts)
This commit is contained in:
@@ -1,43 +1,86 @@
|
||||
import { ipcMain, type IpcMainInvokeEvent } from "electron";
|
||||
import type { Proposal } from "@/lib/schemas";
|
||||
import { db } from "../../db";
|
||||
import { messages } from "../../db/schema";
|
||||
import { desc, eq, and } from "drizzle-orm";
|
||||
import path from "node:path"; // Import path for basename
|
||||
// Import tag parsers
|
||||
import {
|
||||
getDyadChatSummaryTag,
|
||||
getDyadWriteTags,
|
||||
} from "../processors/response_processor";
|
||||
|
||||
// Placeholder Proposal data
|
||||
const placeholderProposal: Proposal = {
|
||||
title: "Review: Example Refactoring (from IPC)",
|
||||
securityRisks: [
|
||||
{
|
||||
type: "warning",
|
||||
title: "Potential XSS Vulnerability",
|
||||
description: "User input is directly rendered without sanitization.",
|
||||
},
|
||||
{
|
||||
type: "danger",
|
||||
title: "Hardcoded API Key",
|
||||
description: "API key found in plain text in configuration file.",
|
||||
},
|
||||
],
|
||||
filesChanged: [
|
||||
{
|
||||
name: "ChatInput.tsx",
|
||||
path: "src/components/chat/ChatInput.tsx",
|
||||
summary: "Added review actions and details section.",
|
||||
},
|
||||
{
|
||||
name: "api.ts",
|
||||
path: "src/lib/api.ts",
|
||||
summary: "Refactored API call structure.",
|
||||
},
|
||||
],
|
||||
};
|
||||
// Placeholder Proposal data (can be removed or kept for reference)
|
||||
// const placeholderProposal: Proposal = { ... };
|
||||
|
||||
// Type guard for the parsed proposal structure
|
||||
interface ParsedProposal {
|
||||
title: string;
|
||||
files: string[];
|
||||
}
|
||||
|
||||
function isParsedProposal(obj: any): obj is ParsedProposal {
|
||||
return (
|
||||
obj &&
|
||||
typeof obj === "object" &&
|
||||
typeof obj.title === "string" &&
|
||||
Array.isArray(obj.files) &&
|
||||
obj.files.every((file: any) => typeof file === "string")
|
||||
);
|
||||
}
|
||||
|
||||
const getProposalHandler = async (
|
||||
_event: IpcMainInvokeEvent,
|
||||
{ chatId }: { chatId: number }
|
||||
): Promise<Proposal> => {
|
||||
): Promise<Proposal | null> => {
|
||||
console.log(`IPC: get-proposal called for chatId: ${chatId}`);
|
||||
// Simulate async operation
|
||||
await new Promise((resolve) => setTimeout(resolve, 500)); // 500ms delay
|
||||
return placeholderProposal;
|
||||
|
||||
try {
|
||||
// Find the latest ASSISTANT message for the chat
|
||||
const latestAssistantMessage = await db.query.messages.findFirst({
|
||||
where: and(eq(messages.chatId, chatId), eq(messages.role, "assistant")),
|
||||
orderBy: [desc(messages.createdAt)],
|
||||
columns: {
|
||||
content: true, // Fetch the content to parse
|
||||
},
|
||||
});
|
||||
|
||||
if (latestAssistantMessage?.content) {
|
||||
console.log("Found latest assistant message, parsing content...");
|
||||
const messageContent = latestAssistantMessage.content;
|
||||
|
||||
// Parse tags directly from message content
|
||||
const proposalTitle = getDyadChatSummaryTag(messageContent);
|
||||
const proposalFiles = getDyadWriteTags(messageContent); // Gets { path: string, content: string }[]
|
||||
|
||||
// Check if we have enough information to create a proposal
|
||||
if (proposalTitle || proposalFiles.length > 0) {
|
||||
const proposal: Proposal = {
|
||||
// Use parsed title or a default title if summary tag is missing but write tags exist
|
||||
title: proposalTitle ?? "Proposed File Changes",
|
||||
securityRisks: [], // Keep empty
|
||||
filesChanged: proposalFiles.map((tag) => ({
|
||||
name: path.basename(tag.path),
|
||||
path: tag.path,
|
||||
summary: tag.description ?? "(no change summary found)", // Generic summary
|
||||
})),
|
||||
};
|
||||
console.log("Generated proposal on the fly:", proposal);
|
||||
return proposal;
|
||||
} else {
|
||||
console.log(
|
||||
"No relevant tags found in the latest assistant message content."
|
||||
);
|
||||
return null; // No proposal could be generated
|
||||
}
|
||||
} else {
|
||||
console.log(`No assistant message found for chatId: ${chatId}`);
|
||||
return null; // No message found
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error processing proposal for chatId ${chatId}:`, error);
|
||||
return null; // Indicate DB or processing error
|
||||
}
|
||||
};
|
||||
|
||||
// Function to register proposal-related handlers
|
||||
|
||||
@@ -11,20 +11,42 @@ import { getGitAuthor } from "../utils/git_author";
|
||||
export function getDyadWriteTags(fullResponse: string): {
|
||||
path: string;
|
||||
content: string;
|
||||
description?: string;
|
||||
}[] {
|
||||
const dyadWriteRegex =
|
||||
/<dyad-write path="([^"]+)"[^>]*>([\s\S]*?)<\/dyad-write>/g;
|
||||
const dyadWriteRegex = /<dyad-write([^>]*)>([\s\S]*?)<\/dyad-write>/gi;
|
||||
const pathRegex = /path="([^"]+)"/;
|
||||
const descriptionRegex = /description="([^"]+)"/;
|
||||
|
||||
let match;
|
||||
const tags: { path: string; content: string }[] = [];
|
||||
const tags: { path: string; content: string; description?: string }[] = [];
|
||||
|
||||
while ((match = dyadWriteRegex.exec(fullResponse)) !== null) {
|
||||
const content = match[2].trim().split("\n");
|
||||
if (content[0].startsWith("```")) {
|
||||
content.shift();
|
||||
const attributesString = match[1];
|
||||
let content = match[2].trim();
|
||||
|
||||
const pathMatch = pathRegex.exec(attributesString);
|
||||
const descriptionMatch = descriptionRegex.exec(attributesString);
|
||||
|
||||
if (pathMatch && pathMatch[1]) {
|
||||
const path = pathMatch[1];
|
||||
const description = descriptionMatch?.[1];
|
||||
|
||||
const contentLines = content.split("\n");
|
||||
if (contentLines[0]?.startsWith("```")) {
|
||||
contentLines.shift();
|
||||
}
|
||||
if (contentLines[contentLines.length - 1]?.startsWith("```")) {
|
||||
contentLines.pop();
|
||||
}
|
||||
content = contentLines.join("\n");
|
||||
|
||||
tags.push({ path, content, description });
|
||||
} else {
|
||||
console.warn(
|
||||
"Found <dyad-write> tag without a valid 'path' attribute:",
|
||||
match[0]
|
||||
);
|
||||
}
|
||||
if (content[content.length - 1].startsWith("```")) {
|
||||
content.pop();
|
||||
}
|
||||
tags.push({ path: match[1], content: content.join("\n") });
|
||||
}
|
||||
return tags;
|
||||
}
|
||||
@@ -65,6 +87,16 @@ export function getDyadAddDependencyTags(fullResponse: string): string[] {
|
||||
return packages;
|
||||
}
|
||||
|
||||
export function getDyadChatSummaryTag(fullResponse: string): string | null {
|
||||
const dyadChatSummaryRegex =
|
||||
/<dyad-chat-summary>([\s\S]*?)<\/dyad-chat-summary>/g;
|
||||
const match = dyadChatSummaryRegex.exec(fullResponse);
|
||||
if (match && match[1]) {
|
||||
return match[1].trim();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export async function processFullResponseActions(
|
||||
fullResponse: string,
|
||||
chatId: number,
|
||||
@@ -206,6 +238,7 @@ export async function processFullResponseActions(
|
||||
if (deletedFiles.length > 0)
|
||||
changes.push(`deleted ${deletedFiles.length} file(s)`);
|
||||
|
||||
// Use chat summary, if provided, or default for commit message
|
||||
await git.commit({
|
||||
fs,
|
||||
dir: appPath,
|
||||
|
||||
Reference in New Issue
Block a user