🔧 fix(vscode): worktree 目录配置改用插件自带设置面板,撤销 VS Code 原生配置
上一版误把 worktree 目录模板注册成 VS Code 原生设置(contributes.configuration + workspace.getConfiguration),但本插件有自己的设置面板(SettingsModal,存 globalState)。 改为: - Settings 新增 worktreeDirectory 字段(默认 ~/Sources/worktree/$project_name/$feature_name, 留空回退默认),贯穿 store/handler/两端 messages/useIssues/App/SettingsModal - SettingsModal「钩子」分组新增「工作树目录」输入项,走既有 save/load 流程 - handleImplement 改从 getSettings(panel.context).worktreeDirectory 读模板 - 删除 package.json 的 contributes.configuration(meta.ts 为 gitignore 生成物,已本地还原) deriveSlug / resolveWorktreeDir / ~ 展开 / 绝对路径 / feature/<slug> 逻辑不变。 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
+1
-11
@@ -2,7 +2,7 @@
|
||||
"publisher": "clurdra",
|
||||
"name": "superpowers-vscode-clurdra",
|
||||
"displayName": "Superpowers-clurdra",
|
||||
"version": "0.2.43",
|
||||
"version": "0.2.44",
|
||||
"packageManager": "pnpm@10.27.0",
|
||||
"description": "Superpowers specs and plans Kanban explorer",
|
||||
"author": "clurdra",
|
||||
@@ -93,16 +93,6 @@
|
||||
"group": "navigation"
|
||||
}
|
||||
]
|
||||
},
|
||||
"configuration": {
|
||||
"title": "Superpowers",
|
||||
"properties": {
|
||||
"superpowers.worktreeDirectory": {
|
||||
"type": "string",
|
||||
"default": "~/Sources/worktree/$project_name/$feature_name",
|
||||
"markdownDescription": "运行 Plan 时创建 worktree 的目录模板。支持占位符 `$project_root`(工作区根目录的绝对路径)、`$project_name`(工作区目录名)、`$feature_name`(spec/plan 的 slug)。开头的 `~` 会展开为当前用户主目录。默认会把 worktree 建到 `~/Sources/worktree/<项目名>/<slug>`。"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -840,6 +840,7 @@ export class KanbanWebviewPanel {
|
||||
reviewPrompt: s.reviewPrompt,
|
||||
devBranch: s.devBranch,
|
||||
autoBuildBranch: s.autoBuildBranch,
|
||||
worktreeDirectory: s.worktreeDirectory,
|
||||
worktreePostCreateScript: s.worktreePostCreateScript,
|
||||
worktreePreRemoveScript: s.worktreePreRemoveScript,
|
||||
implTabPreCreateScript: s.implTabPreCreateScript,
|
||||
@@ -899,6 +900,7 @@ export class KanbanWebviewPanel {
|
||||
reviewPrompt: s.reviewPrompt,
|
||||
devBranch: s.devBranch,
|
||||
autoBuildBranch: s.autoBuildBranch,
|
||||
worktreeDirectory: s.worktreeDirectory,
|
||||
worktreePostCreateScript: s.worktreePostCreateScript,
|
||||
worktreePreRemoveScript: s.worktreePreRemoveScript,
|
||||
implTabPreCreateScript: s.implTabPreCreateScript,
|
||||
|
||||
@@ -684,8 +684,9 @@ export async function handleImplement(
|
||||
// worktree 目录名用 spec/plan 的 slug(可读、与 spec 目录同名),不再用 planFile 的 hash。
|
||||
const slug = deriveSlug(planFile)
|
||||
const branch = `feature/${slug}`
|
||||
// 目录走 superpowers.worktreeDirectory 模板(设置里可改),默认 ~/Sources/worktree/<项目>/<slug>。
|
||||
const worktreeTemplate = workspace.getConfiguration('superpowers').get<string>('worktreeDirectory')
|
||||
// 目录模板取自插件自己的设置面板(globalState),不是 VS Code 原生配置;
|
||||
// 留空时回退到默认 ~/Sources/worktree/<项目>/<slug>。
|
||||
const worktreeTemplate = getSettings(panel.context).worktreeDirectory
|
||||
|| '~/Sources/worktree/$project_name/$feature_name'
|
||||
const worktreePath = resolveWorktreeDir(worktreeTemplate, workspaceRoot, slug)
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
@@ -43,6 +44,9 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
const trimmedYtToken = payload.youtrackToken.trim()
|
||||
const trimmedDevBranch = payload.devBranch.trim()
|
||||
const trimmedAutoBuildBranch = payload.autoBuildBranch.trim()
|
||||
// worktree dir template: '' = "use default", so don't preserve blanks
|
||||
// specially — getSettings falls back to the default at read time.
|
||||
const trimmedWorktreeDir = payload.worktreeDirectory.trim()
|
||||
// Hook script paths: keep '' meaningful (= "use default
|
||||
// .spx/*.sh"). Just strip whitespace.
|
||||
const trimmedPostCreate = payload.worktreePostCreateScript.trim()
|
||||
@@ -71,6 +75,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
reviewPrompt: payload.reviewPrompt || prev.reviewPrompt,
|
||||
devBranch: trimmedDevBranch || prev.devBranch,
|
||||
autoBuildBranch: trimmedAutoBuildBranch,
|
||||
worktreeDirectory: trimmedWorktreeDir || prev.worktreeDirectory,
|
||||
worktreePostCreateScript: trimmedPostCreate,
|
||||
worktreePreRemoveScript: trimmedPreRemove,
|
||||
implTabPreCreateScript: trimmedImplPre,
|
||||
@@ -89,6 +94,7 @@ export async function handleSettingsSave(panel: KanbanWebviewPanel, payload: {
|
||||
// fallback at read time.
|
||||
devBranch: trimmedDevBranch,
|
||||
autoBuildBranch: trimmedAutoBuildBranch,
|
||||
worktreeDirectory: trimmedWorktreeDir,
|
||||
worktreePostCreateScript: trimmedPostCreate,
|
||||
worktreePreRemoveScript: trimmedPreRemove,
|
||||
implTabPreCreateScript: trimmedImplPre,
|
||||
@@ -168,6 +174,7 @@ export async function handleEditSettingsRequest(panel: KanbanWebviewPanel): Prom
|
||||
reviewPrompt: s.reviewPrompt,
|
||||
devBranch: s.devBranch,
|
||||
autoBuildBranch: s.autoBuildBranch,
|
||||
worktreeDirectory: s.worktreeDirectory,
|
||||
worktreePostCreateScript: s.worktreePostCreateScript,
|
||||
worktreePreRemoveScript: s.worktreePreRemoveScript,
|
||||
implTabPreCreateScript: s.implTabPreCreateScript,
|
||||
|
||||
@@ -61,6 +61,7 @@ export type ExtensionToWebview
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
@@ -115,6 +116,7 @@ export type WebviewToExtension
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
|
||||
@@ -86,6 +86,14 @@ export interface Settings {
|
||||
* disabled because there's nothing to fast-forward.
|
||||
*/
|
||||
autoBuildBranch: string
|
||||
/**
|
||||
* Directory template for the worktree created when implementing a plan.
|
||||
* Placeholders: `$project_root` (workspace abs path), `$project_name` (its
|
||||
* basename), `$feature_name` (the spec/plan slug). A leading `~` expands to
|
||||
* the user's home dir. Empty string means "use the default
|
||||
* `~/Sources/worktree/$project_name/$feature_name`".
|
||||
*/
|
||||
worktreeDirectory: string
|
||||
/**
|
||||
* Path to a user-provided shell script that runs *after* the extension
|
||||
* creates a worktree via `git worktree add`. Empty string means use the
|
||||
@@ -157,6 +165,7 @@ function defaults(ctx: ExtensionContext): Settings {
|
||||
reviewPrompt: readDefaultPrompt(ctx.extensionPath, 'review'),
|
||||
devBranch: 'main',
|
||||
autoBuildBranch: '',
|
||||
worktreeDirectory: '~/Sources/worktree/$project_name/$feature_name',
|
||||
worktreePostCreateScript: '',
|
||||
worktreePreRemoveScript: '',
|
||||
implTabPreCreateScript: '',
|
||||
@@ -200,6 +209,11 @@ export function getSettings(ctx: ExtensionContext): Settings {
|
||||
const autoBuildBranch = typeof stored.autoBuildBranch === 'string'
|
||||
? stored.autoBuildBranch
|
||||
: base.autoBuildBranch
|
||||
// worktree dir template: empty/blank means "use the default template", so
|
||||
// fall back to base (unlike the hook paths below, where '' is meaningful).
|
||||
const worktreeDirectory = typeof stored.worktreeDirectory === 'string' && stored.worktreeDirectory.length > 0
|
||||
? stored.worktreeDirectory
|
||||
: base.worktreeDirectory
|
||||
// worktree hook script paths: '' is meaningful (= "use default
|
||||
// .spx/worktree-*.sh"), so don't coerce. Non-string legacy values fall
|
||||
// back to ''. The hook runner resolves '' to the default at execution
|
||||
@@ -238,6 +252,7 @@ export function getSettings(ctx: ExtensionContext): Settings {
|
||||
reviewPrompt,
|
||||
devBranch,
|
||||
autoBuildBranch,
|
||||
worktreeDirectory,
|
||||
worktreePostCreateScript,
|
||||
worktreePreRemoveScript,
|
||||
implTabPreCreateScript,
|
||||
|
||||
@@ -445,6 +445,7 @@ export function App() {
|
||||
initialReviewPrompt={settings?.reviewPrompt ?? ''}
|
||||
initialDevBranch={settings?.devBranch ?? 'main'}
|
||||
initialAutoBuildBranch={settings?.autoBuildBranch ?? ''}
|
||||
initialWorktreeDirectory={settings?.worktreeDirectory ?? ''}
|
||||
initialWorktreePostCreateScript={settings?.worktreePostCreateScript ?? ''}
|
||||
initialWorktreePreRemoveScript={settings?.worktreePreRemoveScript ?? ''}
|
||||
initialImplTabPreCreateScript={settings?.implTabPreCreateScript ?? ''}
|
||||
|
||||
@@ -23,6 +23,7 @@ interface SubmitValues {
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
@@ -59,6 +60,7 @@ export interface SettingsModalProps {
|
||||
initialReviewPrompt: string
|
||||
initialDevBranch: string
|
||||
initialAutoBuildBranch: string
|
||||
initialWorktreeDirectory: string
|
||||
initialWorktreePostCreateScript: string
|
||||
initialWorktreePreRemoveScript: string
|
||||
initialImplTabPreCreateScript: string
|
||||
@@ -128,6 +130,7 @@ export function SettingsModal({
|
||||
initialReviewPrompt,
|
||||
initialDevBranch,
|
||||
initialAutoBuildBranch,
|
||||
initialWorktreeDirectory,
|
||||
initialWorktreePostCreateScript,
|
||||
initialWorktreePreRemoveScript,
|
||||
initialImplTabPreCreateScript,
|
||||
@@ -151,6 +154,7 @@ export function SettingsModal({
|
||||
const [reviewPrompt, setReviewPrompt] = useState(initialReviewPrompt)
|
||||
const [devBranch, setDevBranch] = useState(initialDevBranch)
|
||||
const [autoBuildBranch, setAutoBuildBranch] = useState(initialAutoBuildBranch)
|
||||
const [worktreeDirectory, setWorktreeDirectory] = useState(initialWorktreeDirectory)
|
||||
const [worktreePostCreateScript, setWorktreePostCreateScript] = useState(initialWorktreePostCreateScript)
|
||||
const [worktreePreRemoveScript, setWorktreePreRemoveScript] = useState(initialWorktreePreRemoveScript)
|
||||
const [implTabPreCreateScript, setImplTabPreCreateScript] = useState(initialImplTabPreCreateScript)
|
||||
@@ -252,6 +256,8 @@ export function SettingsModal({
|
||||
reviewPrompt,
|
||||
devBranch: devBranch.trim(),
|
||||
autoBuildBranch: autoBuildBranch.trim(),
|
||||
// worktree 目录模板:trim 后留空 = 用默认模板(getSettings 读时回退)
|
||||
worktreeDirectory: worktreeDirectory.trim(),
|
||||
// 钩子路径保持原样 trim(前后空格无意义;空串 = 用默认 .spx/*.sh)
|
||||
worktreePostCreateScript: worktreePostCreateScript.trim(),
|
||||
worktreePreRemoveScript: worktreePreRemoveScript.trim(),
|
||||
@@ -605,6 +611,37 @@ export function SettingsModal({
|
||||
|
||||
{activeGroup === 'hooks' && (
|
||||
<>
|
||||
<Field
|
||||
label="工作树目录"
|
||||
hint={(
|
||||
<>
|
||||
实施 Plan 时创建 worktree 的目录模板。占位符
|
||||
{' '}
|
||||
<code>$project_root</code>
|
||||
(工作区绝对路径)、
|
||||
<code>$project_name</code>
|
||||
(工作区目录名)、
|
||||
<code>$feature_name</code>
|
||||
(spec/plan 的 slug);开头
|
||||
{' '}
|
||||
<code>~</code>
|
||||
{' '}
|
||||
展开为主目录。留空 = 默认
|
||||
{' '}
|
||||
<code>~/Sources/worktree/$project_name/$feature_name</code>
|
||||
。
|
||||
</>
|
||||
)}
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
value={worktreeDirectory}
|
||||
onChange={e => setWorktreeDirectory(e.target.value)}
|
||||
placeholder="~/Sources/worktree/$project_name/$feature_name"
|
||||
className={inputClass}
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<Field
|
||||
label="worktree 创建后脚本"
|
||||
hint={(
|
||||
|
||||
@@ -33,6 +33,7 @@ export interface SettingsValues {
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
@@ -55,6 +56,7 @@ export interface SettingsOverlayState {
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
@@ -265,6 +267,7 @@ export function useIssues(): UseIssuesResult {
|
||||
reviewPrompt: values.reviewPrompt,
|
||||
devBranch: values.devBranch,
|
||||
autoBuildBranch: values.autoBuildBranch,
|
||||
worktreeDirectory: values.worktreeDirectory,
|
||||
worktreePostCreateScript: values.worktreePostCreateScript,
|
||||
worktreePreRemoveScript: values.worktreePreRemoveScript,
|
||||
implTabPreCreateScript: values.implTabPreCreateScript,
|
||||
@@ -590,6 +593,7 @@ export function useIssues(): UseIssuesResult {
|
||||
reviewPrompt: msg.reviewPrompt,
|
||||
devBranch: msg.devBranch,
|
||||
autoBuildBranch: msg.autoBuildBranch,
|
||||
worktreeDirectory: msg.worktreeDirectory,
|
||||
worktreePostCreateScript: msg.worktreePostCreateScript,
|
||||
worktreePreRemoveScript: msg.worktreePreRemoveScript,
|
||||
implTabPreCreateScript: msg.implTabPreCreateScript,
|
||||
|
||||
@@ -76,6 +76,7 @@ export type ExtensionToWebview
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
@@ -130,6 +131,7 @@ export type WebviewToExtension
|
||||
reviewPrompt: string
|
||||
devBranch: string
|
||||
autoBuildBranch: string
|
||||
worktreeDirectory: string
|
||||
worktreePostCreateScript: string
|
||||
worktreePreRemoveScript: string
|
||||
implTabPreCreateScript: string
|
||||
|
||||
Reference in New Issue
Block a user