@@ -63,6 +63,12 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
6363 case "launchEngine" :
6464 vscode . commands . executeCommand ( "testpilot-ai.launchEngine" ) ;
6565 break ;
66+ case "scanBlueprints" :
67+ await this . _handleScanBlueprints ( ) ;
68+ break ;
69+ case "browseBlueprint" :
70+ await this . _handleBrowseBlueprint ( ) ;
71+ break ;
6672 }
6773 } ) ;
6874
@@ -216,6 +222,34 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
216222 vscode . window . showInformationMessage ( "Bug摘要已复制到剪贴板,粘贴到聊天窗口让AI修复" ) ;
217223 }
218224
225+ private async _handleScanBlueprints ( ) : Promise < void > {
226+ const blueprints : string [ ] = [ ] ;
227+ const folders = vscode . workspace . workspaceFolders ;
228+ if ( folders ) {
229+ for ( const folder of folders ) {
230+ const pattern = new vscode . RelativePattern ( folder , "**/testpilot.json" ) ;
231+ const files = await vscode . workspace . findFiles ( pattern , "**/node_modules/**" , 20 ) ;
232+ for ( const f of files ) {
233+ blueprints . push ( f . fsPath ) ;
234+ }
235+ }
236+ }
237+ this . _postMessage ( { command : "blueprintList" , data : blueprints } ) ;
238+ }
239+
240+ private async _handleBrowseBlueprint ( ) : Promise < void > {
241+ const result = await vscode . window . showOpenDialog ( {
242+ canSelectFiles : true ,
243+ canSelectFolders : false ,
244+ canSelectMany : false ,
245+ filters : { "TestPilot 蓝本" : [ "json" ] } ,
246+ title : "选择蓝本文件 (testpilot.json)" ,
247+ } ) ;
248+ if ( result && result . length > 0 ) {
249+ this . _postMessage ( { command : "blueprintSelected" , data : result [ 0 ] . fsPath } ) ;
250+ }
251+ }
252+
219253 private _getHtml ( webview : vscode . Webview ) : string {
220254 const nonce = getNonce ( ) ;
221255
@@ -256,6 +290,9 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
256290 background: var(--input-bg); color: var(--input-fg);
257291 border: 1px solid var(--input-border); border-radius: 3px;
258292 }
293+ .input-row { display: flex; gap: 4px; align-items: center; }
294+ .input-row select { flex: 1; }
295+ .input-row button { width: auto; min-width: 32px; margin-top: 0; font-size: 14px; }
259296 .checkbox-row { display: flex; align-items: center; gap: 6px; margin: 6px 0; }
260297 .checkbox-row input[type="checkbox"] { width: auto; }
261298
@@ -368,7 +405,12 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
368405 <!-- 蓝本模式 -->
369406 <div class="tab-content active" id="panelBlueprint">
370407 <label>蓝本文件路径 *</label>
371- <input type="text" id="inputBlueprintPath" placeholder="D:\\projects\\my-app\\testpilot.json" />
408+ <div class="input-row">
409+ <select id="selectBlueprint" style="flex:1"><option value="">扫描中...</option></select>
410+ <button id="btnScanBp" title="扫描工作区" style="min-width:32px;padding:4px">🔍</button>
411+ <button id="btnBrowseBp" title="手动浏览" style="min-width:32px;padding:4px">📂</button>
412+ </div>
413+ <input type="text" id="inputBlueprintPath" placeholder="或手动输入路径..." style="font-size:11px;margin-top:4px" />
372414
373415 <label>覆盖 base_url(可选)</label>
374416 <input type="text" id="inputBpBaseUrl" placeholder="http://localhost:3000" />
@@ -495,6 +537,35 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
495537 panelExplore.classList.add("active"); panelBlueprint.classList.remove("active");
496538 });
497539
540+ // 蓝本自动扫描
541+ const selectBlueprint = document.getElementById("selectBlueprint");
542+ const inputBlueprintPath = document.getElementById("inputBlueprintPath");
543+
544+ // 下拉选择 → 同步到手动输入框
545+ selectBlueprint.addEventListener("change", () => {
546+ if (selectBlueprint.value) {
547+ inputBlueprintPath.value = selectBlueprint.value;
548+ }
549+ });
550+
551+ // 手动输入 → 清除下拉选中
552+ inputBlueprintPath.addEventListener("input", () => {
553+ selectBlueprint.value = "";
554+ });
555+
556+ // 扫描按钮
557+ document.getElementById("btnScanBp").addEventListener("click", () => {
558+ vscode.postMessage({ command: "scanBlueprints" });
559+ });
560+
561+ // 浏览按钮
562+ document.getElementById("btnBrowseBp").addEventListener("click", () => {
563+ vscode.postMessage({ command: "browseBlueprint" });
564+ });
565+
566+ // 页面加载时自动扫描
567+ vscode.postMessage({ command: "scanBlueprints" });
568+
498569 // 元素引用
499570 const statusDot = document.getElementById("statusDot");
500571 const engineStatus = document.getElementById("engineStatus");
@@ -630,9 +701,56 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
630701 case "progress": onProgress(msg.data); break;
631702 case "controlResult": addLog("控制: " + msg.data.action + " → " + msg.data.state, "info"); break;
632703 case "controlError": addLog("控制失败: " + msg.data.error, "error"); break;
704+ case "blueprintList": onBlueprintList(msg.data); break;
705+ case "blueprintSelected": onBlueprintSelected(msg.data); break;
633706 }
634707 });
635708
709+ function onBlueprintList(paths) {
710+ selectBlueprint.innerHTML = "";
711+ if (paths.length === 0) {
712+ const opt = document.createElement("option");
713+ opt.value = "";
714+ opt.textContent = "未找到 testpilot.json";
715+ selectBlueprint.appendChild(opt);
716+ return;
717+ }
718+ paths.forEach((p, i) => {
719+ const opt = document.createElement("option");
720+ opt.value = p;
721+ // 显示简短路径
722+ const parts = p.replace(/\\\\/g, "/").split("/");
723+ const short = parts.slice(-3).join("/");
724+ opt.textContent = short;
725+ selectBlueprint.appendChild(opt);
726+ });
727+ // 自动选中第一个并填入输入框
728+ selectBlueprint.selectedIndex = 0;
729+ inputBlueprintPath.value = paths[0];
730+ addLog("找到 " + paths.length + " 个蓝本文件", "info");
731+ }
732+
733+ function onBlueprintSelected(path) {
734+ inputBlueprintPath.value = path;
735+ // 在下拉里也加上(如果不在列表中)
736+ let found = false;
737+ for (let i = 0; i < selectBlueprint.options.length; i++) {
738+ if (selectBlueprint.options[i].value === path) {
739+ selectBlueprint.selectedIndex = i;
740+ found = true;
741+ break;
742+ }
743+ }
744+ if (!found) {
745+ const opt = document.createElement("option");
746+ opt.value = path;
747+ const parts = path.replace(/\\\\/g, "/").split("/");
748+ opt.textContent = parts.slice(-3).join("/");
749+ selectBlueprint.appendChild(opt);
750+ selectBlueprint.value = path;
751+ }
752+ }
753+
636754 function updateEngineStatus(data) {
637755 if (data.connected) {
638756 statusDot.className = "status-dot connected";
@@ -686,6 +804,11 @@ export class SidebarProvider implements vscode.WebviewViewProvider {
686804
687805 // 渲染步骤详情
688806 renderSteps(report.steps || []);
807+
808+ // MCP闭环提示
809+ if (report.bug_count > 0) {
810+ addLog("💡 在Cascade聊天窗口说「帮我修复这些Bug」可自动闭环修复", "info");
811+ }
689812 }
690813
691814 function renderBugs(bugs) {
0 commit comments