Merge branch 'feature/worktrees' of github.com:AutoMaker-Org/automaker into feature/worktrees

This commit is contained in:
Cody Seibert
2025-12-16 16:36:49 -05:00
2 changed files with 66 additions and 18 deletions

View File

@@ -41,7 +41,12 @@ async function findExistingWorktreeForBranch(
} else if (line === "" && currentPath && currentBranch) { } else if (line === "" && currentPath && currentBranch) {
// End of a worktree entry // End of a worktree entry
if (currentBranch === branchName) { if (currentBranch === branchName) {
return { path: currentPath, branch: currentBranch }; // Resolve to absolute path - git may return relative paths
// Critical for cross-platform compatibility (Windows, macOS, Linux)
const resolvedPath = path.isAbsolute(currentPath)
? path.resolve(currentPath)
: path.resolve(projectPath, currentPath);
return { path: resolvedPath, branch: currentBranch };
} }
currentPath = null; currentPath = null;
currentBranch = null; currentBranch = null;
@@ -50,7 +55,11 @@ async function findExistingWorktreeForBranch(
// Check the last entry (if file doesn't end with newline) // Check the last entry (if file doesn't end with newline)
if (currentPath && currentBranch && currentBranch === branchName) { if (currentPath && currentBranch && currentBranch === branchName) {
return { path: currentPath, branch: currentBranch }; // Resolve to absolute path for cross-platform compatibility
const resolvedPath = path.isAbsolute(currentPath)
? path.resolve(currentPath)
: path.resolve(projectPath, currentPath);
return { path: resolvedPath, branch: currentBranch };
} }
return null; return null;
@@ -144,10 +153,13 @@ export function createCreateHandler() {
// Track the branch so it persists in the UI even after worktree is removed // Track the branch so it persists in the UI even after worktree is removed
await trackBranch(projectPath, branchName); await trackBranch(projectPath, branchName);
// Resolve to absolute path for cross-platform compatibility
// normalizePath converts to forward slashes for API consistency
const absoluteWorktreePath = path.resolve(worktreePath);
res.json({ res.json({
success: true, success: true,
worktree: { worktree: {
path: normalizePath(worktreePath), path: normalizePath(absoluteWorktreePath),
branch: branchName, branch: branchName,
isNew: !branchExists, isNew: !branchExists,
}, },

View File

@@ -207,10 +207,26 @@ export class AutoModeService {
// Use provided worktree path if given, otherwise setup new worktree if enabled // Use provided worktree path if given, otherwise setup new worktree if enabled
if (providedWorktreePath) { if (providedWorktreePath) {
// User selected a specific worktree - use it directly // Resolve to absolute path - critical for cross-platform compatibility
worktreePath = providedWorktreePath; // On Windows, relative paths or paths with forward slashes may not work correctly with cwd
console.log(`[AutoMode] Using provided worktree path: ${worktreePath}`); // On all platforms, absolute paths ensure commands execute in the correct directory
} else if (useWorktrees) { try {
// Resolve relative paths relative to projectPath, absolute paths as-is
const resolvedPath = path.isAbsolute(providedWorktreePath)
? path.resolve(providedWorktreePath)
: path.resolve(projectPath, providedWorktreePath);
// Verify the path exists before using it
await fs.access(resolvedPath);
worktreePath = resolvedPath;
console.log(`[AutoMode] Using provided worktree path (resolved): ${worktreePath}`);
} catch (error) {
console.error(`[AutoMode] Provided worktree path invalid or doesn't exist: ${providedWorktreePath}`, error);
// Fall through to create new worktree or use project path
}
}
if (!worktreePath && useWorktrees) {
// No specific worktree provided, create a new one for this feature // No specific worktree provided, create a new one for this feature
worktreePath = await this.setupWorktree( worktreePath = await this.setupWorktree(
projectPath, projectPath,
@@ -219,7 +235,8 @@ export class AutoModeService {
); );
} }
const workDir = worktreePath || projectPath; // Ensure workDir is always an absolute path for cross-platform compatibility
const workDir = worktreePath ? path.resolve(worktreePath) : path.resolve(projectPath);
this.runningFeatures.set(featureId, { this.runningFeatures.set(featureId, {
featureId, featureId,
@@ -382,14 +399,21 @@ export class AutoModeService {
// Use the provided worktreePath (from the feature's assigned branch) // Use the provided worktreePath (from the feature's assigned branch)
// Fall back to project path if not provided // Fall back to project path if not provided
let workDir = projectPath; let workDir = path.resolve(projectPath);
let worktreePath: string | null = null; let worktreePath: string | null = null;
if (providedWorktreePath) { if (providedWorktreePath) {
try { try {
await fs.access(providedWorktreePath); // Resolve to absolute path - critical for cross-platform compatibility
workDir = providedWorktreePath; // On Windows, relative paths or paths with forward slashes may not work correctly with cwd
worktreePath = providedWorktreePath; // On all platforms, absolute paths ensure commands execute in the correct directory
const resolvedPath = path.isAbsolute(providedWorktreePath)
? path.resolve(providedWorktreePath)
: path.resolve(projectPath, providedWorktreePath);
await fs.access(resolvedPath);
workDir = resolvedPath;
worktreePath = resolvedPath;
} catch { } catch {
// Worktree path provided but doesn't exist, use project path // Worktree path provided but doesn't exist, use project path
console.log(`[AutoMode] Provided worktreePath doesn't exist: ${providedWorktreePath}, using project path`); console.log(`[AutoMode] Provided worktreePath doesn't exist: ${providedWorktreePath}, using project path`);
@@ -863,7 +887,13 @@ Format your response as a structured markdown document.`;
} else if (line === "" && currentPath && currentBranch) { } else if (line === "" && currentPath && currentBranch) {
// End of a worktree entry // End of a worktree entry
if (currentBranch === branchName) { if (currentBranch === branchName) {
return currentPath; // Resolve to absolute path - git may return relative paths
// On Windows, this is critical for cwd to work correctly
// On all platforms, absolute paths ensure consistent behavior
const resolvedPath = path.isAbsolute(currentPath)
? path.resolve(currentPath)
: path.resolve(projectPath, currentPath);
return resolvedPath;
} }
currentPath = null; currentPath = null;
currentBranch = null; currentBranch = null;
@@ -872,7 +902,11 @@ Format your response as a structured markdown document.`;
// Check the last entry (if file doesn't end with newline) // Check the last entry (if file doesn't end with newline)
if (currentPath && currentBranch && currentBranch === branchName) { if (currentPath && currentBranch && currentBranch === branchName) {
return currentPath; // Resolve to absolute path for cross-platform compatibility
const resolvedPath = path.isAbsolute(currentPath)
? path.resolve(currentPath)
: path.resolve(projectPath, currentPath);
return resolvedPath;
} }
return null; return null;
@@ -889,6 +923,7 @@ Format your response as a structured markdown document.`;
// First, check if git already has a worktree for this branch (anywhere) // First, check if git already has a worktree for this branch (anywhere)
const existingWorktree = await this.findExistingWorktreeForBranch(projectPath, branchName); const existingWorktree = await this.findExistingWorktreeForBranch(projectPath, branchName);
if (existingWorktree) { if (existingWorktree) {
// Path is already resolved to absolute in findExistingWorktreeForBranch
console.log(`[AutoMode] Found existing worktree for branch "${branchName}" at: ${existingWorktree}`); console.log(`[AutoMode] Found existing worktree for branch "${branchName}" at: ${existingWorktree}`);
return existingWorktree; return existingWorktree;
} }
@@ -902,7 +937,8 @@ Format your response as a structured markdown document.`;
// Check if worktree directory already exists (might not be linked to branch) // Check if worktree directory already exists (might not be linked to branch)
try { try {
await fs.access(worktreePath); await fs.access(worktreePath);
return worktreePath; // Return absolute path for cross-platform compatibility
return path.resolve(worktreePath);
} catch { } catch {
// Create new worktree // Create new worktree
} }
@@ -919,13 +955,13 @@ Format your response as a structured markdown document.`;
await execAsync(`git worktree add "${worktreePath}" ${branchName}`, { await execAsync(`git worktree add "${worktreePath}" ${branchName}`, {
cwd: projectPath, cwd: projectPath,
}); });
// Return absolute path for cross-platform compatibility
return path.resolve(worktreePath);
} catch (error) { } catch (error) {
// Worktree creation failed, fall back to direct execution // Worktree creation failed, fall back to direct execution
console.error(`[AutoMode] Worktree creation failed:`, error); console.error(`[AutoMode] Worktree creation failed:`, error);
return projectPath; return path.resolve(projectPath);
} }
return worktreePath;
} }
private async loadFeature( private async loadFeature(