diff --git a/src/__tests__/autoCommit.test.ts b/src/__tests__/autoCommit.test.ts index 66c22d0..0cc40f5 100644 --- a/src/__tests__/autoCommit.test.ts +++ b/src/__tests__/autoCommit.test.ts @@ -30,7 +30,7 @@ describe('autoCommitAndPush', () => { return Buffer.from(''); }); - const result = autoCommitAndPush('/tmp/clone', 'my-task'); + const result = autoCommitAndPush('/tmp/clone', 'my-task', '/project'); expect(result.success).toBe(true); expect(result.commitHash).toBe('abc1234'); @@ -50,10 +50,10 @@ describe('autoCommitAndPush', () => { expect.objectContaining({ cwd: '/tmp/clone' }) ); - // Verify push was called + // Verify push was called with projectDir directly (no origin remote) expect(mockExecFileSync).toHaveBeenCalledWith( 'git', - ['push', 'origin', 'HEAD'], + ['push', '/project', 'HEAD'], expect.objectContaining({ cwd: '/tmp/clone' }) ); }); @@ -67,7 +67,7 @@ describe('autoCommitAndPush', () => { return Buffer.from(''); }); - const result = autoCommitAndPush('/tmp/clone', 'my-task'); + const result = autoCommitAndPush('/tmp/clone', 'my-task', '/project'); expect(result.success).toBe(true); expect(result.commitHash).toBeUndefined(); @@ -90,7 +90,7 @@ describe('autoCommitAndPush', () => { // Verify push was NOT called expect(mockExecFileSync).not.toHaveBeenCalledWith( 'git', - ['push', 'origin', 'HEAD'], + ['push', '/project', 'HEAD'], expect.anything() ); }); @@ -100,7 +100,7 @@ describe('autoCommitAndPush', () => { throw new Error('git error: not a git repository'); }); - const result = autoCommitAndPush('/tmp/clone', 'my-task'); + const result = autoCommitAndPush('/tmp/clone', 'my-task', '/project'); expect(result.success).toBe(false); expect(result.commitHash).toBeUndefined(); @@ -120,7 +120,7 @@ describe('autoCommitAndPush', () => { return Buffer.from(''); }); - autoCommitAndPush('/tmp/clone', 'test-task'); + autoCommitAndPush('/tmp/clone', 'test-task', '/project'); // Find the commit call const commitCall = mockExecFileSync.mock.calls.find( @@ -145,7 +145,7 @@ describe('autoCommitAndPush', () => { return Buffer.from(''); }); - autoCommitAndPush('/tmp/clone', '認証機能を追加する'); + autoCommitAndPush('/tmp/clone', '認証機能を追加する', '/project'); const commitCall = mockExecFileSync.mock.calls.find( call => (call[1] as string[])[0] === 'commit' diff --git a/src/cli.ts b/src/cli.ts index 68dae7b..8fba1d9 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -228,7 +228,7 @@ program const taskSuccess = await executeTask(task, execCwd, selectedWorkflow, cwd); if (taskSuccess && isWorktree) { - const commitResult = autoCommitAndPush(execCwd, task); + const commitResult = autoCommitAndPush(execCwd, task, cwd); if (commitResult.success && commitResult.commitHash) { success(`Auto-committed & pushed: ${commitResult.commitHash}`); } else if (!commitResult.success) { diff --git a/src/commands/reviewTasks.ts b/src/commands/reviewTasks.ts index a5621b9..56d9551 100644 --- a/src/commands/reviewTasks.ts +++ b/src/commands/reviewTasks.ts @@ -317,7 +317,7 @@ export async function instructBranch( // 6. Auto-commit+push if successful if (taskSuccess) { - const commitResult = autoCommitAndPush(clone.path, item.taskSlug); + const commitResult = autoCommitAndPush(clone.path, item.taskSlug, projectDir); if (commitResult.success && commitResult.commitHash) { info(`Auto-committed & pushed: ${commitResult.commitHash}`); } else if (!commitResult.success) { diff --git a/src/commands/taskExecution.ts b/src/commands/taskExecution.ts index 975099e..fe71051 100644 --- a/src/commands/taskExecution.ts +++ b/src/commands/taskExecution.ts @@ -81,7 +81,7 @@ export async function executeAndCompleteTask( const completedAt = new Date().toISOString(); if (taskSuccess && isWorktree) { - const commitResult = autoCommitAndPush(execCwd, task.name); + const commitResult = autoCommitAndPush(execCwd, task.name, cwd); if (commitResult.success && commitResult.commitHash) { info(`Auto-committed & pushed: ${commitResult.commitHash}`); } else if (!commitResult.success) { diff --git a/src/task/autoCommit.ts b/src/task/autoCommit.ts index 7910257..3a358ad 100644 --- a/src/task/autoCommit.ts +++ b/src/task/autoCommit.ts @@ -32,8 +32,9 @@ export interface AutoCommitResult { * * @param cloneCwd - The clone directory * @param taskName - Task name used in commit message + * @param projectDir - The main project directory (push target) */ -export function autoCommitAndPush(cloneCwd: string, taskName: string): AutoCommitResult { +export function autoCommitAndPush(cloneCwd: string, taskName: string, projectDir: string): AutoCommitResult { log.info('Auto-commit starting', { cwd: cloneCwd, taskName }); try { @@ -71,13 +72,13 @@ export function autoCommitAndPush(cloneCwd: string, taskName: string): AutoCommi log.info('Auto-commit created', { commitHash, message: commitMessage }); - // Push to origin so the branch is reflected in the main repo - execFileSync('git', ['push', 'origin', 'HEAD'], { + // Push directly to the main repo (origin was removed to isolate the clone) + execFileSync('git', ['push', projectDir, 'HEAD'], { cwd: cloneCwd, stdio: 'pipe', }); - log.info('Pushed to origin'); + log.info('Pushed to main repo', { projectDir }); return { success: true, diff --git a/src/task/worktree.ts b/src/task/worktree.ts index ce39595..b228787 100644 --- a/src/task/worktree.ts +++ b/src/task/worktree.ts @@ -137,6 +137,12 @@ export function createSharedClone(projectDir: string, options: WorktreeOptions): stdio: 'pipe', }); + // Remove origin remote so Claude Code SDK won't follow it back to the main repo + execFileSync('git', ['remote', 'remove', 'origin'], { + cwd: clonePath, + stdio: 'pipe', + }); + // Checkout branch if (branchExists(clonePath, branch)) { execFileSync('git', ['checkout', branch], { @@ -185,6 +191,12 @@ export function createTempCloneForBranch(projectDir: string, branch: string): Wo stdio: 'pipe', }); + // Remove origin remote so Claude Code SDK won't follow it back to the main repo + execFileSync('git', ['remote', 'remove', 'origin'], { + cwd: clonePath, + stdio: 'pipe', + }); + execFileSync('git', ['checkout', branch], { cwd: clonePath, stdio: 'pipe',