From 86136d4e56bd880d48ec904244dacb554e6ad45b Mon Sep 17 00:00:00 2001 From: abdushakoor12 Date: Thu, 7 May 2026 13:52:48 +0500 Subject: [PATCH 1/2] disable AI summary behavior entirely --- package.json | 13 +- src/extension.ts | 33 +--- src/test/e2e/aisummaries.e2e.test.ts | 176 --------------------- src/test/e2e/treeview.e2e.test.ts | 18 --- src/test/unit/modelSelection.unit.test.ts | 181 ---------------------- 5 files changed, 6 insertions(+), 415 deletions(-) delete mode 100644 src/test/e2e/aisummaries.e2e.test.ts delete mode 100644 src/test/unit/modelSelection.unit.test.ts diff --git a/package.json b/package.json index b707f6f..25780f1 100644 --- a/package.json +++ b/package.json @@ -131,16 +131,7 @@ "title": "Make Executable", "icon": "$(unlock)" }, - { - "command": "commandtree.generateSummaries", - "title": "Generate AI Summaries", - "icon": "$(sparkle)" - }, - { - "command": "commandtree.selectModel", - "title": "CommandTree: Select AI Model", - "icon": "$(hubot)" - } + ], "menus": { "view/title": [ @@ -371,7 +362,7 @@ }, "commandtree.enableAiSummaries": { "type": "boolean", - "default": true, + "default": false, "description": "Use GitHub Copilot to generate plain-language summaries of scripts, enabling semantic search" }, "commandtree.aiModel": { diff --git a/src/extension.ts b/src/extension.ts index 9161264..02b0286 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -10,15 +10,12 @@ import { TaskRunner } from "./runners/TaskRunner"; import { QuickTasksProvider } from "./QuickTasksProvider"; import { logger } from "./utils/logger"; import { initDb, disposeDb } from "./db/lifecycle"; -import { forceSelectModel } from "./semantic/summariser"; import { syncTagsFromConfig } from "./tags/tagSync"; import { setupFileWatchers } from "./watchers"; import { PrivateTaskDecorationProvider } from "./tree/PrivateTaskDecorationProvider"; import { appState } from "./state"; import { - initAiSummaries, registerDiscoveredCommands, - runSummarisation, syncAndSummarise, } from "./summaryOrchestration"; import type { SummaryDeps } from "./summaryOrchestration"; @@ -87,15 +84,11 @@ export async function activate(context: vscode.ExtensionContext): Promise { - initAiSummaries(getSummaryDeps(workspaceRoot)); - }) - .catch((e: unknown) => { - logger.error("Initial discovery failed", { - error: e instanceof Error ? e.message : String(e), - }); + initialDiscovery(workspaceRoot).catch((e: unknown) => { + logger.error("Initial discovery failed", { + error: e instanceof Error ? e.message : String(e), }); + }); } async function initDatabaseSafe(workspaceRoot: string): Promise { @@ -201,24 +194,6 @@ function registerFilterCommands(context: vscode.ExtensionContext): void { vscode.commands.registerCommand("commandtree.clearFilter", () => { getTreeProvider().clearFilters(); updateFilterContext(); - }), - vscode.commands.registerCommand("commandtree.generateSummaries", async () => { - const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; - if (workspaceRoot !== undefined) { - await runSummarisation(getSummaryDeps(workspaceRoot)); - } - }), - vscode.commands.registerCommand("commandtree.selectModel", async () => { - const result = await forceSelectModel(); - if (result.ok) { - vscode.window.showInformationMessage(`CommandTree: AI model set to ${result.value}`); - const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; - if (workspaceRoot !== undefined) { - await runSummarisation(getSummaryDeps(workspaceRoot)); - } - } else { - vscode.window.showWarningMessage(`CommandTree: ${result.error}`); - } }) ); } diff --git a/src/test/e2e/aisummaries.e2e.test.ts b/src/test/e2e/aisummaries.e2e.test.ts deleted file mode 100644 index f340876..0000000 --- a/src/test/e2e/aisummaries.e2e.test.ts +++ /dev/null @@ -1,176 +0,0 @@ -/** - * SPEC: ai-summary-generation - * AI SUMMARIES E2E TESTS - * - * These tests verify that the Copilot integration ACTUALLY WORKS: - * - Copilot authenticates successfully - * - Summaries are generated for discovered commands - * - Summary data appears on task items in the tree - * - * If Copilot auth fails (GitHubLoginFailed), these tests MUST FAIL. - */ - -import * as assert from "assert"; -import * as vscode from "vscode"; -import { - activateExtension, - sleep, - getCommandTreeProvider, - collectLeafTasks, - getTooltipText, - collectLeafItems, -} from "../helpers/helpers"; - -suite("AI Summary E2E Tests", () => { - suiteSetup(async function () { - this.timeout(30000); - await activateExtension(); - await sleep(2000); - }); - - suite("Copilot Integration", () => { - test("generateSummaries command is registered", async function () { - this.timeout(10000); - const commands = await vscode.commands.getCommands(true); - assert.ok(commands.includes("commandtree.generateSummaries"), "generateSummaries command must be registered"); - }); - - test("selectModel command is registered", async function () { - this.timeout(10000); - const commands = await vscode.commands.getCommands(true); - assert.ok(commands.includes("commandtree.selectModel"), "selectModel command must be registered"); - }); - - test("@exclude-ci Copilot models are available", async function () { - this.timeout(30000); - const models = await vscode.lm.selectChatModels({ vendor: "copilot" }); - assert.ok(models.length > 0, "At least one Copilot model must be available — is GitHub Copilot authenticated?"); - }); - - test("@exclude-ci multiple Copilot models are available for user to pick from", async function () { - this.timeout(30000); - const models = await vscode.lm.selectChatModels({ vendor: "copilot" }); - assert.ok( - models.length >= 1, - `Model picker needs models to show the user — got ${models.length}. Is GitHub Copilot authenticated?` - ); - // Every model must have an id and name for the picker to display - for (const m of models) { - assert.ok(m.id.length > 0, `Model must have an id — got empty string for "${m.name}"`); - assert.ok(m.name.length > 0, `Model must have a name — got empty string for "${m.id}"`); - } - }); - - test("@exclude-ci setting aiModel config selects that model for summarisation", async function () { - this.timeout(120000); - const models = await vscode.lm.selectChatModels({ vendor: "copilot" }); - assert.ok(models.length > 0, "Need at least one Copilot model — is GitHub Copilot authenticated?"); - const firstModel = models[0]; - if (firstModel === undefined) { - assert.fail("First model must exist"); - } - - // Set the model via config (same way the picker persists it) - const config = vscode.workspace.getConfiguration("commandtree"); - await config.update("aiModel", firstModel.id, vscode.ConfigurationTarget.Global); - - // Verify it persisted - const savedId = config.get("aiModel", ""); - assert.strictEqual(savedId, firstModel.id, "aiModel config must persist the chosen model ID"); - - // Run summarisation — it should use the configured model without prompting - await vscode.commands.executeCommand("commandtree.generateSummaries"); - await sleep(10000); - - // If we got here without a QuickPick blocking, the saved model was used - const provider = getCommandTreeProvider(); - const tasks = await collectLeafTasks(provider); - const withSummary = tasks.filter((t) => t.summary !== undefined && t.summary !== ""); - assert.ok( - withSummary.length > 0, - `Summarisation with model "${firstModel.id}" must produce results — got 0/${tasks.length}` - ); - - // Clean up — reset to empty so other tests aren't affected - await config.update("aiModel", "", vscode.ConfigurationTarget.Global); - }); - - test("aiModel config is empty by default so user gets prompted", async function () { - this.timeout(10000); - const config = vscode.workspace.getConfiguration("commandtree"); - // Reset to default - await config.update("aiModel", undefined, vscode.ConfigurationTarget.Global); - const savedId = config.get("aiModel", ""); - assert.strictEqual(savedId, "", "aiModel must default to empty string (triggers picker on first use)"); - }); - - test("@exclude-ci generateSummaries produces actual summaries on tasks", async function () { - this.timeout(120000); - const provider = getCommandTreeProvider(); - const tasksBefore = await collectLeafTasks(provider); - assert.ok(tasksBefore.length > 0, "Must have discovered tasks to summarise"); - - // Run the generate summaries command - await vscode.commands.executeCommand("commandtree.generateSummaries"); - - // Wait for summarisation to complete and refresh - await sleep(10000); - await vscode.commands.executeCommand("commandtree.refresh"); - await sleep(2000); - - const tasksAfter = await collectLeafTasks(provider); - const withSummary = tasksAfter.filter((t) => t.summary !== undefined && t.summary !== ""); - - assert.ok( - withSummary.length > 0, - `Copilot must generate at least one summary — got 0 out of ${tasksAfter.length} tasks. ` + - "If Copilot auth failed (GitHubLoginFailed), that is the root cause." - ); - }); - - test("@exclude-ci summaries appear in tree item tooltips", async function () { - this.timeout(120000); - const provider = getCommandTreeProvider(); - - // Ensure summaries have been generated (may already be done by previous test) - await vscode.commands.executeCommand("commandtree.generateSummaries"); - await sleep(10000); - await vscode.commands.executeCommand("commandtree.refresh"); - await sleep(2000); - - const items = await collectLeafItems(provider); - const withTooltipSummary = items.filter((item) => { - const tooltip = getTooltipText(item); - // Summaries appear as blockquotes in the tooltip markdown - return tooltip.includes("> "); - }); - - assert.ok(withTooltipSummary.length > 0, "At least one tree item must have a summary in its tooltip"); - }); - - test("@exclude-ci security warnings are surfaced in tree labels", async function () { - this.timeout(120000); - const provider = getCommandTreeProvider(); - - // After summaries are generated, any task with security risks - // should have the warning emoji in the label - await vscode.commands.executeCommand("commandtree.generateSummaries"); - await sleep(10000); - await vscode.commands.executeCommand("commandtree.refresh"); - await sleep(2000); - - const tasks = await collectLeafTasks(provider); - const withWarning = tasks.filter((t) => t.securityWarning !== undefined && t.securityWarning !== ""); - - // Not all tasks will have warnings, but if any do, verify they show in tooltips - if (withWarning.length > 0) { - const items = await collectLeafItems(provider); - const warningItems = items.filter((item) => { - const tooltip = getTooltipText(item); - return tooltip.includes("Security Warning"); - }); - assert.ok(warningItems.length > 0, "Tasks with security warnings must show warning in tooltip"); - } - }); - }); -}); diff --git a/src/test/e2e/treeview.e2e.test.ts b/src/test/e2e/treeview.e2e.test.ts index e4e334e..7322f0d 100644 --- a/src/test/e2e/treeview.e2e.test.ts +++ b/src/test/e2e/treeview.e2e.test.ts @@ -14,7 +14,6 @@ import { getCommandTreeProvider, getLabelString, collectLeafItems, - collectLeafTasks, refreshTasks, writeFile, deleteFile, @@ -232,23 +231,6 @@ suite("TreeView E2E Tests", () => { }); }); - suite("AI Summaries", () => { - test("@exclude-ci Copilot summarisation produces summaries for discovered tasks", async function () { - this.timeout(15000); - const provider = getCommandTreeProvider(); - // AI summaries: extension activation triggers summarisation via Copilot. - // If Copilot auth fails (GitHubLoginFailed), tasks will have no summaries. - // This MUST fail if the integration is broken. - const allTasks = await collectLeafTasks(provider); - const withSummary = allTasks.filter((t) => t.summary !== undefined && t.summary !== ""); - assert.ok( - withSummary.length > 0, - `Copilot summarisation must produce summaries — got 0 out of ${allTasks.length} tasks. ` + - "Check for GitHubLoginFailed errors above." - ); - }); - }); - suite("Private Make And Mise Tasks", () => { const makeRelativePath = "private-targets/Makefile"; const miseRelativePath = "private-targets/mise.toml"; diff --git a/src/test/unit/modelSelection.unit.test.ts b/src/test/unit/modelSelection.unit.test.ts deleted file mode 100644 index 103783c..0000000 --- a/src/test/unit/modelSelection.unit.test.ts +++ /dev/null @@ -1,181 +0,0 @@ -import * as assert from "assert"; -import { - pickConcreteModel, - resolveModel, - resolveModelAutomatically, - AUTO_MODEL_ID, -} from "../../semantic/modelSelection"; -import type { ModelRef, ModelSelectionDeps } from "../../semantic/modelSelection"; - -/** - * PURE UNIT TESTS for model selection logic. - * Tests pickConcreteModel and resolveModel — no VS Code dependency. - * SPEC: SPEC-AI-030 - */ -suite("Model Selection Unit Tests", () => { - const GPT4: ModelRef = { id: "gpt-4o", name: "GPT-4o" }; - const CLAUDE: ModelRef = { id: "claude-sonnet", name: "Claude Sonnet" }; - const AUTO: ModelRef = { id: AUTO_MODEL_ID, name: "Auto" }; - - suite("pickConcreteModel", () => { - test("returns specific model when preferredId matches", () => { - const result = pickConcreteModel({ - models: [GPT4, CLAUDE], - preferredId: "claude-sonnet", - }); - if (result === undefined) { - assert.fail("Expected a model but got undefined"); - } - assert.strictEqual(result.id, "claude-sonnet"); - assert.strictEqual(result.name, "Claude Sonnet"); - }); - - test("returns undefined when preferredId not found", () => { - const result = pickConcreteModel({ - models: [GPT4, CLAUDE], - preferredId: "nonexistent-model", - }); - assert.strictEqual(result, undefined); - }); - - test("auto picks first non-auto model", () => { - const result = pickConcreteModel({ - models: [AUTO, GPT4, CLAUDE], - preferredId: AUTO_MODEL_ID, - }); - assert.strictEqual(result?.id, "gpt-4o"); - }); - - test("auto falls back to first model if all are auto", () => { - const result = pickConcreteModel({ - models: [AUTO], - preferredId: AUTO_MODEL_ID, - }); - assert.strictEqual(result?.id, AUTO_MODEL_ID); - }); - - test("returns undefined for empty model list", () => { - const result = pickConcreteModel({ - models: [], - preferredId: "gpt-4o", - }); - assert.strictEqual(result, undefined); - }); - - test("auto with empty list returns undefined", () => { - const result = pickConcreteModel({ - models: [], - preferredId: AUTO_MODEL_ID, - }); - assert.strictEqual(result, undefined); - }); - }); - - suite("resolveModel", () => { - const createDeps = (overrides: Partial = {}): ModelSelectionDeps => ({ - getSavedId: (): string => "", - fetchById: async (): Promise => await Promise.resolve([]), - fetchAll: async (): Promise => await Promise.resolve([GPT4, CLAUDE]), - promptUser: async (models: readonly ModelRef[]): Promise => - await Promise.resolve(models[0]), - saveId: async (): Promise => { - await Promise.resolve(); - }, - ...overrides, - }); - - test("uses saved model ID when it exists and fetches successfully", async () => { - const deps = createDeps({ - getSavedId: (): string => "claude-sonnet", - fetchById: async (): Promise => await Promise.resolve([CLAUDE]), - }); - const result = await resolveModel(deps); - assert.ok(result.ok); - assert.strictEqual(result.value.id, "claude-sonnet"); - }); - - test("prompts user when no saved ID", async () => { - let prompted = false; - const deps = createDeps({ - getSavedId: (): string => "", - promptUser: async (models: readonly ModelRef[]): Promise => { - prompted = true; - return await Promise.resolve(models[0]); - }, - }); - const result = await resolveModel(deps); - assert.ok(result.ok); - assert.ok(prompted, "User must be prompted when no saved ID"); - }); - - test("prompts user when saved ID no longer available", async () => { - let prompted = false; - const deps = createDeps({ - getSavedId: (): string => "deleted-model", - fetchById: async (): Promise => await Promise.resolve([]), - promptUser: async (models: readonly ModelRef[]): Promise => { - prompted = true; - return await Promise.resolve(models[0]); - }, - }); - const result = await resolveModel(deps); - assert.ok(result.ok); - assert.ok(prompted, "User must be prompted when saved model is gone"); - }); - - test("saves the user's choice after prompting", async () => { - let savedId = ""; - const deps = createDeps({ - promptUser: async (): Promise => await Promise.resolve(CLAUDE), - saveId: async (id: string): Promise => { - savedId = id; - await Promise.resolve(); - }, - }); - const result = await resolveModel(deps); - assert.ok(result.ok); - assert.strictEqual(savedId, "claude-sonnet", "Chosen model ID must be persisted"); - }); - - test("returns error when user cancels picker", async () => { - const deps = createDeps({ - promptUser: async (): Promise => { - await Promise.resolve(); - return undefined; - }, - }); - const result = await resolveModel(deps); - assert.ok(!result.ok); - assert.strictEqual(result.error, "Model selection cancelled"); - }); - - test("returns error when no models available", async () => { - const deps = createDeps({ - fetchAll: async (): Promise => await Promise.resolve([]), - }); - const result = await resolveModel(deps); - assert.ok(!result.ok); - assert.strictEqual(result.error, "No Copilot model available after retries"); - }); - - test("automatic selection picks a concrete model without prompting", async () => { - let prompted = false; - let savedId = ""; - const deps = createDeps({ - promptUser: async (): Promise => { - prompted = true; - return await Promise.resolve(CLAUDE); - }, - saveId: async (id: string): Promise => { - savedId = id; - await Promise.resolve(); - }, - }); - const result = await resolveModelAutomatically(deps); - assert.ok(result.ok); - assert.strictEqual(result.value.id, "gpt-4o"); - assert.strictEqual(prompted, false, "Automatic background selection must not open the picker"); - assert.strictEqual(savedId, "", "Automatic background selection must not persist an implicit choice"); - }); - }); -}); From 8c81029b18d304c93380486b5ce92befd234d419 Mon Sep 17 00:00:00 2001 From: abdushakoor12 Date: Thu, 7 May 2026 14:07:07 +0500 Subject: [PATCH 2/2] fix --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index 25780f1..cfe79b2 100644 --- a/package.json +++ b/package.json @@ -130,8 +130,7 @@ "command": "commandtree.makeExecutable", "title": "Make Executable", "icon": "$(unlock)" - }, - + } ], "menus": { "view/title": [