新建工单、实施、审查、测试、原型、续聊、冲突解决、会话管理和各种恢复会话, 都改成开终端时直接指定要跑的程序(用户默认 shell 以 -ilc 'exec "$@"' 拉起), 环境变量照常加载,参数原样传入。程序退出时标签页随之关闭。 提示词和配置文件路径里带单引号也不再拒绝执行,相关的转义和拦截全部删掉。
94 lines
3.2 KiB
TypeScript
94 lines
3.2 KiB
TypeScript
import type { ExtensionContext, Uri } from 'vscode'
|
||
import { promises as fsp } from 'node:fs'
|
||
import { window, workspace } from 'vscode'
|
||
import { listClaudeProfiles } from '../cc/profiles'
|
||
import { logger } from '../logging/logger'
|
||
import { createCcSessionTab } from '../panel/handlers/managedSessions'
|
||
|
||
/**
|
||
* 外部触发开 cc 会话 tab 的 URI 入口(供 delegate-fix skill 等外部方调用):
|
||
*
|
||
* vscode://clurdra.superpowers-vscode-clurdra/create-session
|
||
* ?profile=<profile 名,不带 .json>
|
||
* &name=<终端 tab 名,如 fix:fable5>
|
||
* &prompt=<初始 prompt>
|
||
* &cwd=<绝对路径,可选,缺省用 workspaceRoot>
|
||
*
|
||
* 编码契约:VS Code 把外部 URI 交给扩展前会对 query 整体做一次 percent-decode
|
||
* (vs/base/common/uri.ts 的 URI.parse),这里的 URLSearchParams 是第二次 decode。
|
||
* 因此发送方对参数值必须做两层 urlencode,值里的 & = % + 等字符才能安全通过。
|
||
*/
|
||
export function registerCreateSessionUriHandler(context: ExtensionContext): void {
|
||
context.subscriptions.push(window.registerUriHandler({
|
||
handleUri: (uri: Uri): void => {
|
||
void handleCreateSessionUri(uri).catch((err) => {
|
||
void window.showErrorMessage(`处理 create-session URI 失败:${err instanceof Error ? err.message : String(err)}`)
|
||
})
|
||
},
|
||
}))
|
||
}
|
||
|
||
async function handleCreateSessionUri(uri: Uri): Promise<void> {
|
||
if (uri.path !== '/create-session') {
|
||
void window.showErrorMessage(`未知的 superpowers URI path: ${uri.path}`)
|
||
return
|
||
}
|
||
|
||
const params = new URLSearchParams(uri.query)
|
||
const profileName = (params.get('profile') ?? '').trim()
|
||
const name = (params.get('name') ?? '').trim()
|
||
const prompt = (params.get('prompt') ?? '').trim()
|
||
const cwdParam = (params.get('cwd') ?? '').trim()
|
||
|
||
if (!profileName) {
|
||
void window.showErrorMessage('create-session URI 缺少 profile 参数')
|
||
return
|
||
}
|
||
if (!prompt) {
|
||
void window.showErrorMessage('create-session URI 缺少 prompt 参数')
|
||
return
|
||
}
|
||
|
||
const profiles = await listClaudeProfiles()
|
||
const profile = profiles.find(p => p.name === profileName)
|
||
if (!profile) {
|
||
void window.showErrorMessage(
|
||
`create-session URI:找不到 profile "${profileName}"(可用:${profiles.map(p => p.name).join(', ') || '无'})`,
|
||
)
|
||
return
|
||
}
|
||
|
||
const workspaceRoot = workspace.workspaceFolders?.[0]?.uri.fsPath
|
||
if (!workspaceRoot) {
|
||
void window.showErrorMessage('请先打开一个工作区文件夹')
|
||
return
|
||
}
|
||
|
||
let cwd = workspaceRoot
|
||
if (cwdParam) {
|
||
const stat = await fsp.stat(cwdParam).catch(() => undefined)
|
||
if (!stat?.isDirectory()) {
|
||
void window.showErrorMessage(`create-session URI:cwd 不存在或不是目录 (${cwdParam})`)
|
||
return
|
||
}
|
||
cwd = cwdParam
|
||
}
|
||
|
||
const created = await createCcSessionTab({
|
||
workspaceRoot,
|
||
profilePath: profile.path,
|
||
name,
|
||
prompt,
|
||
cwd,
|
||
panel: null,
|
||
register: false,
|
||
})
|
||
|
||
logger.add({
|
||
level: 'info',
|
||
source: 'uri',
|
||
message: `URI handler 已启动 cc 会话 tab "${created.terminalName}" (profile=${profileName})`,
|
||
})
|
||
void window.showInformationMessage(`已开 cc 会话 tab "${created.terminalName}"`)
|
||
}
|