fix(worktree): normalize paths and improve worktree deletion workflow

This commit is contained in:
Bohdan Triapitsyn
2026-01-30 15:40:01 +02:00
parent b74ac777f5
commit f752e2d644
4 changed files with 181 additions and 97 deletions
@@ -73,7 +73,8 @@ const getWorktreeMethod = (client: unknown, key: string): WorktreeRemovalMethod
if (typeof candidate !== 'function') {
return null;
}
return candidate as WorktreeRemovalMethod;
// Keep method binding; SDK methods use `this.client`.
return (params?: WorktreeRemovalParams) => (candidate as (this: unknown, p?: WorktreeRemovalParams) => Promise<unknown>).call(client, params);
};
export const buildSdkStartCommand = (args: {
@@ -245,23 +246,44 @@ export async function removeProjectWorktree(project: ProjectRef, worktree: Workt
if (worktree.source === 'sdk') {
const scoped = opencodeClient.getScopedApiClient(projectDirectory);
const worktreeClient = scoped.worktree as unknown;
const force = Boolean(options?.force ?? true);
const fallbackRemoveViaGit = async () => {
await removeGitWorktree(projectDirectory, { path: worktree.path, force });
};
const removeMethod = getWorktreeMethod(worktreeClient, 'remove');
if (removeMethod) {
await removeMethod({ worktreeRemoveInput: { directory: worktree.path } });
const raw = await removeMethod({ worktreeRemoveInput: { directory: worktree.path } });
const ok = unwrapSdkData(raw);
if (ok !== true) {
await fallbackRemoveViaGit();
}
} else {
const deleteMethod = getWorktreeMethod(worktreeClient, 'delete');
if (deleteMethod) {
await deleteMethod({ worktreeDeleteInput: { directory: worktree.path } });
const raw = await deleteMethod({ worktreeDeleteInput: { directory: worktree.path } });
const ok = unwrapSdkData(raw);
if (ok !== true) {
await fallbackRemoveViaGit();
}
} else {
const archiveMethod = getWorktreeMethod(worktreeClient, 'archive');
if (archiveMethod) {
await archiveMethod({ worktreeArchiveInput: { directory: worktree.path } });
const raw = await archiveMethod({ worktreeArchiveInput: { directory: worktree.path } });
const ok = unwrapSdkData(raw);
if (ok !== true) {
await fallbackRemoveViaGit();
}
} else {
throw new Error('Worktree removal is not supported by this SDK version.');
}
}
}
// Some OpenCode builds only update internal state; remove git worktree best-effort.
await fallbackRemoveViaGit().catch(() => undefined);
// Best-effort branch cleanup. Some OpenCode builds may keep the branch.
const branchName = (worktree.branch || '').replace(/^refs\/heads\//, '').trim();
if (deleteLocalBranch && branchName) {