Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,20 @@ export class CopilotCLIWorktreeManager {
});
}

async createWorktreeSilently(): Promise<string | undefined> {
try {
const repository = this.gitService.activeRepository.get();
if (!repository) {
return undefined;
}
const worktreePath = await this.gitService.createWorktree(repository.rootUri);
return worktreePath;
} catch (error) {
// Silent failure - no warnings displayed
return undefined;
}
}

async storeWorktreePath(sessionId: string, workingDirectory: string): Promise<void> {
this._sessionWorktrees.set(sessionId, workingDirectory);
const sessionWorktrees = this.extensionContext.globalState.get<Record<string, string>>(CopilotCLIWorktreeManager.COPILOT_CLI_SESSION_WORKTREE_MEMENTO_KEY, {});
Expand Down Expand Up @@ -534,8 +548,23 @@ export class CopilotCLIChatSessionParticipant extends Disposable {
const history = await this.summarizer.provideChatSummary(context, token);

const requestPrompt = history ? `${prompt}\n**Summary**\n${history}` : prompt;
const session = await this.sessionService.createSession(requestPrompt, {}, token);

// Create a temporary session ID to check isolation preference
const tempSessionId = `untitled-${Date.now()}`;
const isolationEnabled = this.worktreeManager.getIsolationPreference(tempSessionId);

// Create worktree silently if isolation is enabled, before creating the session
const workingDirectory = isolationEnabled
? await this.worktreeManager.createWorktreeSilently() ?? await this.getDefaultWorkingDirectory()
: await this.getDefaultWorkingDirectory();

const session = await this.sessionService.createSession(requestPrompt, { workingDirectory, isolationEnabled }, token);
try {
// Store the worktree path for this session
if (workingDirectory) {
await this.worktreeManager.storeWorktreePath(session.object.sessionId, workingDirectory);
}

await this.commandExecutionService.executeCommand('vscode.open', SessionIdForCLI.getResource(session.object.sessionId));
await this.commandExecutionService.executeCommand('workbench.action.chat.submit', { inputValue: requestPrompt });
return {};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ vi.mock('../copilotCLITerminalIntegration', () => {

class FakeWorktreeManager extends mock<CopilotCLIWorktreeManager>() {
override createWorktree = vi.fn(async () => undefined);
override createWorktreeSilently = vi.fn(async (): Promise<string | undefined> => undefined);
override storeWorktreePath = vi.fn(async () => { });
override getWorktreePath = vi.fn((_id: string) => undefined);
override getIsolationPreference = vi.fn(() => false);
Expand Down Expand Up @@ -293,6 +294,28 @@ describe('CopilotCLIChatSessionParticipant.handleRequest', () => {
expect(execSpy.mock.calls[1]).toEqual(['workbench.action.chat.submit', { inputValue: expectedPrompt }]);
});

it('invokes handlePushConfirmationData with isolation enabled creates worktree silently', async () => {
// Enable isolation preference
worktree.getIsolationPreference = vi.fn(() => true);
worktree.createWorktreeSilently = vi.fn(async () => '/tmp/worktree-path');

const request = new TestChatRequest('Push this');
const context = { chatSessionContext: undefined, chatSummary: undefined } as unknown as vscode.ChatContext;
const stream = new MockChatResponseStream();
const token = disposables.add(new CancellationTokenSource()).token;

await participant.createHandler()(request, context, stream, token);

// Verify worktree was created silently (not with stream parameter)
expect(worktree.createWorktreeSilently).toHaveBeenCalledTimes(1);
expect(worktree.createWorktree).not.toHaveBeenCalled();

// Verify worktree path was stored
expect(worktree.storeWorktreePath).toHaveBeenCalledTimes(1);
const sessionId = Array.from(manager.sessions.keys())[0];
expect(worktree.storeWorktreePath).toHaveBeenCalledWith(sessionId, '/tmp/worktree-path');
});

it('handleConfirmationData accepts uncommitted-changes and records push', async () => {
// Existing session (non-untitled) so confirmation path is hit
const sessionId = 'existing-confirm';
Expand Down
Loading