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= * &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 { 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}"`) }