feat(vscode): worktree 打开方式可选 VS Code/PyCharm,并修复 handleOpenWorktree 不展开 ~ 的打开报错

- worktree.ts: handleOpenWorktree/handleDeleteWorktree/dispatchImplTabPostCloseAsync 三处改用 resolveWorktreePath,修复带 ~ 的存量路径被拼成 <工作区>/~/... 导致打不开
- 新增全局设置 worktreeOpenWith('vscode' | 'pycharm',默认 vscode),贯穿 store/settings handler/messages/KanbanPanel 与 webview 的 messages/useIssues/App/SettingsModal「钩子」分组新增「工作树打开方式」下拉
- handleOpenWorktree 按设置分支:pycharm 走 JetBrains pycharm 启动器,ENOENT 时提示启用命令行启动器

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-24 05:20:22 +08:00
co-authored by Claude Opus 4.7
parent 5e7a3d20d9
commit cacc60eb0e
9 changed files with 78 additions and 4 deletions
+23 -4
View File
@@ -1,10 +1,10 @@
import type { HookContext } from '../../git/worktreeHooks'
import type { KanbanWebviewPanel } from '../KanbanPanel'
import { execFile } from 'node:child_process'
import * as path from 'node:path'
import { commands, Uri, window, workspace } from 'vscode'
import { getToken } from '../../auth/secrets'
import { detectRepo } from '../../git/remote'
import { resolveWorktreePath } from '../../git/worktree'
import {
runImplTabPostCloseHook,
runImplTabPreCreateHook,
@@ -26,7 +26,26 @@ export async function handleOpenWorktree(panel: KanbanWebviewPanel, relPath: str
void window.showErrorMessage('请先打开一个工作区文件夹')
return
}
const abs = path.isAbsolute(relPath) ? relPath : path.join(workspaceRoot, relPath)
// 存量工单可能存了带 `~` 的 worktreePath,必须经 resolveWorktreePath 展开,
// 否则 path.join(workspaceRoot, '~/...') 会拼出不存在的路径。
const abs = resolveWorktreePath(relPath, workspaceRoot)
// PyCharm 分支用 JetBrains 启动器打开目录。错误在回调里自行处理,不要落进
// 下面 openFolder 的 catch。
const { worktreeOpenWith } = getSettings(panel.context)
if (worktreeOpenWith === 'pycharm') {
execFile('pycharm', [abs], (err) => {
if (err) {
const code = (err as NodeJS.ErrnoException).code ?? 'unknown'
const message = code === 'ENOENT'
? '找不到 pycharm 命令,请在 JetBrains Toolbox 里启用命令行启动器或把 pycharm 加入 PATH'
: err.message
void window.showErrorMessage(`用 PyCharm 打开失败: ${message}`)
}
})
return
}
try {
await commands.executeCommand('vscode.openFolder', Uri.file(abs), true)
}
@@ -56,7 +75,7 @@ export async function handleDeleteWorktree(panel: KanbanWebviewPanel, issueNumbe
if (choice !== '删除')
return
const abs = path.isAbsolute(relPath) ? relPath : path.join(workspaceRoot, relPath)
const abs = resolveWorktreePath(relPath, workspaceRoot)
// Run the pre-remove lifecycle hook so the user can tear down resources
// (close IDE windows, etc.) before the worktree dir vanishes. Best-effort
@@ -265,7 +284,7 @@ export async function dispatchImplTabPostCloseAsync(panel: KanbanWebviewPanel, i
const wt = typeof stateObj.worktreePath === 'string' ? stateObj.worktreePath : ''
if (!wt)
return
const absWorktree = path.isAbsolute(wt) ? wt : path.join(workspaceRoot, wt)
const absWorktree = resolveWorktreePath(wt, workspaceRoot)
const settings = getSettings(panel.context)
await dispatchWorktreeHook(panel, 'impl-tab-post-close', {
workspaceRoot,