Support add dependencies as part of approval workflow
This commit is contained in:
@@ -2,12 +2,8 @@ import { ipcMain } from "electron";
|
||||
import { db } from "../../db";
|
||||
import { messages, apps, chats } from "../../db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { spawn } from "node:child_process";
|
||||
import { exec } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
import { getDyadAppPath } from "../../paths/paths";
|
||||
|
||||
const execPromise = promisify(exec);
|
||||
import { executeAddDependency } from "../processors/executeAddDependency";
|
||||
|
||||
export function registerDependencyHandlers() {
|
||||
ipcMain.handle(
|
||||
@@ -53,74 +49,11 @@ export function registerDependencyHandlers() {
|
||||
);
|
||||
}
|
||||
|
||||
// Check if the message content contains the dependency tag
|
||||
const dependencyTagRegex = new RegExp(
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">[^<]*</dyad-add-dependency>`,
|
||||
"g"
|
||||
);
|
||||
|
||||
if (!dependencyTagRegex.test(message.content)) {
|
||||
throw new Error(
|
||||
`Message doesn't contain the dependency tag for packages ${packages.join(
|
||||
", "
|
||||
)}`
|
||||
);
|
||||
}
|
||||
|
||||
// Execute npm install
|
||||
try {
|
||||
const { stdout, stderr } = await execPromise(
|
||||
`npm install ${packages.join(" ")}`,
|
||||
{
|
||||
cwd: getDyadAppPath(app.path),
|
||||
}
|
||||
);
|
||||
const installResults = stdout + (stderr ? `\n${stderr}` : "");
|
||||
|
||||
// Update the message content with the installation results
|
||||
const updatedContent = message.content.replace(
|
||||
new RegExp(
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">[^<]*</dyad-add-dependency>`,
|
||||
"g"
|
||||
),
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">${installResults}</dyad-add-dependency>`
|
||||
);
|
||||
|
||||
// Save the updated message back to the database
|
||||
await db
|
||||
.update(messages)
|
||||
.set({ content: updatedContent })
|
||||
.where(eq(messages.id, message.id));
|
||||
|
||||
// Return undefined implicitly
|
||||
} catch (error: any) {
|
||||
// Update the message with the error
|
||||
const updatedContent = message.content.replace(
|
||||
new RegExp(
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">[^<]*</dyad-add-dependency>`,
|
||||
"g"
|
||||
),
|
||||
`<dyad-add-dependency packages="${packages.join(" ")}"><dyad-error>${
|
||||
error.message
|
||||
}</dyad-error></dyad-add-dependency>`
|
||||
);
|
||||
|
||||
// Save the updated message back to the database
|
||||
await db
|
||||
.update(messages)
|
||||
.set({ content: updatedContent })
|
||||
.where(eq(messages.id, message.id));
|
||||
|
||||
throw error;
|
||||
}
|
||||
executeAddDependency({
|
||||
packages,
|
||||
message,
|
||||
appPath: getDyadAppPath(app.path),
|
||||
});
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { desc, eq, and, Update } from "drizzle-orm";
|
||||
import path from "node:path"; // Import path for basename
|
||||
// Import tag parsers
|
||||
import {
|
||||
getDyadAddDependencyTags,
|
||||
getDyadChatSummaryTag,
|
||||
getDyadWriteTags,
|
||||
processFullResponseActions,
|
||||
@@ -67,9 +68,14 @@ const getProposalHandler = async (
|
||||
// Parse tags directly from message content
|
||||
const proposalTitle = getDyadChatSummaryTag(messageContent);
|
||||
const proposalFiles = getDyadWriteTags(messageContent); // Gets { path: string, content: string }[]
|
||||
const packagesAdded = getDyadAddDependencyTags(messageContent);
|
||||
|
||||
// Check if we have enough information to create a proposal
|
||||
if (proposalTitle || proposalFiles.length > 0) {
|
||||
if (
|
||||
proposalTitle ||
|
||||
proposalFiles.length > 0 ||
|
||||
packagesAdded.length > 0
|
||||
) {
|
||||
const proposal: CodeProposal = {
|
||||
type: "code-proposal",
|
||||
// Use parsed title or a default title if summary tag is missing but write tags exist
|
||||
@@ -80,6 +86,7 @@ const getProposalHandler = async (
|
||||
path: tag.path,
|
||||
summary: tag.description ?? "(no change summary found)", // Generic summary
|
||||
})),
|
||||
packagesAdded,
|
||||
};
|
||||
logger.log(
|
||||
"Generated code proposal. title=",
|
||||
|
||||
@@ -36,10 +36,10 @@ export interface CreateAppResult {
|
||||
}
|
||||
|
||||
export interface Message {
|
||||
id: string;
|
||||
id: number;
|
||||
role: "user" | "assistant";
|
||||
content: string;
|
||||
approvalState?: "approved" | "rejected";
|
||||
approvalState?: "approved" | "rejected" | null;
|
||||
}
|
||||
|
||||
export interface Chat {
|
||||
|
||||
71
src/ipc/processors/executeAddDependency.ts
Normal file
71
src/ipc/processors/executeAddDependency.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { db } from "../../db";
|
||||
import { messages } from "../../db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { Message } from "../ipc_types";
|
||||
import { exec } from "node:child_process";
|
||||
import { promisify } from "node:util";
|
||||
|
||||
export const execPromise = promisify(exec);
|
||||
|
||||
export async function executeAddDependency({
|
||||
packages,
|
||||
message,
|
||||
appPath,
|
||||
}: {
|
||||
packages: string[];
|
||||
message: Message;
|
||||
appPath: string;
|
||||
}) {
|
||||
const packageStr = packages.join(" ");
|
||||
try {
|
||||
const { stdout, stderr } = await execPromise(
|
||||
`(pnpm add ${packageStr}) || (npm install ${packageStr})`,
|
||||
{
|
||||
cwd: appPath,
|
||||
}
|
||||
);
|
||||
const installResults = stdout + (stderr ? `\n${stderr}` : "");
|
||||
|
||||
// Update the message content with the installation results
|
||||
const updatedContent = message.content.replace(
|
||||
new RegExp(
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">[^<]*</dyad-add-dependency>`,
|
||||
"g"
|
||||
),
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">${installResults}</dyad-add-dependency>`
|
||||
);
|
||||
|
||||
// Save the updated message back to the database
|
||||
await db
|
||||
.update(messages)
|
||||
.set({ content: updatedContent })
|
||||
.where(eq(messages.id, message.id));
|
||||
|
||||
// Return undefined implicitly
|
||||
} catch (error: any) {
|
||||
// Update the message with the error
|
||||
const updatedContent = message.content.replace(
|
||||
new RegExp(
|
||||
`<dyad-add-dependency packages="${packages.join(
|
||||
" "
|
||||
)}">[^<]*</dyad-add-dependency>`,
|
||||
"g"
|
||||
),
|
||||
`<dyad-add-dependency packages="${packages.join(" ")}"><dyad-error>${
|
||||
error.message
|
||||
}</dyad-error></dyad-add-dependency>`
|
||||
);
|
||||
|
||||
// Save the updated message back to the database
|
||||
await db
|
||||
.update(messages)
|
||||
.set({ content: updatedContent })
|
||||
.where(eq(messages.id, message.id));
|
||||
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { db } from "../../db";
|
||||
import { chats, messages } from "../../db/schema";
|
||||
import { eq } from "drizzle-orm";
|
||||
import { and, eq } from "drizzle-orm";
|
||||
import fs from "node:fs";
|
||||
import { getDyadAppPath } from "../../paths/paths";
|
||||
import path from "node:path";
|
||||
@@ -8,6 +8,7 @@ import git from "isomorphic-git";
|
||||
import { getGithubUser } from "../handlers/github_handlers";
|
||||
import { getGitAuthor } from "../utils/git_author";
|
||||
import log from "electron-log";
|
||||
import { executeAddDependency } from "./executeAddDependency";
|
||||
|
||||
const logger = log.scope("response_processor");
|
||||
|
||||
@@ -81,11 +82,11 @@ export function getDyadDeleteTags(fullResponse: string): string[] {
|
||||
|
||||
export function getDyadAddDependencyTags(fullResponse: string): string[] {
|
||||
const dyadAddDependencyRegex =
|
||||
/<dyad-add-dependency package="([^"]+)">[^<]*<\/dyad-add-dependency>/g;
|
||||
/<dyad-add-dependency packages="([^"]+)">[^<]*<\/dyad-add-dependency>/g;
|
||||
let match;
|
||||
const packages: string[] = [];
|
||||
while ((match = dyadAddDependencyRegex.exec(fullResponse)) !== null) {
|
||||
packages.push(match[1]);
|
||||
packages.push(...match[1].split(" "));
|
||||
}
|
||||
return packages;
|
||||
}
|
||||
@@ -134,7 +135,36 @@ export async function processFullResponseActions(
|
||||
const dyadDeletePaths = getDyadDeleteTags(fullResponse);
|
||||
const dyadAddDependencyPackages = getDyadAddDependencyTags(fullResponse);
|
||||
|
||||
const message = await db.query.messages.findFirst({
|
||||
where: and(
|
||||
eq(messages.id, messageId),
|
||||
eq(messages.role, "assistant"),
|
||||
eq(messages.chatId, chatId)
|
||||
),
|
||||
});
|
||||
|
||||
if (!message) {
|
||||
logger.error(`No message found for ID: ${messageId}`);
|
||||
return {};
|
||||
}
|
||||
|
||||
// TODO: Handle add dependency tags
|
||||
if (dyadAddDependencyPackages.length > 0) {
|
||||
await executeAddDependency({
|
||||
packages: dyadAddDependencyPackages,
|
||||
message: message,
|
||||
appPath,
|
||||
});
|
||||
writtenFiles.push("package.json");
|
||||
const pnpmFilename = "pnpm-lock.yaml";
|
||||
if (fs.existsSync(path.join(appPath, pnpmFilename))) {
|
||||
writtenFiles.push(pnpmFilename);
|
||||
}
|
||||
const packageLockFilename = "package-lock.json";
|
||||
if (fs.existsSync(path.join(appPath, packageLockFilename))) {
|
||||
writtenFiles.push(packageLockFilename);
|
||||
}
|
||||
}
|
||||
|
||||
// Process all file writes
|
||||
for (const tag of dyadWriteTags) {
|
||||
@@ -218,7 +248,8 @@ export async function processFullResponseActions(
|
||||
hasChanges =
|
||||
writtenFiles.length > 0 ||
|
||||
renamedFiles.length > 0 ||
|
||||
deletedFiles.length > 0;
|
||||
deletedFiles.length > 0 ||
|
||||
dyadAddDependencyPackages.length > 0;
|
||||
if (hasChanges) {
|
||||
// Stage all written files
|
||||
for (const file of writtenFiles) {
|
||||
@@ -237,6 +268,10 @@ export async function processFullResponseActions(
|
||||
changes.push(`renamed ${renamedFiles.length} file(s)`);
|
||||
if (deletedFiles.length > 0)
|
||||
changes.push(`deleted ${deletedFiles.length} file(s)`);
|
||||
if (dyadAddDependencyPackages.length > 0)
|
||||
changes.push(
|
||||
`added ${dyadAddDependencyPackages.join(", ")} package(s)`
|
||||
);
|
||||
|
||||
// Use chat summary, if provided, or default for commit message
|
||||
await git.commit({
|
||||
|
||||
Reference in New Issue
Block a user