mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-04 09:13:08 +00:00
feat(worktree): enhance worktree management and git diff functionality
- Integrated git worktree isolation for feature execution, allowing agents to work in isolated branches. - Added GitDiffPanel component to visualize changes in both worktree and main project contexts. - Updated AutoModeService and IPC handlers to support worktree settings. - Implemented Git API for non-worktree operations, enabling file diff retrieval for the main project. - Enhanced UI components to reflect worktree settings and improve user experience. These changes provide a more robust and flexible environment for feature development and testing.
This commit is contained in:
@@ -46,8 +46,18 @@ class AutoModeService {
|
||||
/**
|
||||
* Setup worktree for a feature
|
||||
* Creates an isolated git worktree where the agent can work
|
||||
* @param {Object} feature - The feature object
|
||||
* @param {string} projectPath - Path to the project
|
||||
* @param {Function} sendToRenderer - Function to send events to the renderer
|
||||
* @param {boolean} useWorktreesEnabled - Whether worktrees are enabled in settings (default: false)
|
||||
*/
|
||||
async setupWorktreeForFeature(feature, projectPath, sendToRenderer) {
|
||||
async setupWorktreeForFeature(feature, projectPath, sendToRenderer, useWorktreesEnabled = false) {
|
||||
// If worktrees are disabled in settings, skip entirely
|
||||
if (!useWorktreesEnabled) {
|
||||
console.log(`[AutoMode] Worktrees disabled in settings, working directly on main project`);
|
||||
return { useWorktree: false, workPath: projectPath };
|
||||
}
|
||||
|
||||
// Check if worktrees are enabled (project must be a git repo)
|
||||
const isGit = await worktreeManager.isGitRepo(projectPath);
|
||||
if (!isGit) {
|
||||
@@ -164,14 +174,18 @@ class AutoModeService {
|
||||
|
||||
/**
|
||||
* Run a specific feature by ID
|
||||
* @param {string} projectPath - Path to the project
|
||||
* @param {string} featureId - ID of the feature to run
|
||||
* @param {Function} sendToRenderer - Function to send events to renderer
|
||||
* @param {boolean} useWorktrees - Whether to use git worktree isolation (default: false)
|
||||
*/
|
||||
async runFeature({ projectPath, featureId, sendToRenderer }) {
|
||||
async runFeature({ projectPath, featureId, sendToRenderer, useWorktrees = false }) {
|
||||
// Check if this specific feature is already running
|
||||
if (this.runningFeatures.has(featureId)) {
|
||||
throw new Error(`Feature ${featureId} is already running`);
|
||||
}
|
||||
|
||||
console.log(`[AutoMode] Running specific feature: ${featureId}`);
|
||||
console.log(`[AutoMode] Running specific feature: ${featureId} (worktrees: ${useWorktrees})`);
|
||||
|
||||
// Register this feature as running
|
||||
const execution = this.createExecutionContext(featureId);
|
||||
@@ -190,8 +204,8 @@ class AutoModeService {
|
||||
|
||||
console.log(`[AutoMode] Running feature: ${feature.description}`);
|
||||
|
||||
// Setup worktree for isolated work
|
||||
const worktreeSetup = await this.setupWorktreeForFeature(feature, projectPath, sendToRenderer);
|
||||
// Setup worktree for isolated work (if enabled)
|
||||
const worktreeSetup = await this.setupWorktreeForFeature(feature, projectPath, sendToRenderer, useWorktrees);
|
||||
execution.worktreePath = worktreeSetup.workPath;
|
||||
execution.branchName = worktreeSetup.branchName;
|
||||
|
||||
@@ -621,8 +635,12 @@ class AutoModeService {
|
||||
|
||||
/**
|
||||
* Start a feature asynchronously (similar to drag operation)
|
||||
* @param {Object} feature - The feature to start
|
||||
* @param {string} projectPath - Path to the project
|
||||
* @param {Function} sendToRenderer - Function to send events to renderer
|
||||
* @param {boolean} useWorktrees - Whether to use git worktree isolation (default: false)
|
||||
*/
|
||||
async startFeatureAsync(feature, projectPath, sendToRenderer) {
|
||||
async startFeatureAsync(feature, projectPath, sendToRenderer, useWorktrees = false) {
|
||||
const featureId = feature.id;
|
||||
|
||||
// Skip if already running
|
||||
@@ -633,7 +651,7 @@ class AutoModeService {
|
||||
|
||||
try {
|
||||
console.log(
|
||||
`[AutoMode] Starting feature: ${feature.description.slice(0, 50)}...`
|
||||
`[AutoMode] Starting feature: ${feature.description.slice(0, 50)}... (worktrees: ${useWorktrees})`
|
||||
);
|
||||
|
||||
// Register this feature as running
|
||||
@@ -642,8 +660,8 @@ class AutoModeService {
|
||||
execution.sendToRenderer = sendToRenderer;
|
||||
this.runningFeatures.set(featureId, execution);
|
||||
|
||||
// Setup worktree for isolated work
|
||||
const worktreeSetup = await this.setupWorktreeForFeature(feature, projectPath, sendToRenderer);
|
||||
// Setup worktree for isolated work (if enabled)
|
||||
const worktreeSetup = await this.setupWorktreeForFeature(feature, projectPath, sendToRenderer, useWorktrees);
|
||||
execution.worktreePath = worktreeSetup.workPath;
|
||||
execution.branchName = worktreeSetup.branchName;
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ const { app, BrowserWindow, ipcMain, dialog, shell } = require("electron");
|
||||
const fs = require("fs/promises");
|
||||
const agentService = require("./agent-service");
|
||||
const autoModeService = require("./auto-mode-service");
|
||||
const worktreeManager = require("./services/worktree-manager");
|
||||
|
||||
let mainWindow = null;
|
||||
|
||||
@@ -468,7 +469,7 @@ ipcMain.handle("auto-mode:status", () => {
|
||||
*/
|
||||
ipcMain.handle(
|
||||
"auto-mode:run-feature",
|
||||
async (_, { projectPath, featureId }) => {
|
||||
async (_, { projectPath, featureId, useWorktrees = false }) => {
|
||||
try {
|
||||
const sendToRenderer = (data) => {
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
@@ -480,6 +481,7 @@ ipcMain.handle(
|
||||
projectPath,
|
||||
featureId,
|
||||
sendToRenderer,
|
||||
useWorktrees,
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("[IPC] auto-mode:run-feature error:", error);
|
||||
@@ -934,3 +936,27 @@ ipcMain.handle("worktree:get-file-diff", async (_, { projectPath, featureId, fil
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get file diffs for the main project (non-worktree)
|
||||
*/
|
||||
ipcMain.handle("git:get-diffs", async (_, { projectPath }) => {
|
||||
try {
|
||||
return await worktreeManager.getFileDiffs(projectPath);
|
||||
} catch (error) {
|
||||
console.error("[IPC] git:get-diffs error:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Get diff for a specific file in the main project (non-worktree)
|
||||
*/
|
||||
ipcMain.handle("git:get-file-diff", async (_, { projectPath, filePath }) => {
|
||||
try {
|
||||
return await worktreeManager.getFileDiff(projectPath, filePath);
|
||||
} catch (error) {
|
||||
console.error("[IPC] git:get-file-diff error:", error);
|
||||
return { success: false, error: error.message };
|
||||
}
|
||||
});
|
||||
|
||||
@@ -97,8 +97,8 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
status: () => ipcRenderer.invoke("auto-mode:status"),
|
||||
|
||||
// Run a specific feature
|
||||
runFeature: (projectPath, featureId) =>
|
||||
ipcRenderer.invoke("auto-mode:run-feature", { projectPath, featureId }),
|
||||
runFeature: (projectPath, featureId, useWorktrees) =>
|
||||
ipcRenderer.invoke("auto-mode:run-feature", { projectPath, featureId, useWorktrees }),
|
||||
|
||||
// Verify a specific feature by running its tests
|
||||
verifyFeature: (projectPath, featureId) =>
|
||||
@@ -189,6 +189,17 @@ contextBridge.exposeInMainWorld("electronAPI", {
|
||||
getFileDiff: (projectPath, featureId, filePath) =>
|
||||
ipcRenderer.invoke("worktree:get-file-diff", { projectPath, featureId, filePath }),
|
||||
},
|
||||
|
||||
// Git Operations APIs (for non-worktree operations)
|
||||
git: {
|
||||
// Get file diffs for the main project
|
||||
getDiffs: (projectPath) =>
|
||||
ipcRenderer.invoke("git:get-diffs", { projectPath }),
|
||||
|
||||
// Get diff for a specific file in the main project
|
||||
getFileDiff: (projectPath, filePath) =>
|
||||
ipcRenderer.invoke("git:get-file-diff", { projectPath, filePath }),
|
||||
},
|
||||
});
|
||||
|
||||
// Also expose a flag to detect if we're in Electron
|
||||
|
||||
Reference in New Issue
Block a user