diff --git a/.github/workflows/echocode-tests.yml b/.github/workflows/echocode-tests.yml index 9f621da..325b7e9 100644 --- a/.github/workflows/echocode-tests.yml +++ b/.github/workflows/echocode-tests.yml @@ -1,3 +1,5 @@ +name: EchoCode Tests + on: push: branches: ["**"] @@ -6,16 +8,40 @@ on: jobs: run-tests: + # The suite is run against several Node majors because the test harness behaves + # differently on each, and the difference is not cosmetic: + # + # - Node 20/22 have no native TypeScript support, so ts-node compiles test/*.ts. + # With tsconfig's `module: Node16` and no `"type": "module"` in package.json, + # that output is CommonJS: `require` exists, and `import.meta` is a hard + # compile error (TS1470). + # - Node 24 strips types natively and infers ESM from the `import` syntax, so the + # same files load as ES modules: `import.meta` is legal, and `require` is not + # defined at all. + # + # Code can therefore be correct on one major and fail outright on another. Node 20 + # is closest to what ships to users (VS Code 1.99's extension host bundles Node 20 + # via Electron), while contributors on a current Node install hit the 24 behaviour. + # Testing only one hides breakage in the other until someone runs CI by hand. + name: tests (node ${{ matrix.node-version }}) runs-on: ubuntu-latest + + strategy: + # Report every version's result. Cancelling siblings on the first failure would + # hide exactly the thing this matrix exists to reveal: which majors are affected. + fail-fast: false + matrix: + node-version: [20, 22, 24] + steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 20 + node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm install - name: Run automated tests - run: xvfb-run -a npm test \ No newline at end of file + run: xvfb-run -a npm test diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index ae79974..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: Publish VSCode Extension - -on: - push: - tags: - - 'v*' # Only trigger when you push a tag like v1.0.0 - -jobs: - publish: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Set up Node.js - uses: actions/setup-node@v4 - with: - node-version: '22.x' - - - name: Install vsce - run: npm install -g @vscode/vsce - - - name: Install dependencies - run: npm install - - - name: Publish Extension - run: vsce publish --pat ${{ secrets.MARKETPLACE_PAT }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..949e434 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,32 @@ +name: Release + +on: + push: + branches: [main] + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: 20 + + - name: Install dependencies + run: npm ci + + - name: Run automated tests + run: xvfb-run -a npm test + + - name: Release + run: npx semantic-release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VSCE_PAT: ${{ secrets.MARKETPLACE_PAT }} diff --git a/.gitignore b/.gitignore index 7d3c5e8..af2f729 100644 --- a/.gitignore +++ b/.gitignore @@ -40,5 +40,24 @@ coverage/ echocode-venv/ TestFile1.cpp +# Generated per-machine on activation from the user's own installed +# VS Code commands (see buildExternalCommandRegistry). Regenerates if +# absent, so it must never be committed. Core/program_settings/program_settings/external_commands.json -Core/program_settings/program_settings/external_commands.json +wiki/.obsidian/app.json +wiki/.obsidian/appearance.json +wiki/.obsidian/core-plugins.json +wiki/.obsidian/workspace.json + +# Feature diagrams: the rendered SVGs are tracked, because the wiki pages embed them +# and they must resolve in a fresh clone. The .drawio sources and the generators that +# produce both are local-only: +# node wiki/diagrams/generate.js && python3 wiki/diagrams/render_svg.py +# +# Note the shape of these rules. Excluding "wiki/diagrams/" outright would make the +# negations below impossible β€” git cannot re-include a file whose parent directory is +# excluded β€” so the contents are excluded and svg/ is re-included instead. +wiki/diagrams/* +!wiki/diagrams/svg/ +wiki/diagrams/svg/* +!wiki/diagrams/svg/*.svg diff --git a/.releaserc.json b/.releaserc.json new file mode 100644 index 0000000..333b061 --- /dev/null +++ b/.releaserc.json @@ -0,0 +1,18 @@ +{ + "branches": ["main"], + "plugins": [ + "@semantic-release/commit-analyzer", + "@semantic-release/release-notes-generator", + ["@semantic-release/changelog", { "changelogFile": "CHANGELOG.md" }], + ["@semantic-release/npm", { "npmPublish": false }], + [ + "@semantic-release/git", + { + "assets": ["package.json", "package-lock.json", "CHANGELOG.md"], + "message": "chore(release): ${nextRelease.version} [skip ci]\n\n${nextRelease.notes}" + } + ], + ["@semantic-release/exec", { "publishCmd": "npx vsce publish -p ${VSCE_PAT}" }], + "@semantic-release/github" + ] +} diff --git a/.vscodeignore b/.vscodeignore index 24d8a61..172eae1 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -1,8 +1,9 @@ -# πŸš€ Ignore VS Code workspace settings -!node_modules/** - -# πŸš€ Ignore Node.js dependencies (not needed in the packaged extension) -node_modules/ +# πŸš€ node_modules is intentionally NOT ignored here. +# vsce's default packaging resolves node_modules against "dependencies" in +# package.json and prunes devDependencies automatically; excluding the whole +# folder would strip out runtime deps (mammoth, pdf-parse, dotenv, say, +# sound-play, ffmpeg-static, fluent-ffmpeg) and break the extension for +# anyone installing it from the Marketplace. # πŸš€ Ignore Git metadata .git/ @@ -13,8 +14,20 @@ node_modules/ # πŸš€ Ignore test-related files .vscode-test/ +.vscode-test.mjs test/ +# πŸš€ Ignore the TS command-registry scaffold used only by the test suite +# (the real entry point is extension.js at the repo root; nothing at +# runtime requires src/ or its compiled out/ output). +src/ +out/ +tsconfig.json + +# πŸš€ Ignore CI/CD and repo scaffolding not needed by an installed extension +.github/ +vsc-extension-quickstart.md + # πŸš€ Ignore temporary or cache files **/*.map **/.vscode-test.* diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..6d054ce --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,39 @@ +# Contributing to EchoCode + +## Commit message convention + +Every push to `main` runs [semantic-release](https://semantic-release.gitbook.io/), which computes the next +version number, updates `CHANGELOG.md`, tags the release, and publishes the extension to the VS Code +Marketplace automatically β€” no manual version bump required. It does this by reading your commit messages, so +commits on `main` (including squash-merge commit messages) must follow +[Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + +[optional body] + +[optional footer(s)] +``` + +Common types and what they trigger: + +| Type | Effect | +| --- | --- | +| `fix:` | Patch release (e.g. `2.1.0` β†’ `2.1.1`) | +| `feat:` | Minor release (e.g. `2.1.0` β†’ `2.2.0`) | +| `feat!:` or a footer starting with `BREAKING CHANGE:` | Major release (e.g. `2.1.0` β†’ `3.0.0`) | +| `docs:`, `chore:`, `refactor:`, `style:`, `test:`, `ci:` | No release β€” tests still run, nothing is published | + +Examples: + +``` +fix: correct guidance level not persisting across sessions +feat: add microphone auto-selection on startup +feat!: drop support for VS Code < 1.99 + +BREAKING CHANGE: minimum supported VS Code version is now 1.99. +``` + +If a merge to `main` contains no release-worthy commits, the `Release` workflow still runs the test suite but +publishes nothing. diff --git a/Core/program_settings/program_settings/AIrequest.js b/Core/program_settings/program_settings/AIrequest.js index 36cfd01..15d4431 100644 --- a/Core/program_settings/program_settings/AIrequest.js +++ b/Core/program_settings/program_settings/AIrequest.js @@ -1,4 +1,202 @@ const vscode = require("vscode"); +const { requestJson, requestNdjson } = require("./httpClient"); +const { getPreset, listApiModels, sendApiChat } = require("./apiProviders"); +const { getApiKey } = require("./secretStore"); + +/** + * Reads the active AI backend from settings. + * + * `echocode.aiProvider` is the canonical setting. `echocode.useLocalOllama` predates + * it and is still honoured when the new setting is blank, so existing installs keep + * working after an update without the user touching anything. + */ +function getAiSettings() { + const config = vscode.workspace.getConfiguration("echocode"); + + const useLocalOllama = config.get("useLocalOllama", false); + const explicitProvider = config.get("aiProvider", ""); + const provider = explicitProvider || (useLocalOllama ? "ollama" : "copilot"); + + const apiProviderId = config.get("apiProvider", ""); + const preset = getPreset(apiProviderId); + + return { + provider, + useLocalOllama: provider === "ollama", + ollamaBaseUrl: config.get("ollamaBaseUrl", "http://127.0.0.1:11434"), + ollamaModel: config.get("ollamaModel", "llama3.2"), + copilotModel: config.get("copilotModel", ""), + apiProviderId, + apiBaseUrl: config.get("apiBaseUrl", "") || preset?.baseUrl || "", + apiModel: config.get("apiModel", ""), + apiWire: preset?.wire || "openai", + }; +} + +/** + * Settings plus the API key from secret storage. Only the hosted-API path needs the + * key, so the async lookup stays out of the synchronous settings read. + */ +async function resolveAiSettings() { + const settings = getAiSettings(); + if (settings.provider === "api") { + settings.apiKey = await getApiKey(settings.apiProviderId); + } + return settings; +} + +async function listOllamaModels(opts = {}) { + const settings = getAiSettings(); + const baseUrl = String(opts.baseUrl || settings.ollamaBaseUrl || "").replace( + /\/$/, + "", + ); + const url = `${baseUrl}/api/tags`; + + const json = await requestJson(url, null, { + method: "GET", + timeoutMs: opts.timeoutMs ?? 3000, + label: "Ollama", + }); + + if (!json || !Array.isArray(json.models)) { + return []; + } + + return json.models.map((m) => m && m.name).filter(Boolean); +} + +/** + * Downloads a model into the user's Ollama install. Uses Ollama's HTTP API rather + * than shelling out to `ollama pull` so it also works when the server is on another + * machine, and so the NDJSON progress stream can drive a real progress bar. + */ +async function pullOllamaModel(modelName, opts = {}) { + const settings = getAiSettings(); + const baseUrl = String(opts.baseUrl || settings.ollamaBaseUrl || "").replace( + /\/$/, + "", + ); + + let lastStatus = ""; + await requestNdjson( + `${baseUrl}/api/pull`, + { name: modelName, stream: true }, + { + label: "Ollama", + cancellationToken: opts.cancellationToken, + onEvent: (event) => { + if (event && event.error) { + throw new Error(event.error); + } + lastStatus = (event && event.status) || lastStatus; + if (opts.onProgress) { + opts.onProgress({ + status: lastStatus, + completed: (event && event.completed) || 0, + total: (event && event.total) || 0, + }); + } + }, + }, + ); + + return lastStatus; +} + +/** + * Lists the Copilot models VS Code can currently offer. + * + * `selectChatModels` does not read a cached list β€” it waits for Copilot Chat to + * activate, sign in and fetch its catalog, which is unbounded and routinely runs to + * tens of seconds on a cold window (and always does for users whose backend is Ollama + * or a hosted API, since nothing warms Copilot for them). Every caller here only needs + * this to populate a menu, so cap the wait and treat "too slow" exactly like "not + * installed": the request path uses `selectModel()` below, which is deliberately + * uncapped because there the model *is* the work. + */ +async function listCopilotModels(opts = {}) { + if (!vscode.lm || typeof vscode.lm.selectChatModels !== "function") { + return []; + } + + const timeoutMs = opts.timeoutMs ?? 3000; + const TIMED_OUT = Symbol("copilot-timeout"); + + const selection = vscode.lm.selectChatModels({ vendor: "copilot" }); + // The losing side of the race stays pending; without this a late rejection would + // surface as an unhandled promise rejection long after we stopped caring. + selection.catch(() => {}); + + let timer; + const models = await Promise.race([ + selection, + new Promise((resolve) => { + timer = setTimeout(() => resolve(TIMED_OUT), timeoutMs); + }), + ]).finally(() => clearTimeout(timer)); + + if (models === TIMED_OUT) { + throw new Error( + `Copilot did not list its models within ${timeoutMs}ms; treating it as unavailable.`, + ); + } + + return (models || []).map((m) => ({ + id: m.id || m.family || m.name, + name: m.name || m.family || m.id, + family: m.family || "", + })); +} + +async function sendOllamaPrompt(prompt, opts = {}) { + const settings = getAiSettings(); + const baseUrl = String(settings.ollamaBaseUrl || "").replace(/\/$/, ""); + const url = `${baseUrl}/api/generate`; + + const body = { + model: settings.ollamaModel, + prompt, + stream: false, + options: {}, + }; + + if (typeof opts.temperature === "number") { + body.options.temperature = opts.temperature; + } + + const json = await requestJson(url, body, { label: "Ollama" }); + if (!json || typeof json.response !== "string") { + throw new Error("Ollama did not return a text response."); + } + + return json.response; +} + +function normalizeMessage(message) { + if (!message) return null; + + if (typeof message === "string") { + return { role: "user", content: message }; + } + + const role = + message.role === "assistant" || message.role === "system" + ? message.role + : "user"; + const content = String(message.content ?? ""); + return { role, content }; +} + +function buildOllamaPromptFromMessages(messages) { + return messages + .map((m) => { + if (m.role === "assistant") return `ASSISTANT:\n${m.content}`; + if (m.role === "system") return `SYSTEM:\n${m.content}`; + return `USER:\n${m.content}`; + }) + .join("\n\n"); +} // Helper to get model safely async function selectModel() { @@ -8,11 +206,23 @@ async function selectModel() { // 2. Safety check if (!models || models.length === 0) { throw new Error( - "No Copilot models available. Please check your GitHub Copilot Chat extension." + "No Copilot models available. Please check your GitHub Copilot Chat extension.", ); } - // 3. Prefer GPT-4, fallback to default + // 3. Prefer the model the user explicitly chose (see aiProviderSetup.js) + const settings = getAiSettings(); + if (settings.copilotModel) { + const persisted = models.find( + (m) => + m.id === settings.copilotModel || m.family === settings.copilotModel, + ); + if (persisted) { + return persisted; + } + } + + // 4. Prefer GPT-4, fallback to default let selected = models.find((m) => m.family && m.family.includes("gpt-4")); if (!selected) { selected = models[0]; @@ -20,19 +230,60 @@ async function selectModel() { return selected; } +async function sendCopilotMessages(messages, opts = {}) { + const model = await selectModel(); + const lmMessages = messages.map((m) => { + if (m.role === "assistant") { + return vscode.LanguageModelChatMessage.Assistant(m.content); + } + return vscode.LanguageModelChatMessage.User(m.content); + }); + + const chatReq = await model.sendRequest( + lmMessages, + { temperature: opts.temperature }, + opts.cancellationToken, + ); + + let text = ""; + for await (const fragment of chatReq.text) { + text += fragment; + } + return text; +} + +/** + * The single place that decides which backend answers a prompt. Every feature-facing + * helper below funnels through here, so adding a backend is one new branch instead of + * one per call site. + */ +async function dispatchMessages(rawMessages, opts = {}) { + const messages = (rawMessages || []).map(normalizeMessage).filter(Boolean); + if (messages.length === 0) { + throw new Error("No AI messages were provided."); + } + + const settings = await resolveAiSettings(); + + if (settings.provider === "ollama") { + return sendOllamaPrompt(buildOllamaPromptFromMessages(messages), opts); + } + + if (settings.provider === "api") { + return sendApiChat(messages, settings, opts); + } + + return sendCopilotMessages(messages, opts); +} + +async function requestTextFromMessages(rawMessages, opts = {}) { + return dispatchMessages(rawMessages, opts); +} + async function analyzeAI(code, instructionPrompt) { try { - const model = await selectModel(); const combinedPrompt = `${instructionPrompt}\n\nCode to analyze:\n${code}`; - const messages = [vscode.LanguageModelChatMessage.User(combinedPrompt)]; - - const chatRequest = await model.sendRequest(messages, {}); - - let results = ""; - for await (const fragment of chatRequest.text) { - results += fragment; - } - return results; + return await dispatchMessages([{ role: "user", content: combinedPrompt }]); } catch (err) { // Handle off-topic refusals cleanly if (err.message && err.message.includes("off_topic")) { @@ -45,21 +296,19 @@ async function analyzeAI(code, instructionPrompt) { async function classifyVoiceIntent(transcript, commands, opts = {}) { try { const temperature = opts.temperature ?? 0.0; - const model = await selectModel(); // System prompt engineered as User message const systemInstruction = 'Output only JSON like {"command": ""}. Reply ONLY with strict minified JSON.'; const combinedPrompt = `SYSTEM:\n${systemInstruction}\n\nUSER DATA:\n${JSON.stringify( - { transcript, commands: commands.map((c) => ({ id: c.id })) } + { transcript, commands: commands.map((c) => ({ id: c.id })) }, )}`; - const messages = [vscode.LanguageModelChatMessage.User(combinedPrompt)]; - const chatReq = await model.sendRequest(messages, { temperature }); - - let text = ""; - for await (const frag of chatReq.text) text += frag; + const text = await dispatchMessages( + [{ role: "user", content: combinedPrompt }], + { temperature }, + ); const match = text.match(/\{[\s\S]*\}/); const candidate = match ? match[0] : text; @@ -74,13 +323,16 @@ async function classifyVoiceIntent(transcript, commands, opts = {}) { } } -async function generateCodeFromVoice(transcript, languageId, indentation = "", contextCode = "") { +async function generateCodeFromVoice( + transcript, + languageId, + indentation = "", + contextCode = "", +) { try { - const model = await selectModel(); - - let systemPrompt = `You are an expert coding assistant. + let systemPrompt = `You are an expert coding assistant. Your task is to convert the user's spoken natural language request into valid ${languageId} code. - + STRICT RULES: 1. Return ONLY the code. No markdown backticks, no explanations, no conversational text. 2. **Indentation**: The code MUST be inserted at indentation level: "${indentation}". Ensure all generated lines are strictly indented relative to this baseline. @@ -89,7 +341,7 @@ async function generateCodeFromVoice(transcript, languageId, indentation = "", c 5. **Variable Declaration**: Explicitly declare variables (e.g., 'let'/'const' in JS; proper types in C++/Java). 6. **Standards**: Follow standard coding conventions for ${languageId}. Use meaningful variable names. 7. **Python Specifics**: Use standard 4-space indentation. Do NOT use triple quotes for the body unless asked. - + If the request is unclear, just do your best to write the exact minimal code requested.`; if (contextCode) { @@ -101,17 +353,13 @@ async function generateCodeFromVoice(transcript, languageId, indentation = "", c \`\`\``; } - const messages = [ - vscode.LanguageModelChatMessage.User(systemPrompt), - vscode.LanguageModelChatMessage.User(transcript) - ]; - - const chatReq = await model.sendRequest(messages, { temperature: 0.1 }); - - let code = ""; - for await (const fragment of chatReq.text) { - code += fragment; - } + const code = await dispatchMessages( + [ + { role: "user", content: systemPrompt }, + { role: "user", content: transcript }, + ], + { temperature: 0.1 }, + ); // Cleanup any leaked markdown formatting return code @@ -119,15 +367,28 @@ async function generateCodeFromVoice(transcript, languageId, indentation = "", c .replace(/```$/, "") .trim(); } catch (err) { - if (err.name === 'LanguageModelError' || err instanceof vscode.LanguageModelError) { + const settings = getAiSettings(); + if ( + settings.provider === "copilot" && + (err.name === "LanguageModelError" || + err instanceof vscode.LanguageModelError) + ) { throw new Error(`Copilot LM Error: ${err.message}`); } - throw new Error(`Copilot Error: ${err.message}`); + throw new Error(`AI Error: ${err.message}`); } } module.exports = { + getAiSettings, + resolveAiSettings, + requestTextFromMessages, analyzeAI, classifyVoiceIntent, generateCodeFromVoice, + selectModel, + listOllamaModels, + listCopilotModels, + listApiModels, + pullOllamaModel, }; diff --git a/Core/program_settings/program_settings/ExternalIntentRouter.js b/Core/program_settings/program_settings/ExternalIntentRouter.js index 2a1c41d..ebeb815 100644 --- a/Core/program_settings/program_settings/ExternalIntentRouter.js +++ b/Core/program_settings/program_settings/ExternalIntentRouter.js @@ -2,23 +2,195 @@ const fs = require("fs"); const path = require("path"); const vscode = require("vscode"); -const commandsPath = path.join(__dirname, "external_commands.json"); +const REGISTRY_FILENAME = "external_commands.json"; + +// Absolute path to the generated registry, set by initExternalCommandRegistry. +// +// This used to live next to this file, inside the extension's install directory. That +// is the wrong home for generated per-user data: VS Code replaces the install directory +// wholesale on every extension update (so the file was silently discarded), and the +// directory is not guaranteed writable on managed installs. globalStorageUri is the +// location VS Code sets aside for exactly this β€” per-user, persistent across updates, +// and cleaned up if the extension is uninstalled. +let commandsPath = null; let cachedCommands = null; let watcher = null; +function disposeWatcher() { + if (!watcher) return; + try { + watcher.close(); + } catch { + // A watcher that is already gone is not worth reporting. + } + watcher = null; +} + +/** + * Points the registry at this extension's global storage and ties the file watcher's + * lifetime to the extension's. Call once, from activate(), before building the registry. + * + * The watcher was previously never closed, so every extension reload leaked an OS file + * handle for the lifetime of the window. + */ +function initExternalCommandRegistry(context) { + commandsPath = path.join(context.globalStorageUri.fsPath, REGISTRY_FILENAME); + cachedCommands = null; + disposeWatcher(); + + context.subscriptions.push(new vscode.Disposable(disposeWatcher)); + + return commandsPath; +} + +function normalizeForMatching(text) { + return String(text || "") + .toLowerCase() + .replace(/[^a-z0-9\s]/g, " ") + .replace(/\s+/g, " ") + .trim(); +} + +function levenshteinDistance(a, b) { + if (a === b) return 0; + if (!a.length) return b.length; + if (!b.length) return a.length; + + const rows = a.length + 1; + const cols = b.length + 1; + const dp = Array.from({ length: rows }, () => Array(cols).fill(0)); + + for (let i = 0; i < rows; i++) dp[i][0] = i; + for (let j = 0; j < cols; j++) dp[0][j] = j; + + for (let i = 1; i < rows; i++) { + for (let j = 1; j < cols; j++) { + const cost = a[i - 1] === b[j - 1] ? 0 : 1; + dp[i][j] = Math.min( + dp[i - 1][j] + 1, + dp[i][j - 1] + 1, + dp[i - 1][j - 1] + cost, + ); + } + } + + return dp[a.length][b.length]; +} + +function normalizedEditSimilarity(a, b) { + const x = normalizeForMatching(a); + const y = normalizeForMatching(b); + if (!x || !y) return 0; + + const maxLen = Math.max(x.length, y.length); + if (maxLen === 0) return 1; + return 1 - levenshteinDistance(x, y) / maxLen; +} + +function tokenList(text) { + const normalized = normalizeForMatching(text); + return normalized ? normalized.split(" ").filter(Boolean) : []; +} + +function tokenSet(text) { + return new Set(tokenList(text)); +} + +function jaccardSimilarity(a, b) { + const setA = tokenSet(a); + const setB = tokenSet(b); + if (setA.size === 0 || setB.size === 0) return 0; + + let intersection = 0; + for (const token of setA) { + if (setB.has(token)) intersection += 1; + } + const union = setA.size + setB.size - intersection; + return union > 0 ? intersection / union : 0; +} + +function tokenAlignmentSimilarity(a, b) { + const left = tokenList(a); + const right = tokenList(b); + if (left.length === 0 || right.length === 0) return 0; + + let total = 0; + for (const target of right) { + let best = 0; + for (const observed of left) { + const sim = normalizedEditSimilarity(observed, target); + if (sim > best) best = sim; + } + total += best; + } + + return total / right.length; +} + +function scoreTranscriptToPhrase(transcript, phrase) { + const normalizedTranscript = normalizeForMatching(transcript); + const normalizedPhrase = normalizeForMatching(phrase); + if (!normalizedTranscript || !normalizedPhrase) return 0; + + if ( + normalizedTranscript.includes(normalizedPhrase) || + normalizedPhrase.includes(normalizedTranscript) + ) { + return 1; + } + + const tokenOrderAgnosticEdit = normalizedEditSimilarity( + tokenList(normalizedTranscript).sort().join(" "), + tokenList(normalizedPhrase).sort().join(" "), + ); + const tokenOverlap = jaccardSimilarity( + normalizedTranscript, + normalizedPhrase, + ); + const tokenEdit = tokenAlignmentSimilarity( + normalizedTranscript, + normalizedPhrase, + ); + + return Math.max( + tokenOverlap, + tokenOrderAgnosticEdit * 0.65 + tokenEdit * 0.35, + ); +} + +function getMinimumExternalScore(phrase) { + const tokenCount = tokenList(phrase).length; + if (tokenCount <= 1) return 0.85; + return 0.62; +} + function getCommands() { if (cachedCommands) return cachedCommands; + + // Before init, or before the first build has written the file: no external commands + // yet. Matching simply finds nothing, which is the correct answer, not an error. + if (!commandsPath) return []; + try { cachedCommands = JSON.parse(fs.readFileSync(commandsPath, "utf-8")); + if (!watcher) { - watcher = fs.watch(commandsPath, () => { + watcher = fs.watch(commandsPath, (eventType) => { cachedCommands = null; + // A rename (which is how the file is replaced on rebuild) leaves the watch + // pointing at an inode that no longer backs the path. Drop it so the next read + // re-establishes a watch on the new file. + if (eventType === "rename") disposeWatcher(); }); + // Watch failures β€” descriptor limits, the file disappearing β€” must never take + // down the extension host. Losing the watch only costs a stale cache. + watcher.on("error", disposeWatcher); } } catch { cachedCommands = []; } + return cachedCommands; } @@ -59,6 +231,12 @@ function generateKeywords(commandId) { * and writes them to external_commands.json. Skips echocode internal commands. */ async function buildExternalCommandRegistry() { + if (!commandsPath) { + throw new Error( + "External command registry used before initExternalCommandRegistry(context).", + ); + } + const allCommandIds = await vscode.commands.getCommands(true); const commands = allCommandIds @@ -69,7 +247,24 @@ async function buildExternalCommandRegistry() { keywords: generateKeywords(id), })); - await fs.promises.writeFile(commandsPath, JSON.stringify(commands, null, 2)); + // VS Code does not create globalStorageUri for us; the first write has to. + await fs.promises.mkdir(path.dirname(commandsPath), { recursive: true }); + + // Written to a sibling temp file and renamed into place. rename is atomic within a + // filesystem, so a crash or a reload mid-write can never leave a half-flushed 600KB + // file that fails to parse on next activation β€” readers see either the old registry + // or the complete new one. + const tempPath = `${commandsPath}.${process.pid}.tmp`; + try { + await fs.promises.writeFile(tempPath, JSON.stringify(commands, null, 2)); + await fs.promises.rename(tempPath, commandsPath); + } catch (err) { + await fs.promises.rm(tempPath, { force: true }); + throw err; + } + + // The rename swapped the file out from under any existing watch. + disposeWatcher(); cachedCommands = null; // force reload on next match } @@ -78,16 +273,30 @@ async function buildExternalCommandRegistry() { * @param {string} transcript - Lowercased, trimmed transcript */ function matchExternalCommand(transcript) { + const normalizedTranscript = normalizeForMatching(transcript); + if (!normalizedTranscript) return null; + + let best = null; + for (const cmd of getCommands()) { - if ( - cmd.keywords && - cmd.keywords.some((k) => transcript.includes(k.toLowerCase())) - ) { - return cmd; + const phrases = Array.isArray(cmd.keywords) ? cmd.keywords : []; + for (const phrase of phrases) { + const score = scoreTranscriptToPhrase(normalizedTranscript, phrase); + if (score < getMinimumExternalScore(phrase)) { + continue; + } + + if (!best || score > best.score) { + best = { cmd, score }; + } } } - return null; -} + return best ? best.cmd : null; +} -module.exports = { matchExternalCommand, buildExternalCommandRegistry }; +module.exports = { + matchExternalCommand, + buildExternalCommandRegistry, + initExternalCommandRegistry, +}; diff --git a/Core/program_settings/program_settings/aiProviderSetup.js b/Core/program_settings/program_settings/aiProviderSetup.js new file mode 100644 index 0000000..21445db --- /dev/null +++ b/Core/program_settings/program_settings/aiProviderSetup.js @@ -0,0 +1,868 @@ +const vscode = require("vscode"); +const { + listOllamaModels, + listCopilotModels, + listApiModels, + pullOllamaModel, +} = require("./AIrequest"); +const { API_PRESETS, getPreset, normalizeBaseUrl } = require("./apiProviders"); +const { getApiKey, setApiKey, isAvailable } = require("./secretStore"); +const { scanSystemSpecs, recommendModel, describeSpecs } = require("./systemScan"); + +const FIRST_RUN_KEY = "echocode.aiProviderConfigured"; + +// Sentinel returned by a branch when the user backed out to the API-or-Local question +// instead of finishing or cancelling outright. +const BACK = "back"; + +// Guards the setup loop against a user bouncing between branches forever. +const MAX_BRANCH_HOPS = 12; + +/** + * Speaks a message and returns immediately β€” deliberately not awaited. + * + * speakMessage resolves only once the sentence has finished being read aloud (~5s for a + * short line). Every call site here speaks just before opening a quick pick, so awaiting + * it held the UI closed for the full utterance on every step of setup. + * + * Interleaving is speechHandler's problem: a new message interrupts the one in progress + * and the two never overlap, so callers here can just say what they need to say. + */ +function speak(message) { + try { + const { speakMessage } = require("../speech_settings/speechHandler"); + Promise.resolve(speakMessage(message)).catch(() => {}); + } catch (_) { + // TTS is best-effort here; the quick pick UI is still fully usable without it. + } +} + +async function saveGlobal(config, key, value) { + await config.update(key, value, vscode.ConfigurationTarget.Global); +} + +/* ------------------------------------------------------------------ * + * Spoken quick picks + * ------------------------------------------------------------------ */ + +/** + * Quick pick labels carry codicons (`$(star-full) qwen2.5-coder:7b`). TTS reads that + * markup out literally β€” "dollar sign paren star full" β€” so strip it before speaking. + */ +function spokenText(value) { + return String(value ?? "") + .replace(/\$\([^)]*\)/g, " ") + .replace(/[…]/g, "") + .replace(/\s+/g, " ") + .trim(); +} + +/** + * The wizard's list cursor: the items, plus where the user currently is in them. + * + * EchoCode drives its own TTS rather than assuming a screen reader is running, so + * nothing announces the highlighted row unless we do it. Keeping the index here (rather + * than reading it off the quick pick each time) means the position can be spoken as + * "3 of 7", and means the announcement logic is testable without VS Code. + */ +class AnnouncedList { + constructor(items, { intro = "" } = {}) { + this.items = items; + this.intro = intro; + this.index = -1; + } + + get current() { + return this.index >= 0 ? this.items[this.index] : undefined; + } + + /** + * Moves the cursor to `item` and returns what should be said, or null when nothing + * changed (the same row re-reported, or a filter that matched nothing). + * + * The intro is spoken as its own queued message rather than being welded onto this + * one: the queue guarantees it is heard in full before the first row, and keeping + * them separate means a user who arrows immediately replaces only the pending row. + */ + moveTo(item) { + const next = this.items.indexOf(item); + if (next === -1 || next === this.index) return null; + + this.index = next; + return this.describe(); + } + + describe() { + const item = this.current; + if (!item) return this.intro || null; + + const parts = []; + parts.push(spokenText(item.label)); + if (item.description) parts.push(spokenText(item.description)); + // Position is against the full list. VS Code exposes no public view of the + // type-to-filter result, so while filtering this counts rows the user can't see. + parts.push(`${this.index + 1} of ${this.items.length}`); + + return `${parts.filter(Boolean).join(". ")}.`; + } +} + +/** + * showQuickPick with spoken navigation: says what the list is for, then announces each + * row as the user arrows onto it. + * + * showQuickPick itself resolves only once something is chosen and never reports the + * highlighted row, so this uses createQuickPick, whose onDidChangeActive is the only + * hook that fires per keystroke. Hosts without it (and the test harness, which stubs + * showQuickPick) fall back to the one-shot picker with just the intro spoken. + */ +function showAnnouncedQuickPick(items, options = {}, { intro = "" } = {}) { + const list = new AnnouncedList(items, { intro }); + + if (typeof vscode.window.createQuickPick !== "function") { + if (intro) speak(intro); + return vscode.window.showQuickPick(items, options); + } + + return new Promise((resolve) => { + const quickPick = vscode.window.createQuickPick(); + quickPick.placeholder = options.placeHolder; + quickPick.ignoreFocusOut = options.ignoreFocusOut ?? true; + quickPick.matchOnDescription = options.matchOnDescription ?? false; + quickPick.matchOnDetail = options.matchOnDetail ?? false; + + let settled = false; + const finish = (value) => { + if (settled) return; + settled = true; + quickPick.dispose(); + resolve(value); + }; + + // Spoken before the rows so it leads. A user who arrows immediately cuts it off, + // which is the right outcome β€” they already know what the list is for. + if (intro) speak(intro); + + // Subscribe before assigning items: setting them makes the first row active, and + // that is the announcement the user needs to hear on open. + quickPick.onDidChangeActive((active) => { + const line = list.moveTo(active && active[0]); + // Each row interrupts the last, so holding the arrow key down narrates the row + // landed on rather than every row travelled through. + if (line) speak(line); + }); + quickPick.onDidAccept(() => finish(quickPick.selectedItems[0])); + quickPick.onDidHide(() => finish(undefined)); + + quickPick.items = items; + quickPick.show(); + }); +} + +/** + * Probes every provider for what's actually available right now. + * Copilot models come from vscode.lm; Ollama models come from a live /api/tags call; + * the hosted-API entry reports whatever endpoint the user has already configured. + * Any probe failing (extension missing, server not running) just yields an empty list. + * + * The three probes hit unrelated backends, so they run concurrently: the caller waits + * for the slowest one rather than the sum of all three. Nothing is rendered while this + * runs, so every second here is a second of apparently-frozen UI. + */ +async function detectProviders(outputChannel) { + const config = vscode.workspace.getConfiguration("echocode"); + const apiProviderId = config.get("apiProvider", ""); + + const copilotProbe = (async () => { + try { + const models = await listCopilotModels({ timeoutMs: 3000 }); + return { available: models.length > 0, models }; + } catch (err) { + outputChannel?.appendLine( + `[AI Setup] Copilot model detection failed: ${err.message}`, + ); + return { available: false, models: [] }; + } + })(); + + const ollamaProbe = (async () => { + try { + const models = await listOllamaModels({ timeoutMs: 2000 }); + return { available: models.length > 0, models }; + } catch (err) { + outputChannel?.appendLine( + `[AI Setup] Ollama model detection failed: ${err.message}`, + ); + return { available: false, models: [] }; + } + })(); + + const apiProbe = (async () => { + if (!apiProviderId) { + return { available: false, models: [], configured: false }; + } + try { + const preset = getPreset(apiProviderId); + const models = await listApiModels( + { + baseUrl: config.get("apiBaseUrl", "") || preset?.baseUrl || "", + apiKey: await getApiKey(apiProviderId), + wire: preset?.wire || "openai", + }, + { timeoutMs: 5000 }, + ); + return { available: models.length > 0, models, configured: true }; + } catch (err) { + outputChannel?.appendLine( + `[AI Setup] API provider check failed: ${err.message}`, + ); + return { available: false, models: [], configured: true }; + } + })(); + + const [copilot, ollama, api] = await Promise.all([ + copilotProbe, + ollamaProbe, + apiProbe, + ]); + + return { copilot, ollama, api }; +} + +/* ------------------------------------------------------------------ * + * Hosted API branch + * ------------------------------------------------------------------ */ + +/** + * Asks for the endpoint and key, then proves the endpoint answers before saving + * anything. Verifying up front means the first real EchoCode request can't be the + * thing that discovers a typo'd URL or a rejected key. + */ +async function configureApiProvider(context, outputChannel) { + const presetPick = await showAnnouncedQuickPick( + API_PRESETS.map((p) => ({ + label: p.label, + detail: p.detail || p.baseUrl, + value: p.id, + })), + { + placeHolder: "Which model API do you want to use?", + ignoreFocusOut: true, + }, + { intro: "Select an API provider" }, + ); + + if (!presetPick) return BACK; + + const preset = getPreset(presetPick.value); + + const baseUrl = await vscode.window.showInputBox({ + prompt: "API base URL", + value: preset.baseUrl, + placeHolder: "https://api.example.com/v1", + ignoreFocusOut: true, + validateInput: (value) => { + if (!value || !value.trim()) return "A base URL is required."; + try { + new URL(value.trim()); + return null; + } catch { + return "That is not a valid URL."; + } + }, + }); + if (!baseUrl) return false; + + if (!isAvailable()) { + vscode.window.showErrorMessage( + "EchoCode cannot store an API key because VS Code secret storage is unavailable.", + ); + return false; + } + + const existingKey = await getApiKey(preset.id); + const apiKey = await vscode.window.showInputBox({ + prompt: existingKey + ? `API key for ${preset.label} (leave blank to keep the saved key)` + : `API key for ${preset.label}`, + placeHolder: preset.keyHint, + password: true, + ignoreFocusOut: true, + }); + // An explicit Escape cancels; an empty box means "reuse what's stored". + if (apiKey === undefined) return false; + + const effectiveKey = apiKey.trim() || existingKey; + + // --- Verify the API can be reached (and populate the model list) --- + let models = []; + try { + models = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: `EchoCode: contacting ${preset.label}…`, + cancellable: false, + }, + () => + listApiModels( + { + baseUrl: normalizeBaseUrl(baseUrl), + apiKey: effectiveKey, + wire: preset.wire, + }, + { timeoutMs: 15000 }, + ), + ); + } catch (err) { + outputChannel?.appendLine(`[AI Setup] API verification failed: ${err.message}`); + speak(`Could not reach ${preset.label}.`); + + const retry = await vscode.window.showErrorMessage( + `EchoCode could not reach ${preset.label}: ${err.message}`, + "Try Again", + "Choose a Different Provider", + ); + if (retry === "Try Again") return configureApiProvider(context, outputChannel); + return BACK; + } + + // --- User selects the model --- + const MANUAL = "__manual__"; + const items = [ + ...models.map((m) => ({ + label: m.id, + description: m.owner || undefined, + value: m.id, + })), + { + label: "$(edit) Enter a model name manually", + value: MANUAL, + }, + ]; + + const modelPick = await showAnnouncedQuickPick( + items, + { + placeHolder: `Choose a ${preset.label} model (${models.length} available)`, + matchOnDescription: true, + ignoreFocusOut: true, + }, + { intro: `Select a model for ${preset.label}` }, + ); + if (!modelPick) return false; + + let modelId = modelPick.value; + if (modelId === MANUAL) { + const manual = await vscode.window.showInputBox({ + prompt: `Model name for ${preset.label}`, + ignoreFocusOut: true, + }); + if (!manual || !manual.trim()) return false; + modelId = manual.trim(); + } + + // --- Persist: EchoCode routes models there --- + const config = vscode.workspace.getConfiguration("echocode"); + if (apiKey.trim()) { + await setApiKey(preset.id, apiKey.trim()); + } + await saveGlobal(config, "aiProvider", "api"); + await saveGlobal(config, "useLocalOllama", false); + await saveGlobal(config, "apiProvider", preset.id); + await saveGlobal(config, "apiBaseUrl", normalizeBaseUrl(baseUrl)); + await saveGlobal(config, "apiModel", modelId); + + vscode.window.showInformationMessage( + `EchoCode: Using ${preset.label} (${modelId}).`, + ); + speak(`EchoCode will use ${preset.label} with ${modelId}.`); + await context.globalState.update(FIRST_RUN_KEY, true); + return true; +} + +async function configureCopilot(context, detected) { + if (!detected.copilot.models.length) { + vscode.window.showWarningMessage( + "No Copilot models were detected. Make sure GitHub Copilot Chat is installed and you're signed in.", + ); + } + + let modelValue = ""; + let modelLabel = "the default model"; + if (detected.copilot.models.length) { + const modelPick = await showAnnouncedQuickPick( + detected.copilot.models.map((m) => ({ + label: m.name, + description: m.family, + value: m.id, + })), + { placeHolder: "Choose a Copilot model", ignoreFocusOut: true }, + { intro: "Select a model for GitHub Copilot" }, + ); + if (!modelPick) return false; + modelValue = modelPick.value; + modelLabel = modelPick.label; + } + + const config = vscode.workspace.getConfiguration("echocode"); + await saveGlobal(config, "aiProvider", "copilot"); + await saveGlobal(config, "useLocalOllama", false); + await saveGlobal(config, "copilotModel", modelValue); + + vscode.window.showInformationMessage( + `EchoCode: Using GitHub Copilot (${modelLabel}).`, + ); + speak(`EchoCode will use GitHub Copilot with ${modelLabel}.`); + await context.globalState.update(FIRST_RUN_KEY, true); + return true; +} + +/** + * The API side of the fork: Copilot rides on VS Code's own language model API, so it + * needs no URL or key; anything else is a third-party endpoint we have to verify. + */ +async function runApiBranch(context, outputChannel, detected) { + const choice = await showAnnouncedQuickPick( + [ + { + label: "$(github) GitHub Copilot", + value: "copilot", + detail: detected.copilot.available + ? `${detected.copilot.models.length} model(s) detected β€” no API key needed` + : "Not detected β€” install/sign in to GitHub Copilot Chat", + }, + { + label: "$(cloud) Another model API", + value: "api", + detail: "OpenAI, OpenRouter, Groq, Anthropic, or a custom endpoint", + }, + { label: "$(arrow-left) Back", value: BACK }, + ], + { placeHolder: "What model do you want?", ignoreFocusOut: true }, + { intro: "Select which API to use" }, + ); + + if (!choice) return false; + if (choice.value === BACK) return BACK; + if (choice.value === "copilot") return configureCopilot(context, detected); + return configureApiProvider(context, outputChannel); +} + +/* ------------------------------------------------------------------ * + * Local Ollama branch + * ------------------------------------------------------------------ */ + +/** + * `spokenPrefix` lets a caller fold its own announcement into this one. Speech no + * longer blocks (see speak()), so a caller that spoke first would simply be cut off + * mid-sentence by the confirmation below; one utterance carries both facts instead. + */ +async function saveOllamaChoice(context, modelName, spokenPrefix = "") { + const config = vscode.workspace.getConfiguration("echocode"); + await saveGlobal(config, "aiProvider", "ollama"); + await saveGlobal(config, "useLocalOllama", true); + await saveGlobal(config, "ollamaModel", modelName); + + vscode.window.showInformationMessage( + `EchoCode: Using local Ollama model "${modelName}".`, + ); + speak( + `${spokenPrefix}EchoCode will use the local Ollama model ${modelName}.`, + ); + await context.globalState.update(FIRST_RUN_KEY, true); + return true; +} + +/** + * The "Ollama Model Install" subgraph: read the machine, recommend a model that fits + * it, confirm, then pull. Sizing matters here because a model that overruns memory + * doesn't fail loudly β€” it swaps, and EchoCode just feels broken. + */ +async function installRecommendedModel(context, outputChannel) { + const specs = await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: "EchoCode: checking what your machine can run…", + cancellable: false, + }, + () => scanSystemSpecs(), + ); + + const { recommended, alternatives, underpowered, budgetGb } = + recommendModel(specs); + + outputChannel?.appendLine( + `[AI Setup] System scan: ${describeSpecs(specs)} (usable model budget ~${budgetGb.toFixed(1)} GB)`, + ); + // The recommendation rides in on the list's own intro below rather than being spoken + // here β€” the list announces itself the moment it opens and would cut this off. Kept + // short either way: reading the full hardware summary aloud stalls setup for ten-plus + // seconds, so the details stay in the output channel above. + if (underpowered) { + vscode.window.showWarningMessage( + `EchoCode: this machine has about ${budgetGb.toFixed(1)} GB to spare, which is below what any recommended model wants. ${recommended.name} will run but may be slow.`, + ); + } + + const CUSTOM = "__custom__"; + const modelPick = await showAnnouncedQuickPick( + [ + { + label: `$(star-full) ${recommended.name}`, + description: "Recommended for this machine", + detail: `${recommended.blurb} Needs roughly ${recommended.requiredGb} GB.`, + value: recommended.name, + }, + ...alternatives.map((m) => ({ + label: m.name, + description: + m.requiredGb > budgetGb ? "May not fit this machine" : "Also fits", + detail: `${m.blurb} Needs roughly ${m.requiredGb} GB.`, + value: m.name, + })), + { + label: "$(edit) Enter a different model name", + value: CUSTOM, + }, + ], + { + placeHolder: `Install for you β€” ${describeSpecs(specs)}`, + ignoreFocusOut: true, + matchOnDetail: true, + }, + { + intro: `Select a model to install. Recommending ${recommended.name} for this machine`, + }, + ); + + if (!modelPick) return BACK; + + let modelName = modelPick.value; + if (modelName === CUSTOM) { + const manual = await vscode.window.showInputBox({ + prompt: "Ollama model name to pull (e.g. llama3.2, qwen2.5-coder:7b)", + ignoreFocusOut: true, + }); + if (!manual || !manual.trim()) return BACK; + modelName = manual.trim(); + } + + const confirm = await vscode.window.showInformationMessage( + `Download "${modelName}" now? This can take several minutes and uses several gigabytes of disk.`, + { modal: true }, + "Install", + ); + if (confirm !== "Install") return BACK; + + try { + await vscode.window.withProgress( + { + location: vscode.ProgressLocation.Notification, + title: `EchoCode: downloading ${modelName}`, + cancellable: true, + }, + async (progress, token) => { + let lastPercent = 0; + await pullOllamaModel(modelName, { + cancellationToken: token, + onProgress: ({ status, completed, total }) => { + if (total > 0) { + const percent = Math.floor((completed / total) * 100); + // report() takes an increment, so send only the delta since last tick. + const increment = Math.max(0, percent - lastPercent); + lastPercent = percent; + progress.report({ message: `${status} β€” ${percent}%`, increment }); + } else { + progress.report({ message: status }); + } + }, + }); + }, + ); + } catch (err) { + outputChannel?.appendLine(`[AI Setup] Model pull failed: ${err.message}`); + vscode.window.showErrorMessage( + `EchoCode: could not download "${modelName}": ${err.message}`, + ); + speak(`The download failed. ${err.message}`); + return BACK; + } + + return saveOllamaChoice(context, modelName, `${modelName} is installed. `); +} + +/** + * The local side of the fork. Scanning the configured Ollama address is the first + * gate: if nothing answers there we warn and hand the user back to the top-level + * choice rather than dead-ending them in a branch that cannot work. + */ +async function runLocalBranch(context, outputChannel) { + const config = vscode.workspace.getConfiguration("echocode"); + let baseUrl = config.get("ollamaBaseUrl", "http://127.0.0.1:11434"); + + for (let attempt = 0; attempt < 3; attempt += 1) { + let models = null; + + try { + models = await listOllamaModels({ baseUrl, timeoutMs: 3000 }); + } catch (err) { + outputChannel?.appendLine( + `[AI Setup] Ollama not reachable at ${baseUrl}: ${err.message}`, + ); + } + + // --- Ollama itself is not answering --- + if (models === null) { + speak("Ollama was not detected."); + const choice = await vscode.window.showWarningMessage( + `EchoCode: Ollama is not responding at ${baseUrl}. Make sure Ollama is installed and running, then try again.`, + "Retry", + "Change Address", + "Use an API Instead", + ); + + if (choice === "Retry") continue; + if (choice === "Change Address") { + const entered = await vscode.window.showInputBox({ + prompt: "Ollama server address", + value: baseUrl, + ignoreFocusOut: true, + }); + if (!entered) return false; + baseUrl = normalizeBaseUrl(entered); + await saveGlobal(config, "ollamaBaseUrl", baseUrl); + continue; + } + if (choice === "Use an API Instead") return BACK; + return false; + } + + // --- Reachable, and models are installed --- + if (models.length) { + const INSTALL = "__install__"; + const pick = await showAnnouncedQuickPick( + [ + ...models.map((name) => ({ label: name, value: name })), + { + label: "$(cloud-download) Install another model…", + value: INSTALL, + }, + ], + { + placeHolder: "Choose an installed Ollama model", + ignoreFocusOut: true, + }, + { intro: "Select a model. These are the Ollama models installed on this machine" }, + ); + if (!pick) return false; + if (pick.value === INSTALL) { + return installRecommendedModel(context, outputChannel); + } + return saveOllamaChoice(context, pick.value); + } + + // --- Reachable, but nothing is installed --- + speak("Ollama is running, but no models are installed."); + const careToInstall = await vscode.window.showWarningMessage( + "EchoCode: Ollama is running but has no models installed. Would you like EchoCode to recommend and install one?", + "Recommend a Model", + "Enter Model Name", + "Use an API Instead", + ); + + if (careToInstall === "Recommend a Model") { + const result = await installRecommendedModel(context, outputChannel); + if (result === BACK) continue; + return result; + } + + if (careToInstall === "Enter Model Name") { + const manual = await vscode.window.showInputBox({ + prompt: "Enter the Ollama model name (e.g. llama3.2)", + placeHolder: "llama3.2", + ignoreFocusOut: true, + }); + if (!manual || !manual.trim()) continue; + return saveOllamaChoice(context, manual.trim()); + } + + if (careToInstall === "Use an API Instead") return BACK; + return false; + } + + return false; +} + +/* ------------------------------------------------------------------ * + * Entry points + * ------------------------------------------------------------------ */ + +/** + * The top-level "API or Local" fork. Each branch can hand control back here, which is + * what makes "Ollama isn't installed" recoverable without restarting setup. + * Persists the choice to EchoCode's global settings; returns true if one was saved. + */ +async function pickProviderAndModel(context, outputChannel, spokenPrefix = "") { + const detected = await detectProviders(outputChannel); + + for (let hop = 0; hop < MAX_BRANCH_HOPS; hop += 1) { + const providerPick = await showAnnouncedQuickPick( + [ + { + label: "$(cloud) API model", + value: "api", + description: "A hosted API such as GitHub Copilot or OpenAI", + detail: detected.copilot.available + ? `GitHub Copilot (${detected.copilot.models.length} model(s)) or another hosted API` + : "GitHub Copilot, OpenAI, OpenRouter, Groq, Anthropic, or a custom endpoint", + }, + { + label: "$(server-environment) Local model (Ollama)", + value: "local", + description: "Runs on this machine through Ollama", + detail: detected.ollama.available + ? `${detected.ollama.models.length} model(s) installed β€” runs entirely on this machine` + : "Runs entirely on this machine β€” EchoCode will help you set it up", + }, + ], + { + placeHolder: "Choose your EchoCode AI provider", + ignoreFocusOut: true, + }, + { + // States the actual fork: "choose your provider" alone does not tell a user + // who cannot see the list what the two choices are. The keyboard hint is + // first-pass only β€” a branch handing control back here (Ollama missing, say) + // re-enters the loop, and repeating the full preamble every hop is noise to + // someone who has already heard it. + intro: + hop === 0 + ? `${spokenPrefix}Choose your EchoCode AI provider. ` + + "You are choosing between an API model and a local Ollama model. " + + "Use the arrow keys to move through the list, then press Enter to select" + : "Back to choosing your AI provider. An API model, or a local Ollama model", + }, + ); + + if (!providerPick) return false; + + const result = + providerPick.value === "api" + ? await runApiBranch(context, outputChannel, detected) + : await runLocalBranch(context, outputChannel); + + if (result !== BACK) return result; + } + + return false; +} + +/** + * Re-probes the active provider and warns if the configured model has disappeared + * (Ollama model removed, Copilot model deprecated/renamed, API key revoked, etc). + * Safe to call on every activation; does nothing intrusive when everything matches. + */ +async function checkForProviderUpdates(context, outputChannel) { + const config = vscode.workspace.getConfiguration("echocode"); + const detected = await detectProviders(outputChannel); + + const explicitProvider = config.get("aiProvider", ""); + const provider = + explicitProvider || (config.get("useLocalOllama", false) ? "ollama" : "copilot"); + + if (provider === "ollama") { + const currentModel = config.get("ollamaModel", ""); + if (!detected.ollama.available) { + outputChannel?.appendLine( + "[AI Setup] Ollama is selected but not reachable at the configured URL.", + ); + } else if (currentModel && !detected.ollama.models.includes(currentModel)) { + outputChannel?.appendLine( + `[AI Setup] Configured Ollama model "${currentModel}" is no longer installed. Available: ${detected.ollama.models.join(", ") || "none"}`, + ); + const choice = await vscode.window.showWarningMessage( + `EchoCode: The configured Ollama model "${currentModel}" was not found. Choose a different installed model?`, + "Choose Model", + "Dismiss", + ); + if (choice === "Choose Model") { + await pickProviderAndModel(context, outputChannel); + } + } + } else if (provider === "api") { + const currentModel = config.get("apiModel", ""); + const presetId = config.get("apiProvider", ""); + const label = getPreset(presetId)?.label || presetId || "the API provider"; + + if (!detected.api.available) { + outputChannel?.appendLine( + `[AI Setup] ${label} is selected but did not respond to a model listing.`, + ); + } else if ( + currentModel && + !detected.api.models.some((m) => m.id === currentModel) + ) { + outputChannel?.appendLine( + `[AI Setup] Configured ${label} model "${currentModel}" is no longer offered.`, + ); + const choice = await vscode.window.showWarningMessage( + `EchoCode: ${label} no longer offers the model "${currentModel}". Choose a different one?`, + "Choose Model", + "Dismiss", + ); + if (choice === "Choose Model") { + await pickProviderAndModel(context, outputChannel); + } + } + } else { + const currentModel = config.get("copilotModel", ""); + if (!detected.copilot.available) { + outputChannel?.appendLine( + "[AI Setup] Copilot is selected but no models were detected.", + ); + } else if ( + currentModel && + !detected.copilot.models.some((m) => m.id === currentModel) + ) { + outputChannel?.appendLine( + `[AI Setup] Configured Copilot model "${currentModel}" is no longer available; EchoCode will fall back to a default model automatically.`, + ); + } + } + + return detected; +} + +/** + * Called once per activation. First launch ever: shows the provider/model picker. + * Every launch after that: silently rechecks availability (Ollama/Copilot/API model + * lineups all change often). + */ +async function initializeAIProviderOnStartup(context, outputChannel) { + const alreadyConfigured = context.globalState.get(FIRST_RUN_KEY, false); + + if (!alreadyConfigured) { + // Handed to the picker rather than spoken here: the list announces itself as soon + // as it opens, which would cut a separate welcome line off mid-sentence. + await pickProviderAndModel( + context, + outputChannel, + "Welcome to EchoCode. ", + ); + return; + } + + await checkForProviderUpdates(context, outputChannel); +} + +module.exports = { + detectProviders, + pickProviderAndModel, + checkForProviderUpdates, + initializeAIProviderOnStartup, + installRecommendedModel, + // Exported for tests: the cursor carries the announcement wording, and asserting on + // it needs no quick pick, no VS Code window and no audio device. + AnnouncedList, + spokenText, +}; diff --git a/Core/program_settings/program_settings/apiProviders.js b/Core/program_settings/program_settings/apiProviders.js new file mode 100644 index 0000000..42a458e --- /dev/null +++ b/Core/program_settings/program_settings/apiProviders.js @@ -0,0 +1,199 @@ +const { requestJson } = require("./httpClient"); + +/** + * Hosted API backends EchoCode can talk to directly, for users who want a cloud model + * without GitHub Copilot. Everything except Anthropic speaks the OpenAI wire format, + * so adding another vendor is usually a new row here and nothing else. + */ +const API_PRESETS = [ + { + id: "openai", + label: "OpenAI", + baseUrl: "https://api.openai.com/v1", + wire: "openai", + keyHint: "sk-…", + signupUrl: "https://platform.openai.com/api-keys", + }, + { + id: "openrouter", + label: "OpenRouter", + baseUrl: "https://openrouter.ai/api/v1", + wire: "openai", + keyHint: "sk-or-…", + signupUrl: "https://openrouter.ai/keys", + }, + { + id: "groq", + label: "Groq", + baseUrl: "https://api.groq.com/openai/v1", + wire: "openai", + keyHint: "gsk_…", + signupUrl: "https://console.groq.com/keys", + }, + { + id: "anthropic", + label: "Anthropic (Claude)", + baseUrl: "https://api.anthropic.com/v1", + wire: "anthropic", + keyHint: "sk-ant-…", + signupUrl: "https://console.anthropic.com/settings/keys", + }, + { + id: "custom", + label: "Custom endpoint", + baseUrl: "", + wire: "openai", + keyHint: "optional", + signupUrl: "", + detail: + "Any OpenAI-compatible server β€” LM Studio, vLLM, llama.cpp, Together, DeepSeek, a company gateway.", + }, +]; + +const ANTHROPIC_VERSION = "2023-06-01"; + +function getPreset(id) { + return API_PRESETS.find((p) => p.id === id) || null; +} + +function normalizeBaseUrl(baseUrl) { + return String(baseUrl || "").replace(/\/+$/, ""); +} + +/** + * Anthropic authenticates with x-api-key and requires a pinned API version; + * every other supported vendor uses a bearer token. + */ +function authHeaders(wire, apiKey) { + if (!apiKey) return {}; + if (wire === "anthropic") { + return { + "x-api-key": apiKey, + "anthropic-version": ANTHROPIC_VERSION, + }; + } + return { Authorization: `Bearer ${apiKey}` }; +} + +/** + * The diagram's "verify API can be reached" step. Both wire formats expose GET + * /models, so one call proves the base URL resolves, the key is accepted, and gives + * us the model list to populate the picker β€” three checks for one round trip. + */ +async function listApiModels({ baseUrl, apiKey, wire }, opts = {}) { + const url = `${normalizeBaseUrl(baseUrl)}/models`; + const json = await requestJson(url, null, { + method: "GET", + headers: authHeaders(wire, apiKey), + timeoutMs: opts.timeoutMs ?? 10000, + label: "API provider", + }); + + const rows = Array.isArray(json?.data) ? json.data : []; + return rows + .map((m) => ({ + id: m?.id || m?.name || "", + label: m?.display_name || m?.id || m?.name || "", + owner: m?.owned_by || "", + })) + .filter((m) => m.id); +} + +function buildOpenAiBody(messages, model, opts) { + const body = { + model, + messages: messages.map((m) => ({ role: m.role, content: m.content })), + }; + if (typeof opts.temperature === "number") body.temperature = opts.temperature; + if (typeof opts.maxTokens === "number") body.max_tokens = opts.maxTokens; + return body; +} + +/** + * Anthropic takes the system prompt as a top-level field rather than a message role, + * and requires an explicit max_tokens, so its body is built separately. + */ +function buildAnthropicBody(messages, model, opts) { + const system = messages + .filter((m) => m.role === "system") + .map((m) => m.content) + .join("\n\n"); + + const turns = messages + .filter((m) => m.role !== "system") + .map((m) => ({ role: m.role, content: m.content })); + + const body = { + model, + max_tokens: opts.maxTokens ?? 4096, + messages: turns.length + ? turns + : [{ role: "user", content: system || "Hello" }], + }; + if (system && turns.length) body.system = system; + if (typeof opts.temperature === "number") body.temperature = opts.temperature; + return body; +} + +function extractOpenAiText(json) { + const content = json?.choices?.[0]?.message?.content; + if (typeof content === "string") return content; + // Some gateways return the multi-part content array shape. + if (Array.isArray(content)) { + return content + .map((part) => (typeof part === "string" ? part : part?.text || "")) + .join(""); + } + return null; +} + +function extractAnthropicText(json) { + if (!Array.isArray(json?.content)) return null; + return json.content + .filter((block) => block?.type === "text") + .map((block) => block.text) + .join(""); +} + +/** + * Sends a chat completion to whichever hosted API the user configured and returns + * plain text. Callers never need to know which wire format is in play. + */ +async function sendApiChat(messages, settings, opts = {}) { + const { apiBaseUrl, apiModel, apiKey, apiWire } = settings; + + if (!apiModel) { + throw new Error( + "No API model is selected. Run 'EchoCode: Select AI Provider & Model'.", + ); + } + + const base = normalizeBaseUrl(apiBaseUrl); + const isAnthropic = apiWire === "anthropic"; + const url = `${base}${isAnthropic ? "/messages" : "/chat/completions"}`; + const body = isAnthropic + ? buildAnthropicBody(messages, apiModel, opts) + : buildOpenAiBody(messages, apiModel, opts); + + const json = await requestJson(url, body, { + headers: authHeaders(apiWire, apiKey), + timeoutMs: opts.timeoutMs ?? 120000, + label: "API provider", + }); + + const text = isAnthropic ? extractAnthropicText(json) : extractOpenAiText(json); + if (typeof text !== "string") { + throw new Error("The API provider did not return a text response."); + } + return text; +} + +module.exports = { + API_PRESETS, + ANTHROPIC_VERSION, + getPreset, + normalizeBaseUrl, + authHeaders, + listApiModels, + sendApiChat, +}; diff --git a/Core/program_settings/program_settings/external_commands.json b/Core/program_settings/program_settings/external_commands.json deleted file mode 100644 index 901cef0..0000000 --- a/Core/program_settings/program_settings/external_commands.json +++ /dev/null @@ -1,29816 +0,0 @@ -[ - { - "id": "noop", - "title": "noop", - "keywords": [ - "noop" - ] - }, - { - "id": "undo", - "title": "undo", - "keywords": [ - "undo" - ] - }, - { - "id": "default:undo", - "title": "default:undo", - "keywords": [ - "default:undo" - ] - }, - { - "id": "redo", - "title": "redo", - "keywords": [ - "redo" - ] - }, - { - "id": "default:redo", - "title": "default:redo", - "keywords": [ - "default:redo" - ] - }, - { - "id": "editor.action.selectAll", - "title": "editor.action.selectAll", - "keywords": [ - "select all" - ] - }, - { - "id": "columnSelect", - "title": "columnSelect", - "keywords": [ - "column select" - ] - }, - { - "id": "cursorColumnSelectLeft", - "title": "cursorColumnSelectLeft", - "keywords": [ - "cursor column select left", - "cursor column" - ] - }, - { - "id": "cursorColumnSelectRight", - "title": "cursorColumnSelectRight", - "keywords": [ - "cursor column select right", - "cursor column" - ] - }, - { - "id": "cursorColumnSelectUp", - "title": "cursorColumnSelectUp", - "keywords": [ - "cursor column select up", - "cursor column" - ] - }, - { - "id": "cursorColumnSelectPageUp", - "title": "cursorColumnSelectPageUp", - "keywords": [ - "cursor column select page up", - "cursor column" - ] - }, - { - "id": "cursorColumnSelectDown", - "title": "cursorColumnSelectDown", - "keywords": [ - "cursor column select down", - "cursor column" - ] - }, - { - "id": "cursorColumnSelectPageDown", - "title": "cursorColumnSelectPageDown", - "keywords": [ - "cursor column select page down", - "cursor column" - ] - }, - { - "id": "cursorMove", - "title": "cursorMove", - "keywords": [ - "cursor move" - ] - }, - { - "id": "cursorLeft", - "title": "cursorLeft", - "keywords": [ - "cursor left" - ] - }, - { - "id": "cursorLeftSelect", - "title": "cursorLeftSelect", - "keywords": [ - "cursor left select", - "cursor left" - ] - }, - { - "id": "cursorRight", - "title": "cursorRight", - "keywords": [ - "cursor right" - ] - }, - { - "id": "cursorRightSelect", - "title": "cursorRightSelect", - "keywords": [ - "cursor right select", - "cursor right" - ] - }, - { - "id": "cursorUp", - "title": "cursorUp", - "keywords": [ - "cursor up" - ] - }, - { - "id": "cursorUpSelect", - "title": "cursorUpSelect", - "keywords": [ - "cursor up select", - "cursor up" - ] - }, - { - "id": "cursorPageUp", - "title": "cursorPageUp", - "keywords": [ - "cursor page up", - "cursor page" - ] - }, - { - "id": "cursorPageUpSelect", - "title": "cursorPageUpSelect", - "keywords": [ - "cursor page up select", - "cursor page" - ] - }, - { - "id": "cursorDown", - "title": "cursorDown", - "keywords": [ - "cursor down" - ] - }, - { - "id": "cursorDownSelect", - "title": "cursorDownSelect", - "keywords": [ - "cursor down select", - "cursor down" - ] - }, - { - "id": "cursorPageDown", - "title": "cursorPageDown", - "keywords": [ - "cursor page down", - "cursor page" - ] - }, - { - "id": "cursorPageDownSelect", - "title": "cursorPageDownSelect", - "keywords": [ - "cursor page down select", - "cursor page" - ] - }, - { - "id": "createCursor", - "title": "createCursor", - "keywords": [ - "create cursor" - ] - }, - { - "id": "cursorHome", - "title": "cursorHome", - "keywords": [ - "cursor home" - ] - }, - { - "id": "cursorHomeSelect", - "title": "cursorHomeSelect", - "keywords": [ - "cursor home select", - "cursor home" - ] - }, - { - "id": "cursorLineStart", - "title": "cursorLineStart", - "keywords": [ - "cursor line start", - "cursor line" - ] - }, - { - "id": "cursorLineStartSelect", - "title": "cursorLineStartSelect", - "keywords": [ - "cursor line start select", - "cursor line" - ] - }, - { - "id": "cursorEnd", - "title": "cursorEnd", - "keywords": [ - "cursor end" - ] - }, - { - "id": "cursorEndSelect", - "title": "cursorEndSelect", - "keywords": [ - "cursor end select", - "cursor end" - ] - }, - { - "id": "cursorLineEnd", - "title": "cursorLineEnd", - "keywords": [ - "cursor line end", - "cursor line" - ] - }, - { - "id": "cursorLineEndSelect", - "title": "cursorLineEndSelect", - "keywords": [ - "cursor line end select", - "cursor line" - ] - }, - { - "id": "cursorTop", - "title": "cursorTop", - "keywords": [ - "cursor top" - ] - }, - { - "id": "cursorTopSelect", - "title": "cursorTopSelect", - "keywords": [ - "cursor top select", - "cursor top" - ] - }, - { - "id": "cursorBottom", - "title": "cursorBottom", - "keywords": [ - "cursor bottom" - ] - }, - { - "id": "cursorBottomSelect", - "title": "cursorBottomSelect", - "keywords": [ - "cursor bottom select", - "cursor bottom" - ] - }, - { - "id": "editorScroll", - "title": "editorScroll", - "keywords": [ - "editor scroll" - ] - }, - { - "id": "scrollLineUp", - "title": "scrollLineUp", - "keywords": [ - "scroll line up", - "scroll line" - ] - }, - { - "id": "scrollPageUp", - "title": "scrollPageUp", - "keywords": [ - "scroll page up", - "scroll page" - ] - }, - { - "id": "scrollEditorTop", - "title": "scrollEditorTop", - "keywords": [ - "scroll editor top", - "scroll editor" - ] - }, - { - "id": "scrollLineDown", - "title": "scrollLineDown", - "keywords": [ - "scroll line down", - "scroll line" - ] - }, - { - "id": "scrollPageDown", - "title": "scrollPageDown", - "keywords": [ - "scroll page down", - "scroll page" - ] - }, - { - "id": "scrollEditorBottom", - "title": "scrollEditorBottom", - "keywords": [ - "scroll editor bottom", - "scroll editor" - ] - }, - { - "id": "scrollLeft", - "title": "scrollLeft", - "keywords": [ - "scroll left" - ] - }, - { - "id": "scrollRight", - "title": "scrollRight", - "keywords": [ - "scroll right" - ] - }, - { - "id": "lastCursorWordSelect", - "title": "lastCursorWordSelect", - "keywords": [ - "last cursor word select", - "last cursor" - ] - }, - { - "id": "lastCursorLineSelect", - "title": "lastCursorLineSelect", - "keywords": [ - "last cursor line select", - "last cursor" - ] - }, - { - "id": "lastCursorLineSelectDrag", - "title": "lastCursorLineSelectDrag", - "keywords": [ - "last cursor line select drag", - "last cursor" - ] - }, - { - "id": "cancelSelection", - "title": "cancelSelection", - "keywords": [ - "cancel selection" - ] - }, - { - "id": "removeSecondaryCursors", - "title": "removeSecondaryCursors", - "keywords": [ - "remove secondary cursors", - "remove secondary" - ] - }, - { - "id": "revealLine", - "title": "revealLine", - "keywords": [ - "reveal line" - ] - }, - { - "id": "setSelection", - "title": "setSelection", - "keywords": [ - "set selection" - ] - }, - { - "id": "lineBreakInsert", - "title": "lineBreakInsert", - "keywords": [ - "line break insert", - "line break" - ] - }, - { - "id": "outdent", - "title": "outdent", - "keywords": [ - "outdent" - ] - }, - { - "id": "tab", - "title": "tab", - "keywords": [ - "tab" - ] - }, - { - "id": "deleteLeft", - "title": "deleteLeft", - "keywords": [ - "delete left" - ] - }, - { - "id": "deleteRight", - "title": "deleteRight", - "keywords": [ - "delete right" - ] - }, - { - "id": "default:type", - "title": "default:type", - "keywords": [ - "default:type" - ] - }, - { - "id": "type", - "title": "type", - "keywords": [ - "type" - ] - }, - { - "id": "default:replacePreviousChar", - "title": "default:replacePreviousChar", - "keywords": [ - "default:replace previous char", - "default:replace previous" - ] - }, - { - "id": "replacePreviousChar", - "title": "replacePreviousChar", - "keywords": [ - "replace previous char", - "replace previous" - ] - }, - { - "id": "default:compositionType", - "title": "default:compositionType", - "keywords": [ - "default:composition type" - ] - }, - { - "id": "compositionType", - "title": "compositionType", - "keywords": [ - "composition type" - ] - }, - { - "id": "default:compositionStart", - "title": "default:compositionStart", - "keywords": [ - "default:composition start" - ] - }, - { - "id": "compositionStart", - "title": "compositionStart", - "keywords": [ - "composition start" - ] - }, - { - "id": "default:compositionEnd", - "title": "default:compositionEnd", - "keywords": [ - "default:composition end" - ] - }, - { - "id": "compositionEnd", - "title": "compositionEnd", - "keywords": [ - "composition end" - ] - }, - { - "id": "default:paste", - "title": "default:paste", - "keywords": [ - "default:paste" - ] - }, - { - "id": "paste", - "title": "paste", - "keywords": [ - "paste" - ] - }, - { - "id": "default:cut", - "title": "default:cut", - "keywords": [ - "default:cut" - ] - }, - { - "id": "cut", - "title": "cut", - "keywords": [ - "cut" - ] - }, - { - "id": "diffEditor.toggleCollapseUnchangedRegions", - "title": "diffEditor.toggleCollapseUnchangedRegions", - "keywords": [ - "diff editor toggle collapse unchanged regions", - "diff editor" - ] - }, - { - "id": "diffEditor.toggleShowMovedCodeBlocks", - "title": "diffEditor.toggleShowMovedCodeBlocks", - "keywords": [ - "diff editor toggle show moved code blocks", - "diff editor" - ] - }, - { - "id": "diffEditor.toggleUseInlineViewWhenSpaceIsLimited", - "title": "diffEditor.toggleUseInlineViewWhenSpaceIsLimited", - "keywords": [ - "diff editor toggle use inline view when space is limited", - "diff editor" - ] - }, - { - "id": "diffEditor.revert", - "title": "diffEditor.revert", - "keywords": [ - "diff editor revert", - "diff editor" - ] - }, - { - "id": "diffEditor.switchSide", - "title": "diffEditor.switchSide", - "keywords": [ - "diff editor switch side", - "diff editor" - ] - }, - { - "id": "diffEditor.exitCompareMove", - "title": "diffEditor.exitCompareMove", - "keywords": [ - "diff editor exit compare move", - "diff editor" - ] - }, - { - "id": "diffEditor.collapseAllUnchangedRegions", - "title": "diffEditor.collapseAllUnchangedRegions", - "keywords": [ - "diff editor collapse all unchanged regions", - "diff editor" - ] - }, - { - "id": "diffEditor.showAllUnchangedRegions", - "title": "diffEditor.showAllUnchangedRegions", - "keywords": [ - "diff editor show all unchanged regions", - "diff editor" - ] - }, - { - "id": "editor.action.diffReview.next", - "title": "editor.action.diffReview.next", - "keywords": [ - "diff review next", - "diff review" - ] - }, - { - "id": "editor.action.accessibleDiffViewer.next", - "title": "editor.action.accessibleDiffViewer.next", - "keywords": [ - "accessible diff viewer next", - "accessible diff" - ] - }, - { - "id": "editor.action.diffReview.prev", - "title": "editor.action.diffReview.prev", - "keywords": [ - "diff review prev", - "diff review" - ] - }, - { - "id": "editor.action.accessibleDiffViewer.prev", - "title": "editor.action.accessibleDiffViewer.prev", - "keywords": [ - "accessible diff viewer prev", - "accessible diff" - ] - }, - { - "id": "editor.action.setSelectionAnchor", - "title": "editor.action.setSelectionAnchor", - "keywords": [ - "set selection anchor", - "set selection" - ] - }, - { - "id": "editor.action.goToSelectionAnchor", - "title": "editor.action.goToSelectionAnchor", - "keywords": [ - "go to selection anchor", - "go to" - ] - }, - { - "id": "editor.action.selectFromAnchorToCursor", - "title": "editor.action.selectFromAnchorToCursor", - "keywords": [ - "select from anchor to cursor", - "select from" - ] - }, - { - "id": "editor.action.cancelSelectionAnchor", - "title": "editor.action.cancelSelectionAnchor", - "keywords": [ - "cancel selection anchor", - "cancel selection" - ] - }, - { - "id": "editor.action.selectToBracket", - "title": "editor.action.selectToBracket", - "keywords": [ - "select to bracket", - "select to" - ] - }, - { - "id": "editor.action.jumpToBracket", - "title": "editor.action.jumpToBracket", - "keywords": [ - "jump to bracket", - "jump to" - ] - }, - { - "id": "editor.action.removeBrackets", - "title": "editor.action.removeBrackets", - "keywords": [ - "remove brackets" - ] - }, - { - "id": "editor.action.moveCarretLeftAction", - "title": "editor.action.moveCarretLeftAction", - "keywords": [ - "move carret left action", - "move carret" - ] - }, - { - "id": "editor.action.moveCarretRightAction", - "title": "editor.action.moveCarretRightAction", - "keywords": [ - "move carret right action", - "move carret" - ] - }, - { - "id": "editor.action.transposeLetters", - "title": "editor.action.transposeLetters", - "keywords": [ - "transpose letters" - ] - }, - { - "id": "editor.cancelOperation", - "title": "editor.cancelOperation", - "keywords": [ - "editor cancel operation", - "editor cancel" - ] - }, - { - "id": "leaveEditorMessage", - "title": "leaveEditorMessage", - "keywords": [ - "leave editor message", - "leave editor" - ] - }, - { - "id": "hideCodeActionWidget", - "title": "hideCodeActionWidget", - "keywords": [ - "hide code action widget", - "hide code" - ] - }, - { - "id": "clearFilterCodeActionWidget", - "title": "clearFilterCodeActionWidget", - "keywords": [ - "clear filter code action widget", - "clear filter" - ] - }, - { - "id": "selectPrevCodeAction", - "title": "selectPrevCodeAction", - "keywords": [ - "select prev code action", - "select prev" - ] - }, - { - "id": "selectNextCodeAction", - "title": "selectNextCodeAction", - "keywords": [ - "select next code action", - "select next" - ] - }, - { - "id": "collapseSectionCodeAction", - "title": "collapseSectionCodeAction", - "keywords": [ - "collapse section code action", - "collapse section" - ] - }, - { - "id": "expandSectionCodeAction", - "title": "expandSectionCodeAction", - "keywords": [ - "expand section code action", - "expand section" - ] - }, - { - "id": "toggleSectionCodeAction", - "title": "toggleSectionCodeAction", - "keywords": [ - "toggle section code action", - "toggle section" - ] - }, - { - "id": "acceptSelectedCodeAction", - "title": "acceptSelectedCodeAction", - "keywords": [ - "accept selected code action", - "accept selected" - ] - }, - { - "id": "previewSelectedCodeAction", - "title": "previewSelectedCodeAction", - "keywords": [ - "preview selected code action", - "preview selected" - ] - }, - { - "id": "editor.action.clipboardCutAction", - "title": "editor.action.clipboardCutAction", - "keywords": [ - "clipboard cut action", - "clipboard cut" - ] - }, - { - "id": "editor.action.clipboardCopyAction", - "title": "editor.action.clipboardCopyAction", - "keywords": [ - "clipboard copy action", - "clipboard copy" - ] - }, - { - "id": "editor.action.clipboardPasteAction", - "title": "editor.action.clipboardPasteAction", - "keywords": [ - "clipboard paste action", - "clipboard paste" - ] - }, - { - "id": "editor.action.clipboardCopyWithSyntaxHighlightingAction", - "title": "editor.action.clipboardCopyWithSyntaxHighlightingAction", - "keywords": [ - "clipboard copy with syntax highlighting action", - "clipboard copy" - ] - }, - { - "id": "editor.action.quickFix", - "title": "editor.action.quickFix", - "keywords": [ - "quick fix" - ] - }, - { - "id": "editor.action.refactor", - "title": "editor.action.refactor", - "keywords": [ - "refactor" - ] - }, - { - "id": "editor.action.sourceAction", - "title": "editor.action.sourceAction", - "keywords": [ - "source action" - ] - }, - { - "id": "editor.action.organizeImports", - "title": "editor.action.organizeImports", - "keywords": [ - "organize imports" - ] - }, - { - "id": "editor.action.autoFix", - "title": "editor.action.autoFix", - "keywords": [ - "auto fix" - ] - }, - { - "id": "editor.action.fixAll", - "title": "editor.action.fixAll", - "keywords": [ - "fix all" - ] - }, - { - "id": "editor.action.codeAction", - "title": "editor.action.codeAction", - "keywords": [ - "code action" - ] - }, - { - "id": "codelens.showLensesInCurrentLine", - "title": "codelens.showLensesInCurrentLine", - "keywords": [ - "codelens show lenses in current line", - "codelens show" - ] - }, - { - "id": "history.showPrevious", - "title": "history.showPrevious", - "keywords": [ - "history show previous", - "history show" - ] - }, - { - "id": "history.showNext", - "title": "history.showNext", - "keywords": [ - "history show next", - "history show" - ] - }, - { - "id": "togglePeekWidgetFocus", - "title": "togglePeekWidgetFocus", - "keywords": [ - "toggle peek widget focus", - "toggle peek" - ] - }, - { - "id": "goToNextReference", - "title": "goToNextReference", - "keywords": [ - "go to next reference", - "go to" - ] - }, - { - "id": "goToPreviousReference", - "title": "goToPreviousReference", - "keywords": [ - "go to previous reference", - "go to" - ] - }, - { - "id": "goToNextReferenceFromEmbeddedEditor", - "title": "goToNextReferenceFromEmbeddedEditor", - "keywords": [ - "go to next reference from embedded editor", - "go to" - ] - }, - { - "id": "goToPreviousReferenceFromEmbeddedEditor", - "title": "goToPreviousReferenceFromEmbeddedEditor", - "keywords": [ - "go to previous reference from embedded editor", - "go to" - ] - }, - { - "id": "closeReferenceSearchEditor", - "title": "closeReferenceSearchEditor", - "keywords": [ - "close reference search editor", - "close reference" - ] - }, - { - "id": "closeReferenceSearch", - "title": "closeReferenceSearch", - "keywords": [ - "close reference search", - "close reference" - ] - }, - { - "id": "revealReference", - "title": "revealReference", - "keywords": [ - "reveal reference" - ] - }, - { - "id": "openReferenceToSide", - "title": "openReferenceToSide", - "keywords": [ - "open reference to side", - "open reference" - ] - }, - { - "id": "openReference", - "title": "openReference", - "keywords": [ - "open reference" - ] - }, - { - "id": "editor.gotoNextSymbolFromResult", - "title": "editor.gotoNextSymbolFromResult", - "keywords": [ - "editor goto next symbol from result", - "editor goto" - ] - }, - { - "id": "editor.gotoNextSymbolFromResult.cancel", - "title": "editor.gotoNextSymbolFromResult.cancel", - "keywords": [ - "editor goto next symbol from result cancel", - "editor goto" - ] - }, - { - "id": "editor.action.goToDeclaration", - "title": "editor.action.goToDeclaration", - "keywords": [ - "go to declaration", - "go to" - ] - }, - { - "id": "editor.action.revealDefinition", - "title": "editor.action.revealDefinition", - "keywords": [ - "reveal definition" - ] - }, - { - "id": "editor.action.openDeclarationToTheSide", - "title": "editor.action.openDeclarationToTheSide", - "keywords": [ - "open declaration to the side", - "open declaration" - ] - }, - { - "id": "editor.action.revealDefinitionAside", - "title": "editor.action.revealDefinitionAside", - "keywords": [ - "reveal definition aside", - "reveal definition" - ] - }, - { - "id": "editor.action.previewDeclaration", - "title": "editor.action.previewDeclaration", - "keywords": [ - "preview declaration" - ] - }, - { - "id": "editor.action.peekDefinition", - "title": "editor.action.peekDefinition", - "keywords": [ - "peek definition" - ] - }, - { - "id": "editor.action.revealDeclaration", - "title": "editor.action.revealDeclaration", - "keywords": [ - "reveal declaration" - ] - }, - { - "id": "editor.action.peekDeclaration", - "title": "editor.action.peekDeclaration", - "keywords": [ - "peek declaration" - ] - }, - { - "id": "editor.action.goToTypeDefinition", - "title": "editor.action.goToTypeDefinition", - "keywords": [ - "go to type definition", - "go to" - ] - }, - { - "id": "editor.action.peekTypeDefinition", - "title": "editor.action.peekTypeDefinition", - "keywords": [ - "peek type definition", - "peek type" - ] - }, - { - "id": "editor.action.goToImplementation", - "title": "editor.action.goToImplementation", - "keywords": [ - "go to implementation", - "go to" - ] - }, - { - "id": "editor.action.peekImplementation", - "title": "editor.action.peekImplementation", - "keywords": [ - "peek implementation" - ] - }, - { - "id": "editor.action.goToReferences", - "title": "editor.action.goToReferences", - "keywords": [ - "go to references", - "go to" - ] - }, - { - "id": "editor.action.referenceSearch.trigger", - "title": "editor.action.referenceSearch.trigger", - "keywords": [ - "reference search trigger", - "reference search" - ] - }, - { - "id": "editor.action.goToLocations", - "title": "editor.action.goToLocations", - "keywords": [ - "go to locations", - "go to" - ] - }, - { - "id": "editor.action.peekLocations", - "title": "editor.action.peekLocations", - "keywords": [ - "peek locations" - ] - }, - { - "id": "editor.action.findReferences", - "title": "editor.action.findReferences", - "keywords": [ - "find references" - ] - }, - { - "id": "editor.action.showReferences", - "title": "editor.action.showReferences", - "keywords": [ - "show references" - ] - }, - { - "id": "editor.action.marker.next", - "title": "editor.action.marker.next", - "keywords": [ - "marker next" - ] - }, - { - "id": "editor.action.marker.prev", - "title": "editor.action.marker.prev", - "keywords": [ - "marker prev" - ] - }, - { - "id": "editor.action.marker.nextInFiles", - "title": "editor.action.marker.nextInFiles", - "keywords": [ - "marker next in files", - "marker next" - ] - }, - { - "id": "editor.action.marker.prevInFiles", - "title": "editor.action.marker.prevInFiles", - "keywords": [ - "marker prev in files", - "marker prev" - ] - }, - { - "id": "closeMarkersNavigation", - "title": "closeMarkersNavigation", - "keywords": [ - "close markers navigation", - "close markers" - ] - }, - { - "id": "editor.action.hideColorPicker", - "title": "editor.action.hideColorPicker", - "keywords": [ - "hide color picker", - "hide color" - ] - }, - { - "id": "editor.action.insertColorWithStandaloneColorPicker", - "title": "editor.action.insertColorWithStandaloneColorPicker", - "keywords": [ - "insert color with standalone color picker", - "insert color" - ] - }, - { - "id": "editor.action.showOrFocusStandaloneColorPicker", - "title": "editor.action.showOrFocusStandaloneColorPicker", - "keywords": [ - "show or focus standalone color picker", - "show or" - ] - }, - { - "id": "editor.action.commentLine", - "title": "editor.action.commentLine", - "keywords": [ - "comment line" - ] - }, - { - "id": "editor.action.addCommentLine", - "title": "editor.action.addCommentLine", - "keywords": [ - "add comment line", - "add comment" - ] - }, - { - "id": "editor.action.removeCommentLine", - "title": "editor.action.removeCommentLine", - "keywords": [ - "remove comment line", - "remove comment" - ] - }, - { - "id": "editor.action.blockComment", - "title": "editor.action.blockComment", - "keywords": [ - "block comment" - ] - }, - { - "id": "editor.action.showContextMenu", - "title": "editor.action.showContextMenu", - "keywords": [ - "show context menu", - "show context" - ] - }, - { - "id": "cursorUndo", - "title": "cursorUndo", - "keywords": [ - "cursor undo" - ] - }, - { - "id": "cursorRedo", - "title": "cursorRedo", - "keywords": [ - "cursor redo" - ] - }, - { - "id": "editor.changePasteType", - "title": "editor.changePasteType", - "keywords": [ - "editor change paste type", - "editor change" - ] - }, - { - "id": "editor.hidePasteWidget", - "title": "editor.hidePasteWidget", - "keywords": [ - "editor hide paste widget", - "editor hide" - ] - }, - { - "id": "editor.action.pasteAs", - "title": "editor.action.pasteAs", - "keywords": [ - "paste as" - ] - }, - { - "id": "editor.action.pasteAsText", - "title": "editor.action.pasteAsText", - "keywords": [ - "paste as text", - "paste as" - ] - }, - { - "id": "editor.changeDropType", - "title": "editor.changeDropType", - "keywords": [ - "editor change drop type", - "editor change" - ] - }, - { - "id": "editor.hideDropWidget", - "title": "editor.hideDropWidget", - "keywords": [ - "editor hide drop widget", - "editor hide" - ] - }, - { - "id": "actions.find", - "title": "actions.find", - "keywords": [ - "actions find" - ] - }, - { - "id": "editor.action.nextMatchFindAction", - "title": "editor.action.nextMatchFindAction", - "keywords": [ - "next match find action", - "next match" - ] - }, - { - "id": "editor.action.previousMatchFindAction", - "title": "editor.action.previousMatchFindAction", - "keywords": [ - "previous match find action", - "previous match" - ] - }, - { - "id": "editor.action.startFindReplaceAction", - "title": "editor.action.startFindReplaceAction", - "keywords": [ - "start find replace action", - "start find" - ] - }, - { - "id": "editor.actions.findWithArgs", - "title": "editor.actions.findWithArgs", - "keywords": [ - "editor actions find with args", - "editor actions" - ] - }, - { - "id": "actions.findWithSelection", - "title": "actions.findWithSelection", - "keywords": [ - "actions find with selection", - "actions find" - ] - }, - { - "id": "editor.action.goToMatchFindAction", - "title": "editor.action.goToMatchFindAction", - "keywords": [ - "go to match find action", - "go to" - ] - }, - { - "id": "editor.action.nextSelectionMatchFindAction", - "title": "editor.action.nextSelectionMatchFindAction", - "keywords": [ - "next selection match find action", - "next selection" - ] - }, - { - "id": "editor.action.previousSelectionMatchFindAction", - "title": "editor.action.previousSelectionMatchFindAction", - "keywords": [ - "previous selection match find action", - "previous selection" - ] - }, - { - "id": "closeFindWidget", - "title": "closeFindWidget", - "keywords": [ - "close find widget", - "close find" - ] - }, - { - "id": "toggleFindCaseSensitive", - "title": "toggleFindCaseSensitive", - "keywords": [ - "toggle find case sensitive", - "toggle find" - ] - }, - { - "id": "toggleFindWholeWord", - "title": "toggleFindWholeWord", - "keywords": [ - "toggle find whole word", - "toggle find" - ] - }, - { - "id": "toggleFindRegex", - "title": "toggleFindRegex", - "keywords": [ - "toggle find regex", - "toggle find" - ] - }, - { - "id": "toggleFindInSelection", - "title": "toggleFindInSelection", - "keywords": [ - "toggle find in selection", - "toggle find" - ] - }, - { - "id": "togglePreserveCase", - "title": "togglePreserveCase", - "keywords": [ - "toggle preserve case", - "toggle preserve" - ] - }, - { - "id": "editor.action.replaceOne", - "title": "editor.action.replaceOne", - "keywords": [ - "replace one" - ] - }, - { - "id": "editor.action.replaceAll", - "title": "editor.action.replaceAll", - "keywords": [ - "replace all" - ] - }, - { - "id": "editor.action.selectAllMatches", - "title": "editor.action.selectAllMatches", - "keywords": [ - "select all matches", - "select all" - ] - }, - { - "id": "editor.unfold", - "title": "editor.unfold", - "keywords": [ - "editor unfold" - ] - }, - { - "id": "editor.unfoldRecursively", - "title": "editor.unfoldRecursively", - "keywords": [ - "editor unfold recursively", - "editor unfold" - ] - }, - { - "id": "editor.fold", - "title": "editor.fold", - "keywords": [ - "editor fold" - ] - }, - { - "id": "editor.foldRecursively", - "title": "editor.foldRecursively", - "keywords": [ - "editor fold recursively", - "editor fold" - ] - }, - { - "id": "editor.toggleFoldRecursively", - "title": "editor.toggleFoldRecursively", - "keywords": [ - "editor toggle fold recursively", - "editor toggle" - ] - }, - { - "id": "editor.foldAll", - "title": "editor.foldAll", - "keywords": [ - "editor fold all", - "editor fold" - ] - }, - { - "id": "editor.unfoldAll", - "title": "editor.unfoldAll", - "keywords": [ - "editor unfold all", - "editor unfold" - ] - }, - { - "id": "editor.foldAllBlockComments", - "title": "editor.foldAllBlockComments", - "keywords": [ - "editor fold all block comments", - "editor fold" - ] - }, - { - "id": "editor.foldAllMarkerRegions", - "title": "editor.foldAllMarkerRegions", - "keywords": [ - "editor fold all marker regions", - "editor fold" - ] - }, - { - "id": "editor.unfoldAllMarkerRegions", - "title": "editor.unfoldAllMarkerRegions", - "keywords": [ - "editor unfold all marker regions", - "editor unfold" - ] - }, - { - "id": "editor.foldAllExcept", - "title": "editor.foldAllExcept", - "keywords": [ - "editor fold all except", - "editor fold" - ] - }, - { - "id": "editor.unfoldAllExcept", - "title": "editor.unfoldAllExcept", - "keywords": [ - "editor unfold all except", - "editor unfold" - ] - }, - { - "id": "editor.toggleFold", - "title": "editor.toggleFold", - "keywords": [ - "editor toggle fold", - "editor toggle" - ] - }, - { - "id": "editor.gotoParentFold", - "title": "editor.gotoParentFold", - "keywords": [ - "editor goto parent fold", - "editor goto" - ] - }, - { - "id": "editor.gotoPreviousFold", - "title": "editor.gotoPreviousFold", - "keywords": [ - "editor goto previous fold", - "editor goto" - ] - }, - { - "id": "editor.gotoNextFold", - "title": "editor.gotoNextFold", - "keywords": [ - "editor goto next fold", - "editor goto" - ] - }, - { - "id": "editor.createFoldingRangeFromSelection", - "title": "editor.createFoldingRangeFromSelection", - "keywords": [ - "editor create folding range from selection", - "editor create" - ] - }, - { - "id": "editor.removeManualFoldingRanges", - "title": "editor.removeManualFoldingRanges", - "keywords": [ - "editor remove manual folding ranges", - "editor remove" - ] - }, - { - "id": "editor.toggleImportFold", - "title": "editor.toggleImportFold", - "keywords": [ - "editor toggle import fold", - "editor toggle" - ] - }, - { - "id": "editor.foldLevel1", - "title": "editor.foldLevel1", - "keywords": [ - "editor fold level1", - "editor fold" - ] - }, - { - "id": "editor.foldLevel2", - "title": "editor.foldLevel2", - "keywords": [ - "editor fold level2", - "editor fold" - ] - }, - { - "id": "editor.foldLevel3", - "title": "editor.foldLevel3", - "keywords": [ - "editor fold level3", - "editor fold" - ] - }, - { - "id": "editor.foldLevel4", - "title": "editor.foldLevel4", - "keywords": [ - "editor fold level4", - "editor fold" - ] - }, - { - "id": "editor.foldLevel5", - "title": "editor.foldLevel5", - "keywords": [ - "editor fold level5", - "editor fold" - ] - }, - { - "id": "editor.foldLevel6", - "title": "editor.foldLevel6", - "keywords": [ - "editor fold level6", - "editor fold" - ] - }, - { - "id": "editor.foldLevel7", - "title": "editor.foldLevel7", - "keywords": [ - "editor fold level7", - "editor fold" - ] - }, - { - "id": "editor.action.fontZoomIn", - "title": "editor.action.fontZoomIn", - "keywords": [ - "font zoom in", - "font zoom" - ] - }, - { - "id": "editor.action.fontZoomOut", - "title": "editor.action.fontZoomOut", - "keywords": [ - "font zoom out", - "font zoom" - ] - }, - { - "id": "editor.action.fontZoomReset", - "title": "editor.action.fontZoomReset", - "keywords": [ - "font zoom reset", - "font zoom" - ] - }, - { - "id": "editor.action.formatDocument", - "title": "editor.action.formatDocument", - "keywords": [ - "format document" - ] - }, - { - "id": "editor.action.formatSelection", - "title": "editor.action.formatSelection", - "keywords": [ - "format selection" - ] - }, - { - "id": "editor.action.format", - "title": "editor.action.format", - "keywords": [ - "format" - ] - }, - { - "id": "editor.action.copyLinesUpAction", - "title": "editor.action.copyLinesUpAction", - "keywords": [ - "copy lines up action", - "copy lines" - ] - }, - { - "id": "editor.action.copyLinesDownAction", - "title": "editor.action.copyLinesDownAction", - "keywords": [ - "copy lines down action", - "copy lines" - ] - }, - { - "id": "editor.action.duplicateSelection", - "title": "editor.action.duplicateSelection", - "keywords": [ - "duplicate selection" - ] - }, - { - "id": "editor.action.moveLinesUpAction", - "title": "editor.action.moveLinesUpAction", - "keywords": [ - "move lines up action", - "move lines" - ] - }, - { - "id": "editor.action.moveLinesDownAction", - "title": "editor.action.moveLinesDownAction", - "keywords": [ - "move lines down action", - "move lines" - ] - }, - { - "id": "editor.action.sortLinesAscending", - "title": "editor.action.sortLinesAscending", - "keywords": [ - "sort lines ascending", - "sort lines" - ] - }, - { - "id": "editor.action.sortLinesDescending", - "title": "editor.action.sortLinesDescending", - "keywords": [ - "sort lines descending", - "sort lines" - ] - }, - { - "id": "editor.action.removeDuplicateLines", - "title": "editor.action.removeDuplicateLines", - "keywords": [ - "remove duplicate lines", - "remove duplicate" - ] - }, - { - "id": "editor.action.trimTrailingWhitespace", - "title": "editor.action.trimTrailingWhitespace", - "keywords": [ - "trim trailing whitespace", - "trim trailing" - ] - }, - { - "id": "editor.action.deleteLines", - "title": "editor.action.deleteLines", - "keywords": [ - "delete lines" - ] - }, - { - "id": "editor.action.indentLines", - "title": "editor.action.indentLines", - "keywords": [ - "indent lines" - ] - }, - { - "id": "editor.action.outdentLines", - "title": "editor.action.outdentLines", - "keywords": [ - "outdent lines" - ] - }, - { - "id": "editor.action.insertLineBefore", - "title": "editor.action.insertLineBefore", - "keywords": [ - "insert line before", - "insert line" - ] - }, - { - "id": "editor.action.insertLineAfter", - "title": "editor.action.insertLineAfter", - "keywords": [ - "insert line after", - "insert line" - ] - }, - { - "id": "deleteAllLeft", - "title": "deleteAllLeft", - "keywords": [ - "delete all left", - "delete all" - ] - }, - { - "id": "deleteAllRight", - "title": "deleteAllRight", - "keywords": [ - "delete all right", - "delete all" - ] - }, - { - "id": "editor.action.joinLines", - "title": "editor.action.joinLines", - "keywords": [ - "join lines" - ] - }, - { - "id": "editor.action.transpose", - "title": "editor.action.transpose", - "keywords": [ - "transpose" - ] - }, - { - "id": "editor.action.transformToUppercase", - "title": "editor.action.transformToUppercase", - "keywords": [ - "transform to uppercase", - "transform to" - ] - }, - { - "id": "editor.action.transformToLowercase", - "title": "editor.action.transformToLowercase", - "keywords": [ - "transform to lowercase", - "transform to" - ] - }, - { - "id": "editor.action.reverseLines", - "title": "editor.action.reverseLines", - "keywords": [ - "reverse lines" - ] - }, - { - "id": "editor.action.transformToSnakecase", - "title": "editor.action.transformToSnakecase", - "keywords": [ - "transform to snakecase", - "transform to" - ] - }, - { - "id": "editor.action.transformToCamelcase", - "title": "editor.action.transformToCamelcase", - "keywords": [ - "transform to camelcase", - "transform to" - ] - }, - { - "id": "editor.action.transformToPascalcase", - "title": "editor.action.transformToPascalcase", - "keywords": [ - "transform to pascalcase", - "transform to" - ] - }, - { - "id": "editor.action.transformToTitlecase", - "title": "editor.action.transformToTitlecase", - "keywords": [ - "transform to titlecase", - "transform to" - ] - }, - { - "id": "editor.action.transformToKebabcase", - "title": "editor.action.transformToKebabcase", - "keywords": [ - "transform to kebabcase", - "transform to" - ] - }, - { - "id": "jumpToNextSnippetPlaceholder", - "title": "jumpToNextSnippetPlaceholder", - "keywords": [ - "jump to next snippet placeholder", - "jump to" - ] - }, - { - "id": "jumpToPrevSnippetPlaceholder", - "title": "jumpToPrevSnippetPlaceholder", - "keywords": [ - "jump to prev snippet placeholder", - "jump to" - ] - }, - { - "id": "leaveSnippet", - "title": "leaveSnippet", - "keywords": [ - "leave snippet" - ] - }, - { - "id": "acceptSnippet", - "title": "acceptSnippet", - "keywords": [ - "accept snippet" - ] - }, - { - "id": "editor.action.rename", - "title": "editor.action.rename", - "keywords": [ - "rename" - ] - }, - { - "id": "acceptRenameInput", - "title": "acceptRenameInput", - "keywords": [ - "accept rename input", - "accept rename" - ] - }, - { - "id": "acceptRenameInputWithPreview", - "title": "acceptRenameInputWithPreview", - "keywords": [ - "accept rename input with preview", - "accept rename" - ] - }, - { - "id": "cancelRenameInput", - "title": "cancelRenameInput", - "keywords": [ - "cancel rename input", - "cancel rename" - ] - }, - { - "id": "focusNextRenameSuggestion", - "title": "focusNextRenameSuggestion", - "keywords": [ - "focus next rename suggestion", - "focus next" - ] - }, - { - "id": "focusPreviousRenameSuggestion", - "title": "focusPreviousRenameSuggestion", - "keywords": [ - "focus previous rename suggestion", - "focus previous" - ] - }, - { - "id": "editor.action.triggerSuggest", - "title": "editor.action.triggerSuggest", - "keywords": [ - "trigger suggest" - ] - }, - { - "id": "acceptSelectedSuggestion", - "title": "acceptSelectedSuggestion", - "keywords": [ - "accept selected suggestion", - "accept selected" - ] - }, - { - "id": "acceptAlternativeSelectedSuggestion", - "title": "acceptAlternativeSelectedSuggestion", - "keywords": [ - "accept alternative selected suggestion", - "accept alternative" - ] - }, - { - "id": "acceptSelectedSuggestionOnEnter", - "title": "acceptSelectedSuggestionOnEnter", - "keywords": [ - "accept selected suggestion on enter", - "accept selected" - ] - }, - { - "id": "hideSuggestWidget", - "title": "hideSuggestWidget", - "keywords": [ - "hide suggest widget", - "hide suggest" - ] - }, - { - "id": "selectNextSuggestion", - "title": "selectNextSuggestion", - "keywords": [ - "select next suggestion", - "select next" - ] - }, - { - "id": "selectNextPageSuggestion", - "title": "selectNextPageSuggestion", - "keywords": [ - "select next page suggestion", - "select next" - ] - }, - { - "id": "selectLastSuggestion", - "title": "selectLastSuggestion", - "keywords": [ - "select last suggestion", - "select last" - ] - }, - { - "id": "selectPrevSuggestion", - "title": "selectPrevSuggestion", - "keywords": [ - "select prev suggestion", - "select prev" - ] - }, - { - "id": "selectPrevPageSuggestion", - "title": "selectPrevPageSuggestion", - "keywords": [ - "select prev page suggestion", - "select prev" - ] - }, - { - "id": "selectFirstSuggestion", - "title": "selectFirstSuggestion", - "keywords": [ - "select first suggestion", - "select first" - ] - }, - { - "id": "focusSuggestion", - "title": "focusSuggestion", - "keywords": [ - "focus suggestion" - ] - }, - { - "id": "focusAndAcceptSuggestion", - "title": "focusAndAcceptSuggestion", - "keywords": [ - "focus and accept suggestion", - "focus and" - ] - }, - { - "id": "toggleSuggestionDetails", - "title": "toggleSuggestionDetails", - "keywords": [ - "toggle suggestion details", - "toggle suggestion" - ] - }, - { - "id": "toggleExplainMode", - "title": "toggleExplainMode", - "keywords": [ - "toggle explain mode", - "toggle explain" - ] - }, - { - "id": "toggleSuggestionFocus", - "title": "toggleSuggestionFocus", - "keywords": [ - "toggle suggestion focus", - "toggle suggestion" - ] - }, - { - "id": "insertBestCompletion", - "title": "insertBestCompletion", - "keywords": [ - "insert best completion", - "insert best" - ] - }, - { - "id": "insertNextSuggestion", - "title": "insertNextSuggestion", - "keywords": [ - "insert next suggestion", - "insert next" - ] - }, - { - "id": "insertPrevSuggestion", - "title": "insertPrevSuggestion", - "keywords": [ - "insert prev suggestion", - "insert prev" - ] - }, - { - "id": "suggestWidgetCopy", - "title": "suggestWidgetCopy", - "keywords": [ - "suggest widget copy", - "suggest widget" - ] - }, - { - "id": "editor.action.resetSuggestSize", - "title": "editor.action.resetSuggestSize", - "keywords": [ - "reset suggest size", - "reset suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.trigger", - "title": "editor.action.inlineSuggest.trigger", - "keywords": [ - "inline suggest trigger", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.showNext", - "title": "editor.action.inlineSuggest.showNext", - "keywords": [ - "inline suggest show next", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.showPrevious", - "title": "editor.action.inlineSuggest.showPrevious", - "keywords": [ - "inline suggest show previous", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.acceptNextWord", - "title": "editor.action.inlineSuggest.acceptNextWord", - "keywords": [ - "inline suggest accept next word", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.acceptNextLine", - "title": "editor.action.inlineSuggest.acceptNextLine", - "keywords": [ - "inline suggest accept next line", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.commit", - "title": "editor.action.inlineSuggest.commit", - "keywords": [ - "inline suggest commit", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.commitAlternativeAction", - "title": "editor.action.inlineSuggest.commitAlternativeAction", - "keywords": [ - "inline suggest commit alternative action", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.toggleShowCollapsed", - "title": "editor.action.inlineSuggest.toggleShowCollapsed", - "keywords": [ - "inline suggest toggle show collapsed", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.hide", - "title": "editor.action.inlineSuggest.hide", - "keywords": [ - "inline suggest hide", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.jump", - "title": "editor.action.inlineSuggest.jump", - "keywords": [ - "inline suggest jump", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.toggleAlwaysShowToolbar", - "title": "editor.action.inlineSuggest.toggleAlwaysShowToolbar", - "keywords": [ - "inline suggest toggle always show toolbar", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.dev.extractRepro", - "title": "editor.action.inlineSuggest.dev.extractRepro", - "keywords": [ - "inline suggest dev extract repro", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.snooze", - "title": "editor.action.inlineSuggest.snooze", - "keywords": [ - "inline suggest snooze", - "inline suggest" - ] - }, - { - "id": "editor.action.inlineSuggest.cancelSnooze", - "title": "editor.action.inlineSuggest.cancelSnooze", - "keywords": [ - "inline suggest cancel snooze", - "inline suggest" - ] - }, - { - "id": "editor.action.debugEditorGpuRenderer", - "title": "editor.action.debugEditorGpuRenderer", - "keywords": [ - "debug editor gpu renderer", - "debug editor" - ] - }, - { - "id": "editor.action.showHover", - "title": "editor.action.showHover", - "keywords": [ - "show hover" - ] - }, - { - "id": "editor.action.showDefinitionPreviewHover", - "title": "editor.action.showDefinitionPreviewHover", - "keywords": [ - "show definition preview hover", - "show definition" - ] - }, - { - "id": "editor.action.hideHover", - "title": "editor.action.hideHover", - "keywords": [ - "hide hover" - ] - }, - { - "id": "editor.action.scrollUpHover", - "title": "editor.action.scrollUpHover", - "keywords": [ - "scroll up hover", - "scroll up" - ] - }, - { - "id": "editor.action.scrollDownHover", - "title": "editor.action.scrollDownHover", - "keywords": [ - "scroll down hover", - "scroll down" - ] - }, - { - "id": "editor.action.scrollLeftHover", - "title": "editor.action.scrollLeftHover", - "keywords": [ - "scroll left hover", - "scroll left" - ] - }, - { - "id": "editor.action.scrollRightHover", - "title": "editor.action.scrollRightHover", - "keywords": [ - "scroll right hover", - "scroll right" - ] - }, - { - "id": "editor.action.pageUpHover", - "title": "editor.action.pageUpHover", - "keywords": [ - "page up hover", - "page up" - ] - }, - { - "id": "editor.action.pageDownHover", - "title": "editor.action.pageDownHover", - "keywords": [ - "page down hover", - "page down" - ] - }, - { - "id": "editor.action.goToTopHover", - "title": "editor.action.goToTopHover", - "keywords": [ - "go to top hover", - "go to" - ] - }, - { - "id": "editor.action.goToBottomHover", - "title": "editor.action.goToBottomHover", - "keywords": [ - "go to bottom hover", - "go to" - ] - }, - { - "id": "editor.action.increaseHoverVerbosityLevel", - "title": "editor.action.increaseHoverVerbosityLevel", - "keywords": [ - "increase hover verbosity level", - "increase hover" - ] - }, - { - "id": "editor.action.decreaseHoverVerbosityLevel", - "title": "editor.action.decreaseHoverVerbosityLevel", - "keywords": [ - "decrease hover verbosity level", - "decrease hover" - ] - }, - { - "id": "editor.action.hideLongLineWarningHover", - "title": "editor.action.hideLongLineWarningHover", - "keywords": [ - "hide long line warning hover", - "hide long" - ] - }, - { - "id": "editor.action.indentationToSpaces", - "title": "editor.action.indentationToSpaces", - "keywords": [ - "indentation to spaces", - "indentation to" - ] - }, - { - "id": "editor.action.indentationToTabs", - "title": "editor.action.indentationToTabs", - "keywords": [ - "indentation to tabs", - "indentation to" - ] - }, - { - "id": "editor.action.indentUsingTabs", - "title": "editor.action.indentUsingTabs", - "keywords": [ - "indent using tabs", - "indent using" - ] - }, - { - "id": "editor.action.indentUsingSpaces", - "title": "editor.action.indentUsingSpaces", - "keywords": [ - "indent using spaces", - "indent using" - ] - }, - { - "id": "editor.action.changeTabDisplaySize", - "title": "editor.action.changeTabDisplaySize", - "keywords": [ - "change tab display size", - "change tab" - ] - }, - { - "id": "editor.action.detectIndentation", - "title": "editor.action.detectIndentation", - "keywords": [ - "detect indentation" - ] - }, - { - "id": "editor.action.reindentlines", - "title": "editor.action.reindentlines", - "keywords": [ - "reindentlines" - ] - }, - { - "id": "editor.action.reindentselectedlines", - "title": "editor.action.reindentselectedlines", - "keywords": [ - "reindentselectedlines" - ] - }, - { - "id": "editor.action.inPlaceReplace.up", - "title": "editor.action.inPlaceReplace.up", - "keywords": [ - "in place replace up", - "in place" - ] - }, - { - "id": "editor.action.inPlaceReplace.down", - "title": "editor.action.inPlaceReplace.down", - "keywords": [ - "in place replace down", - "in place" - ] - }, - { - "id": "editor.action.insertFinalNewLine", - "title": "editor.action.insertFinalNewLine", - "keywords": [ - "insert final new line", - "insert final" - ] - }, - { - "id": "expandLineSelection", - "title": "expandLineSelection", - "keywords": [ - "expand line selection", - "expand line" - ] - }, - { - "id": "cancelLinkedEditingInput", - "title": "cancelLinkedEditingInput", - "keywords": [ - "cancel linked editing input", - "cancel linked" - ] - }, - { - "id": "editor.action.linkedEditing", - "title": "editor.action.linkedEditing", - "keywords": [ - "linked editing" - ] - }, - { - "id": "editor.action.openLink", - "title": "editor.action.openLink", - "keywords": [ - "open link" - ] - }, - { - "id": "editor.action.insertCursorAbove", - "title": "editor.action.insertCursorAbove", - "keywords": [ - "insert cursor above", - "insert cursor" - ] - }, - { - "id": "editor.action.insertCursorBelow", - "title": "editor.action.insertCursorBelow", - "keywords": [ - "insert cursor below", - "insert cursor" - ] - }, - { - "id": "editor.action.insertCursorAtEndOfEachLineSelected", - "title": "editor.action.insertCursorAtEndOfEachLineSelected", - "keywords": [ - "insert cursor at end of each line selected", - "insert cursor" - ] - }, - { - "id": "editor.action.addSelectionToNextFindMatch", - "title": "editor.action.addSelectionToNextFindMatch", - "keywords": [ - "add selection to next find match", - "add selection" - ] - }, - { - "id": "editor.action.addSelectionToPreviousFindMatch", - "title": "editor.action.addSelectionToPreviousFindMatch", - "keywords": [ - "add selection to previous find match", - "add selection" - ] - }, - { - "id": "editor.action.moveSelectionToNextFindMatch", - "title": "editor.action.moveSelectionToNextFindMatch", - "keywords": [ - "move selection to next find match", - "move selection" - ] - }, - { - "id": "editor.action.moveSelectionToPreviousFindMatch", - "title": "editor.action.moveSelectionToPreviousFindMatch", - "keywords": [ - "move selection to previous find match", - "move selection" - ] - }, - { - "id": "editor.action.selectHighlights", - "title": "editor.action.selectHighlights", - "keywords": [ - "select highlights" - ] - }, - { - "id": "editor.action.changeAll", - "title": "editor.action.changeAll", - "keywords": [ - "change all" - ] - }, - { - "id": "editor.action.addCursorsToBottom", - "title": "editor.action.addCursorsToBottom", - "keywords": [ - "add cursors to bottom", - "add cursors" - ] - }, - { - "id": "editor.action.addCursorsToTop", - "title": "editor.action.addCursorsToTop", - "keywords": [ - "add cursors to top", - "add cursors" - ] - }, - { - "id": "editor.action.focusNextCursor", - "title": "editor.action.focusNextCursor", - "keywords": [ - "focus next cursor", - "focus next" - ] - }, - { - "id": "editor.action.focusPreviousCursor", - "title": "editor.action.focusPreviousCursor", - "keywords": [ - "focus previous cursor", - "focus previous" - ] - }, - { - "id": "editor.action.triggerParameterHints", - "title": "editor.action.triggerParameterHints", - "keywords": [ - "trigger parameter hints", - "trigger parameter" - ] - }, - { - "id": "closeParameterHints", - "title": "closeParameterHints", - "keywords": [ - "close parameter hints", - "close parameter" - ] - }, - { - "id": "showPrevParameterHint", - "title": "showPrevParameterHint", - "keywords": [ - "show prev parameter hint", - "show prev" - ] - }, - { - "id": "showNextParameterHint", - "title": "showNextParameterHint", - "keywords": [ - "show next parameter hint", - "show next" - ] - }, - { - "id": "editor.action.smartSelect.grow", - "title": "editor.action.smartSelect.grow", - "keywords": [ - "smart select grow", - "smart select" - ] - }, - { - "id": "editor.action.smartSelect.expand", - "title": "editor.action.smartSelect.expand", - "keywords": [ - "smart select expand", - "smart select" - ] - }, - { - "id": "editor.action.smartSelect.shrink", - "title": "editor.action.smartSelect.shrink", - "keywords": [ - "smart select shrink", - "smart select" - ] - }, - { - "id": "editor.action.toggleStickyScroll", - "title": "editor.action.toggleStickyScroll", - "keywords": [ - "toggle sticky scroll", - "toggle sticky" - ] - }, - { - "id": "editor.action.focusStickyScroll", - "title": "editor.action.focusStickyScroll", - "keywords": [ - "focus sticky scroll", - "focus sticky" - ] - }, - { - "id": "editor.action.selectPreviousStickyScrollLine", - "title": "editor.action.selectPreviousStickyScrollLine", - "keywords": [ - "select previous sticky scroll line", - "select previous" - ] - }, - { - "id": "editor.action.selectNextStickyScrollLine", - "title": "editor.action.selectNextStickyScrollLine", - "keywords": [ - "select next sticky scroll line", - "select next" - ] - }, - { - "id": "editor.action.goToFocusedStickyScrollLine", - "title": "editor.action.goToFocusedStickyScrollLine", - "keywords": [ - "go to focused sticky scroll line", - "go to" - ] - }, - { - "id": "editor.action.selectEditor", - "title": "editor.action.selectEditor", - "keywords": [ - "select editor" - ] - }, - { - "id": "editor.action.forceRetokenize", - "title": "editor.action.forceRetokenize", - "keywords": [ - "force retokenize" - ] - }, - { - "id": "editor.action.toggleTabFocusMode", - "title": "editor.action.toggleTabFocusMode", - "keywords": [ - "toggle tab focus mode", - "toggle tab" - ] - }, - { - "id": "editor.action.unicodeHighlight.disableHighlightingOfAmbiguousCharacters", - "title": "editor.action.unicodeHighlight.disableHighlightingOfAmbiguousCharacters", - "keywords": [ - "unicode highlight disable highlighting of ambiguous characters", - "unicode highlight" - ] - }, - { - "id": "editor.action.unicodeHighlight.disableHighlightingOfInvisibleCharacters", - "title": "editor.action.unicodeHighlight.disableHighlightingOfInvisibleCharacters", - "keywords": [ - "unicode highlight disable highlighting of invisible characters", - "unicode highlight" - ] - }, - { - "id": "editor.action.unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters", - "title": "editor.action.unicodeHighlight.disableHighlightingOfNonBasicAsciiCharacters", - "keywords": [ - "unicode highlight disable highlighting of non basic ascii characters", - "unicode highlight" - ] - }, - { - "id": "editor.action.unicodeHighlight.showExcludeOptions", - "title": "editor.action.unicodeHighlight.showExcludeOptions", - "keywords": [ - "unicode highlight show exclude options", - "unicode highlight" - ] - }, - { - "id": "editor.action.wordHighlight.next", - "title": "editor.action.wordHighlight.next", - "keywords": [ - "word highlight next", - "word highlight" - ] - }, - { - "id": "editor.action.wordHighlight.prev", - "title": "editor.action.wordHighlight.prev", - "keywords": [ - "word highlight prev", - "word highlight" - ] - }, - { - "id": "editor.action.wordHighlight.trigger", - "title": "editor.action.wordHighlight.trigger", - "keywords": [ - "word highlight trigger", - "word highlight" - ] - }, - { - "id": "cursorWordStartLeft", - "title": "cursorWordStartLeft", - "keywords": [ - "cursor word start left", - "cursor word" - ] - }, - { - "id": "cursorWordEndLeft", - "title": "cursorWordEndLeft", - "keywords": [ - "cursor word end left", - "cursor word" - ] - }, - { - "id": "cursorWordLeft", - "title": "cursorWordLeft", - "keywords": [ - "cursor word left", - "cursor word" - ] - }, - { - "id": "cursorWordStartLeftSelect", - "title": "cursorWordStartLeftSelect", - "keywords": [ - "cursor word start left select", - "cursor word" - ] - }, - { - "id": "cursorWordEndLeftSelect", - "title": "cursorWordEndLeftSelect", - "keywords": [ - "cursor word end left select", - "cursor word" - ] - }, - { - "id": "cursorWordLeftSelect", - "title": "cursorWordLeftSelect", - "keywords": [ - "cursor word left select", - "cursor word" - ] - }, - { - "id": "cursorWordStartRight", - "title": "cursorWordStartRight", - "keywords": [ - "cursor word start right", - "cursor word" - ] - }, - { - "id": "cursorWordEndRight", - "title": "cursorWordEndRight", - "keywords": [ - "cursor word end right", - "cursor word" - ] - }, - { - "id": "cursorWordRight", - "title": "cursorWordRight", - "keywords": [ - "cursor word right", - "cursor word" - ] - }, - { - "id": "cursorWordStartRightSelect", - "title": "cursorWordStartRightSelect", - "keywords": [ - "cursor word start right select", - "cursor word" - ] - }, - { - "id": "cursorWordEndRightSelect", - "title": "cursorWordEndRightSelect", - "keywords": [ - "cursor word end right select", - "cursor word" - ] - }, - { - "id": "cursorWordRightSelect", - "title": "cursorWordRightSelect", - "keywords": [ - "cursor word right select", - "cursor word" - ] - }, - { - "id": "cursorWordAccessibilityLeft", - "title": "cursorWordAccessibilityLeft", - "keywords": [ - "cursor word accessibility left", - "cursor word" - ] - }, - { - "id": "cursorWordAccessibilityLeftSelect", - "title": "cursorWordAccessibilityLeftSelect", - "keywords": [ - "cursor word accessibility left select", - "cursor word" - ] - }, - { - "id": "cursorWordAccessibilityRight", - "title": "cursorWordAccessibilityRight", - "keywords": [ - "cursor word accessibility right", - "cursor word" - ] - }, - { - "id": "cursorWordAccessibilityRightSelect", - "title": "cursorWordAccessibilityRightSelect", - "keywords": [ - "cursor word accessibility right select", - "cursor word" - ] - }, - { - "id": "deleteWordStartLeft", - "title": "deleteWordStartLeft", - "keywords": [ - "delete word start left", - "delete word" - ] - }, - { - "id": "deleteWordEndLeft", - "title": "deleteWordEndLeft", - "keywords": [ - "delete word end left", - "delete word" - ] - }, - { - "id": "deleteWordLeft", - "title": "deleteWordLeft", - "keywords": [ - "delete word left", - "delete word" - ] - }, - { - "id": "deleteWordStartRight", - "title": "deleteWordStartRight", - "keywords": [ - "delete word start right", - "delete word" - ] - }, - { - "id": "deleteWordEndRight", - "title": "deleteWordEndRight", - "keywords": [ - "delete word end right", - "delete word" - ] - }, - { - "id": "deleteWordRight", - "title": "deleteWordRight", - "keywords": [ - "delete word right", - "delete word" - ] - }, - { - "id": "deleteInsideWord", - "title": "deleteInsideWord", - "keywords": [ - "delete inside word", - "delete inside" - ] - }, - { - "id": "cursorWordPartStartLeft", - "title": "cursorWordPartStartLeft", - "keywords": [ - "cursor word part start left", - "cursor word" - ] - }, - { - "id": "cursorWordPartStartLeftSelect", - "title": "cursorWordPartStartLeftSelect", - "keywords": [ - "cursor word part start left select", - "cursor word" - ] - }, - { - "id": "deleteWordPartLeft", - "title": "deleteWordPartLeft", - "keywords": [ - "delete word part left", - "delete word" - ] - }, - { - "id": "deleteWordPartRight", - "title": "deleteWordPartRight", - "keywords": [ - "delete word part right", - "delete word" - ] - }, - { - "id": "cursorWordPartLeft", - "title": "cursorWordPartLeft", - "keywords": [ - "cursor word part left", - "cursor word" - ] - }, - { - "id": "cursorWordPartLeftSelect", - "title": "cursorWordPartLeftSelect", - "keywords": [ - "cursor word part left select", - "cursor word" - ] - }, - { - "id": "cursorWordPartRight", - "title": "cursorWordPartRight", - "keywords": [ - "cursor word part right", - "cursor word" - ] - }, - { - "id": "cursorWordPartRightSelect", - "title": "cursorWordPartRightSelect", - "keywords": [ - "cursor word part right select", - "cursor word" - ] - }, - { - "id": "workbench.action.chat.addDynamicVariable", - "title": "workbench.action.chat.addDynamicVariable", - "keywords": [ - "chat add dynamic variable", - "chat add" - ] - }, - { - "id": "files.participants.resetChoice", - "title": "files.participants.resetChoice", - "keywords": [ - "files participants reset choice", - "files participants" - ] - }, - { - "id": "workbench.extensions.action.manageAuthorizedExtensionURIs", - "title": "workbench.extensions.action.manageAuthorizedExtensionURIs", - "keywords": [ - "workbench extensions action manage authorized extension uris", - "workbench extensions" - ] - }, - { - "id": "commentsFocusViewFromFilter", - "title": "commentsFocusViewFromFilter", - "keywords": [ - "comments focus view from filter", - "comments focus" - ] - }, - { - "id": "commentsClearFilterText", - "title": "commentsClearFilterText", - "keywords": [ - "comments clear filter text", - "comments clear" - ] - }, - { - "id": "commentsFocusFilter", - "title": "commentsFocusFilter", - "keywords": [ - "comments focus filter", - "comments focus" - ] - }, - { - "id": "workbench.actions.workbench.panel.comments.toggleUnResolvedComments", - "title": "workbench.actions.workbench.panel.comments.toggleUnResolvedComments", - "keywords": [ - "workbench actions workbench panel comments toggle un resolved comments", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.comments.toggleResolvedComments", - "title": "workbench.actions.workbench.panel.comments.toggleResolvedComments", - "keywords": [ - "workbench actions workbench panel comments toggle resolved comments", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.comments.toggleSortByUpdatedAt", - "title": "workbench.actions.workbench.panel.comments.toggleSortByUpdatedAt", - "keywords": [ - "workbench actions workbench panel comments toggle sort by updated at", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.comments.toggleSortByResource", - "title": "workbench.actions.workbench.panel.comments.toggleSortByResource", - "keywords": [ - "workbench actions workbench panel comments toggle sort by resource", - "workbench actions" - ] - }, - { - "id": "workbench.action.editorDictation.start", - "title": "workbench.action.editorDictation.start", - "keywords": [ - "editor dictation start", - "editor dictation" - ] - }, - { - "id": "workbench.action.editorDictation.stop", - "title": "workbench.action.editorDictation.stop", - "keywords": [ - "editor dictation stop", - "editor dictation" - ] - }, - { - "id": "insertSnippet", - "title": "insertSnippet", - "keywords": [ - "insert snippet" - ] - }, - { - "id": "widgetNavigation.focusPrevious", - "title": "widgetNavigation.focusPrevious", - "keywords": [ - "widget navigation focus previous", - "widget navigation" - ] - }, - { - "id": "widgetNavigation.focusNext", - "title": "widgetNavigation.focusNext", - "keywords": [ - "widget navigation focus next", - "widget navigation" - ] - }, - { - "id": "editor.action.accessibleViewNext", - "title": "editor.action.accessibleViewNext", - "keywords": [ - "accessible view next", - "accessible view" - ] - }, - { - "id": "editor.action.accessibleViewNextCodeBlock", - "title": "editor.action.accessibleViewNextCodeBlock", - "keywords": [ - "accessible view next code block", - "accessible view" - ] - }, - { - "id": "editor.action.accessibleViewPreviousCodeBlock", - "title": "editor.action.accessibleViewPreviousCodeBlock", - "keywords": [ - "accessible view previous code block", - "accessible view" - ] - }, - { - "id": "editor.action.accessibleViewPrevious", - "title": "editor.action.accessibleViewPrevious", - "keywords": [ - "accessible view previous", - "accessible view" - ] - }, - { - "id": "editor.action.accessibleViewGoToSymbol", - "title": "editor.action.accessibleViewGoToSymbol", - "keywords": [ - "accessible view go to symbol", - "accessible view" - ] - }, - { - "id": "editor.action.accessibilityHelp", - "title": "editor.action.accessibilityHelp", - "keywords": [ - "accessibility help" - ] - }, - { - "id": "editor.action.accessibleView", - "title": "editor.action.accessibleView", - "keywords": [ - "accessible view" - ] - }, - { - "id": "editor.action.accessibleViewDisableHint", - "title": "editor.action.accessibleViewDisableHint", - "keywords": [ - "accessible view disable hint", - "accessible view" - ] - }, - { - "id": "editor.action.accessibilityHelpConfigureKeybindings", - "title": "editor.action.accessibilityHelpConfigureKeybindings", - "keywords": [ - "accessibility help configure keybindings", - "accessibility help" - ] - }, - { - "id": "editor.action.accessibilityHelpConfigureAssignedKeybindings", - "title": "editor.action.accessibilityHelpConfigureAssignedKeybindings", - "keywords": [ - "accessibility help configure assigned keybindings", - "accessibility help" - ] - }, - { - "id": "editor.action.accessibilityHelpOpenHelpLink", - "title": "editor.action.accessibilityHelpOpenHelpLink", - "keywords": [ - "accessibility help open help link", - "accessibility help" - ] - }, - { - "id": "editor.action.accessibleViewAcceptInlineCompletion", - "title": "editor.action.accessibleViewAcceptInlineCompletion", - "keywords": [ - "accessible view accept inline completion", - "accessible view" - ] - }, - { - "id": "views.moveViewUp", - "title": "views.moveViewUp", - "keywords": [ - "views move view up", - "views move" - ] - }, - { - "id": "views.moveViewLeft", - "title": "views.moveViewLeft", - "keywords": [ - "views move view left", - "views move" - ] - }, - { - "id": "views.moveViewDown", - "title": "views.moveViewDown", - "keywords": [ - "views move view down", - "views move" - ] - }, - { - "id": "views.moveViewRight", - "title": "views.moveViewRight", - "keywords": [ - "views move view right", - "views move" - ] - }, - { - "id": "vscode.moveViews", - "title": "vscode.moveViews", - "keywords": [ - "vscode move views", - "vscode move" - ] - }, - { - "id": "workbench.scm.action.graph.addHistoryItemToChat", - "title": "workbench.scm.action.graph.addHistoryItemToChat", - "keywords": [ - "workbench scm action graph add history item to chat", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.summarizeHistoryItem", - "title": "workbench.scm.action.graph.summarizeHistoryItem", - "keywords": [ - "workbench scm action graph summarize history item", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.addHistoryItemChangeToChat", - "title": "workbench.scm.action.graph.addHistoryItemChangeToChat", - "keywords": [ - "workbench scm action graph add history item change to chat", - "workbench scm" - ] - }, - { - "id": "workbench.action.chat.toggleDefaultVisibility", - "title": "workbench.action.chat.toggleDefaultVisibility", - "keywords": [ - "chat toggle default visibility", - "chat toggle" - ] - }, - { - "id": "workbench.action.chat.editToolApproval", - "title": "workbench.action.chat.editToolApproval", - "keywords": [ - "chat edit tool approval", - "chat edit" - ] - }, - { - "id": "workbench.action.toggleAuxiliaryBar", - "title": "workbench.action.toggleAuxiliaryBar", - "keywords": [ - "toggle auxiliary bar", - "toggle auxiliary" - ] - }, - { - "id": "workbench.action.closeAuxiliaryBar", - "title": "workbench.action.closeAuxiliaryBar", - "keywords": [ - "close auxiliary bar", - "close auxiliary" - ] - }, - { - "id": "workbench.action.focusAuxiliaryBar", - "title": "workbench.action.focusAuxiliaryBar", - "keywords": [ - "focus auxiliary bar", - "focus auxiliary" - ] - }, - { - "id": "workbench.action.previousAuxiliaryBarView", - "title": "workbench.action.previousAuxiliaryBarView", - "keywords": [ - "previous auxiliary bar view", - "previous auxiliary" - ] - }, - { - "id": "workbench.action.nextAuxiliaryBarView", - "title": "workbench.action.nextAuxiliaryBarView", - "keywords": [ - "next auxiliary bar view", - "next auxiliary" - ] - }, - { - "id": "workbench.action.maximizeAuxiliaryBar", - "title": "workbench.action.maximizeAuxiliaryBar", - "keywords": [ - "maximize auxiliary bar", - "maximize auxiliary" - ] - }, - { - "id": "workbench.action.restoreAuxiliaryBar", - "title": "workbench.action.restoreAuxiliaryBar", - "keywords": [ - "restore auxiliary bar", - "restore auxiliary" - ] - }, - { - "id": "workbench.action.toggleMaximizedAuxiliaryBar", - "title": "workbench.action.toggleMaximizedAuxiliaryBar", - "keywords": [ - "toggle maximized auxiliary bar", - "toggle maximized" - ] - }, - { - "id": "workbench.action.togglePanel", - "title": "workbench.action.togglePanel", - "keywords": [ - "toggle panel" - ] - }, - { - "id": "workbench.action.closePanel", - "title": "workbench.action.closePanel", - "keywords": [ - "close panel" - ] - }, - { - "id": "workbench.action.focusPanel", - "title": "workbench.action.focusPanel", - "keywords": [ - "focus panel" - ] - }, - { - "id": "workbench.action.positionPanelTop", - "title": "workbench.action.positionPanelTop", - "keywords": [ - "position panel top", - "position panel" - ] - }, - { - "id": "workbench.action.positionPanelLeft", - "title": "workbench.action.positionPanelLeft", - "keywords": [ - "position panel left", - "position panel" - ] - }, - { - "id": "workbench.action.positionPanelRight", - "title": "workbench.action.positionPanelRight", - "keywords": [ - "position panel right", - "position panel" - ] - }, - { - "id": "workbench.action.positionPanelBottom", - "title": "workbench.action.positionPanelBottom", - "keywords": [ - "position panel bottom", - "position panel" - ] - }, - { - "id": "workbench.action.alignPanelLeft", - "title": "workbench.action.alignPanelLeft", - "keywords": [ - "align panel left", - "align panel" - ] - }, - { - "id": "workbench.action.alignPanelRight", - "title": "workbench.action.alignPanelRight", - "keywords": [ - "align panel right", - "align panel" - ] - }, - { - "id": "workbench.action.alignPanelCenter", - "title": "workbench.action.alignPanelCenter", - "keywords": [ - "align panel center", - "align panel" - ] - }, - { - "id": "workbench.action.alignPanelJustify", - "title": "workbench.action.alignPanelJustify", - "keywords": [ - "align panel justify", - "align panel" - ] - }, - { - "id": "workbench.action.previousPanelView", - "title": "workbench.action.previousPanelView", - "keywords": [ - "previous panel view", - "previous panel" - ] - }, - { - "id": "workbench.action.nextPanelView", - "title": "workbench.action.nextPanelView", - "keywords": [ - "next panel view", - "next panel" - ] - }, - { - "id": "workbench.action.toggleMaximizedPanel", - "title": "workbench.action.toggleMaximizedPanel", - "keywords": [ - "toggle maximized panel", - "toggle maximized" - ] - }, - { - "id": "workbench.action.movePanelToSidePanel", - "title": "workbench.action.movePanelToSidePanel", - "keywords": [ - "move panel to side panel", - "move panel" - ] - }, - { - "id": "workbench.action.movePanelToSecondarySideBar", - "title": "workbench.action.movePanelToSecondarySideBar", - "keywords": [ - "move panel to secondary side bar", - "move panel" - ] - }, - { - "id": "workbench.action.moveSidePanelToPanel", - "title": "workbench.action.moveSidePanelToPanel", - "keywords": [ - "move side panel to panel", - "move side" - ] - }, - { - "id": "workbench.action.moveSecondarySideBarToPanel", - "title": "workbench.action.moveSecondarySideBarToPanel", - "keywords": [ - "move secondary side bar to panel", - "move secondary" - ] - }, - { - "id": "workbench.action.toggleCenteredLayout", - "title": "workbench.action.toggleCenteredLayout", - "keywords": [ - "toggle centered layout", - "toggle centered" - ] - }, - { - "id": "workbench.action.moveSideBarRight", - "title": "workbench.action.moveSideBarRight", - "keywords": [ - "move side bar right", - "move side" - ] - }, - { - "id": "workbench.action.moveSideBarLeft", - "title": "workbench.action.moveSideBarLeft", - "keywords": [ - "move side bar left", - "move side" - ] - }, - { - "id": "workbench.action.toggleSidebarPosition", - "title": "workbench.action.toggleSidebarPosition", - "keywords": [ - "toggle sidebar position", - "toggle sidebar" - ] - }, - { - "id": "workbench.action.toggleEditorVisibility", - "title": "workbench.action.toggleEditorVisibility", - "keywords": [ - "toggle editor visibility", - "toggle editor" - ] - }, - { - "id": "workbench.action.toggleSidebarVisibility", - "title": "workbench.action.toggleSidebarVisibility", - "keywords": [ - "toggle sidebar visibility", - "toggle sidebar" - ] - }, - { - "id": "workbench.action.toggleStatusbarVisibility", - "title": "workbench.action.toggleStatusbarVisibility", - "keywords": [ - "toggle statusbar visibility", - "toggle statusbar" - ] - }, - { - "id": "workbench.action.hideEditorTabs", - "title": "workbench.action.hideEditorTabs", - "keywords": [ - "hide editor tabs", - "hide editor" - ] - }, - { - "id": "workbench.action.showMultipleEditorTabs", - "title": "workbench.action.showMultipleEditorTabs", - "keywords": [ - "show multiple editor tabs", - "show multiple" - ] - }, - { - "id": "workbench.action.showEditorTab", - "title": "workbench.action.showEditorTab", - "keywords": [ - "show editor tab", - "show editor" - ] - }, - { - "id": "workbench.action.editorActionsTitleBar", - "title": "workbench.action.editorActionsTitleBar", - "keywords": [ - "editor actions title bar", - "editor actions" - ] - }, - { - "id": "workbench.action.editorActionsDefault", - "title": "workbench.action.editorActionsDefault", - "keywords": [ - "editor actions default", - "editor actions" - ] - }, - { - "id": "workbench.action.hideEditorActions", - "title": "workbench.action.hideEditorActions", - "keywords": [ - "hide editor actions", - "hide editor" - ] - }, - { - "id": "workbench.action.showEditorActions", - "title": "workbench.action.showEditorActions", - "keywords": [ - "show editor actions", - "show editor" - ] - }, - { - "id": "workbench.action.configureEditorTabs", - "title": "workbench.action.configureEditorTabs", - "keywords": [ - "configure editor tabs", - "configure editor" - ] - }, - { - "id": "workbench.action.configureEditor", - "title": "workbench.action.configureEditor", - "keywords": [ - "configure editor" - ] - }, - { - "id": "workbench.action.toggleSeparatePinnedEditorTabs", - "title": "workbench.action.toggleSeparatePinnedEditorTabs", - "keywords": [ - "toggle separate pinned editor tabs", - "toggle separate" - ] - }, - { - "id": "workbench.action.toggleMenuBar", - "title": "workbench.action.toggleMenuBar", - "keywords": [ - "toggle menu bar", - "toggle menu" - ] - }, - { - "id": "workbench.action.resetViewLocations", - "title": "workbench.action.resetViewLocations", - "keywords": [ - "reset view locations", - "reset view" - ] - }, - { - "id": "workbench.action.moveView", - "title": "workbench.action.moveView", - "keywords": [ - "move view" - ] - }, - { - "id": "workbench.action.moveFocusedView", - "title": "workbench.action.moveFocusedView", - "keywords": [ - "move focused view", - "move focused" - ] - }, - { - "id": "workbench.action.resetFocusedViewLocation", - "title": "workbench.action.resetFocusedViewLocation", - "keywords": [ - "reset focused view location", - "reset focused" - ] - }, - { - "id": "workbench.action.increaseViewSize", - "title": "workbench.action.increaseViewSize", - "keywords": [ - "increase view size", - "increase view" - ] - }, - { - "id": "workbench.action.increaseViewWidth", - "title": "workbench.action.increaseViewWidth", - "keywords": [ - "increase view width", - "increase view" - ] - }, - { - "id": "workbench.action.increaseViewHeight", - "title": "workbench.action.increaseViewHeight", - "keywords": [ - "increase view height", - "increase view" - ] - }, - { - "id": "workbench.action.decreaseViewSize", - "title": "workbench.action.decreaseViewSize", - "keywords": [ - "decrease view size", - "decrease view" - ] - }, - { - "id": "workbench.action.decreaseViewWidth", - "title": "workbench.action.decreaseViewWidth", - "keywords": [ - "decrease view width", - "decrease view" - ] - }, - { - "id": "workbench.action.decreaseViewHeight", - "title": "workbench.action.decreaseViewHeight", - "keywords": [ - "decrease view height", - "decrease view" - ] - }, - { - "id": "workbench.action.alignQuickInputTop", - "title": "workbench.action.alignQuickInputTop", - "keywords": [ - "align quick input top", - "align quick" - ] - }, - { - "id": "workbench.action.alignQuickInputCenter", - "title": "workbench.action.alignQuickInputCenter", - "keywords": [ - "align quick input center", - "align quick" - ] - }, - { - "id": "workbench.action.customizeLayout", - "title": "workbench.action.customizeLayout", - "keywords": [ - "customize layout" - ] - }, - { - "id": "workbench.action.zenHideEditorTabs", - "title": "workbench.action.zenHideEditorTabs", - "keywords": [ - "zen hide editor tabs", - "zen hide" - ] - }, - { - "id": "workbench.action.zenShowMultipleEditorTabs", - "title": "workbench.action.zenShowMultipleEditorTabs", - "keywords": [ - "zen show multiple editor tabs", - "zen show" - ] - }, - { - "id": "workbench.action.zenShowEditorTab", - "title": "workbench.action.zenShowEditorTab", - "keywords": [ - "zen show editor tab", - "zen show" - ] - }, - { - "id": "workbench.action.toggleZenMode", - "title": "workbench.action.toggleZenMode", - "keywords": [ - "toggle zen mode", - "toggle zen" - ] - }, - { - "id": "workbench.action.exitZenMode", - "title": "workbench.action.exitZenMode", - "keywords": [ - "exit zen mode", - "exit zen" - ] - }, - { - "id": "workbench.action.inspectContextKeys", - "title": "workbench.action.inspectContextKeys", - "keywords": [ - "inspect context keys", - "inspect context" - ] - }, - { - "id": "workbench.action.toggleScreencastMode", - "title": "workbench.action.toggleScreencastMode", - "keywords": [ - "toggle screencast mode", - "toggle screencast" - ] - }, - { - "id": "workbench.action.logStorage", - "title": "workbench.action.logStorage", - "keywords": [ - "log storage" - ] - }, - { - "id": "workbench.action.logWorkingCopies", - "title": "workbench.action.logWorkingCopies", - "keywords": [ - "log working copies", - "log working" - ] - }, - { - "id": "workbench.action.removeLargeStorageDatabaseEntries", - "title": "workbench.action.removeLargeStorageDatabaseEntries", - "keywords": [ - "remove large storage database entries", - "remove large" - ] - }, - { - "id": "workbench.action.showPolicyDiagnostics", - "title": "workbench.action.showPolicyDiagnostics", - "keywords": [ - "show policy diagnostics", - "show policy" - ] - }, - { - "id": "workbench.action.syncAccountPolicy", - "title": "workbench.action.syncAccountPolicy", - "keywords": [ - "sync account policy", - "sync account" - ] - }, - { - "id": "workbench.action.keybindingsReference", - "title": "workbench.action.keybindingsReference", - "keywords": [ - "keybindings reference" - ] - }, - { - "id": "workbench.action.openVideoTutorialsUrl", - "title": "workbench.action.openVideoTutorialsUrl", - "keywords": [ - "open video tutorials url", - "open video" - ] - }, - { - "id": "workbench.action.openTipsAndTricksUrl", - "title": "workbench.action.openTipsAndTricksUrl", - "keywords": [ - "open tips and tricks url", - "open tips" - ] - }, - { - "id": "workbench.action.openDocumentationUrl", - "title": "workbench.action.openDocumentationUrl", - "keywords": [ - "open documentation url", - "open documentation" - ] - }, - { - "id": "workbench.action.openNewsletterSignupUrl", - "title": "workbench.action.openNewsletterSignupUrl", - "keywords": [ - "open newsletter signup url", - "open newsletter" - ] - }, - { - "id": "workbench.action.openYouTubeUrl", - "title": "workbench.action.openYouTubeUrl", - "keywords": [ - "open you tube url", - "open you" - ] - }, - { - "id": "workbench.action.openRequestFeatureUrl", - "title": "workbench.action.openRequestFeatureUrl", - "keywords": [ - "open request feature url", - "open request" - ] - }, - { - "id": "workbench.action.openLicenseUrl", - "title": "workbench.action.openLicenseUrl", - "keywords": [ - "open license url", - "open license" - ] - }, - { - "id": "workbench.action.openPrivacyStatementUrl", - "title": "workbench.action.openPrivacyStatementUrl", - "keywords": [ - "open privacy statement url", - "open privacy" - ] - }, - { - "id": "workbench.action.getStartedWithAccessibilityFeatures", - "title": "workbench.action.getStartedWithAccessibilityFeatures", - "keywords": [ - "get started with accessibility features", - "get started" - ] - }, - { - "id": "workbench.action.askVScode", - "title": "workbench.action.askVScode", - "keywords": [ - "ask vscode" - ] - }, - { - "id": "list.focusDown", - "title": "list.focusDown", - "keywords": [ - "list focus down", - "list focus" - ] - }, - { - "id": "list.focusUp", - "title": "list.focusUp", - "keywords": [ - "list focus up", - "list focus" - ] - }, - { - "id": "list.focusAnyDown", - "title": "list.focusAnyDown", - "keywords": [ - "list focus any down", - "list focus" - ] - }, - { - "id": "list.focusAnyUp", - "title": "list.focusAnyUp", - "keywords": [ - "list focus any up", - "list focus" - ] - }, - { - "id": "list.focusPageDown", - "title": "list.focusPageDown", - "keywords": [ - "list focus page down", - "list focus" - ] - }, - { - "id": "list.focusPageUp", - "title": "list.focusPageUp", - "keywords": [ - "list focus page up", - "list focus" - ] - }, - { - "id": "list.focusFirst", - "title": "list.focusFirst", - "keywords": [ - "list focus first", - "list focus" - ] - }, - { - "id": "list.focusLast", - "title": "list.focusLast", - "keywords": [ - "list focus last", - "list focus" - ] - }, - { - "id": "list.focusAnyFirst", - "title": "list.focusAnyFirst", - "keywords": [ - "list focus any first", - "list focus" - ] - }, - { - "id": "list.focusAnyLast", - "title": "list.focusAnyLast", - "keywords": [ - "list focus any last", - "list focus" - ] - }, - { - "id": "list.expandSelectionDown", - "title": "list.expandSelectionDown", - "keywords": [ - "list expand selection down", - "list expand" - ] - }, - { - "id": "list.expandSelectionUp", - "title": "list.expandSelectionUp", - "keywords": [ - "list expand selection up", - "list expand" - ] - }, - { - "id": "list.collapse", - "title": "list.collapse", - "keywords": [ - "list collapse" - ] - }, - { - "id": "list.stickyScroll.collapse", - "title": "list.stickyScroll.collapse", - "keywords": [ - "list sticky scroll collapse", - "list sticky" - ] - }, - { - "id": "list.collapseAll", - "title": "list.collapseAll", - "keywords": [ - "list collapse all", - "list collapse" - ] - }, - { - "id": "list.collapseAllToFocus", - "title": "list.collapseAllToFocus", - "keywords": [ - "list collapse all to focus", - "list collapse" - ] - }, - { - "id": "list.focusParent", - "title": "list.focusParent", - "keywords": [ - "list focus parent", - "list focus" - ] - }, - { - "id": "list.expand", - "title": "list.expand", - "keywords": [ - "list expand" - ] - }, - { - "id": "list.select", - "title": "list.select", - "keywords": [ - "list select" - ] - }, - { - "id": "list.stickyScrollselect", - "title": "list.stickyScrollselect", - "keywords": [ - "list sticky scrollselect", - "list sticky" - ] - }, - { - "id": "list.selectAndPreserveFocus", - "title": "list.selectAndPreserveFocus", - "keywords": [ - "list select and preserve focus", - "list select" - ] - }, - { - "id": "list.selectAll", - "title": "list.selectAll", - "keywords": [ - "list select all", - "list select" - ] - }, - { - "id": "list.toggleSelection", - "title": "list.toggleSelection", - "keywords": [ - "list toggle selection", - "list toggle" - ] - }, - { - "id": "list.showHover", - "title": "list.showHover", - "keywords": [ - "list show hover", - "list show" - ] - }, - { - "id": "list.toggleExpand", - "title": "list.toggleExpand", - "keywords": [ - "list toggle expand", - "list toggle" - ] - }, - { - "id": "list.stickyScrolltoggleExpand", - "title": "list.stickyScrolltoggleExpand", - "keywords": [ - "list sticky scrolltoggle expand", - "list sticky" - ] - }, - { - "id": "list.clear", - "title": "list.clear", - "keywords": [ - "list clear" - ] - }, - { - "id": "list.triggerTypeNavigation", - "title": "list.triggerTypeNavigation", - "keywords": [ - "list trigger type navigation", - "list trigger" - ] - }, - { - "id": "list.toggleFindMode", - "title": "list.toggleFindMode", - "keywords": [ - "list toggle find mode", - "list toggle" - ] - }, - { - "id": "list.toggleFindMatchType", - "title": "list.toggleFindMatchType", - "keywords": [ - "list toggle find match type", - "list toggle" - ] - }, - { - "id": "list.toggleKeyboardNavigation", - "title": "list.toggleKeyboardNavigation", - "keywords": [ - "list toggle keyboard navigation", - "list toggle" - ] - }, - { - "id": "list.toggleFilterOnType", - "title": "list.toggleFilterOnType", - "keywords": [ - "list toggle filter on type", - "list toggle" - ] - }, - { - "id": "list.find", - "title": "list.find", - "keywords": [ - "list find" - ] - }, - { - "id": "list.closeFind", - "title": "list.closeFind", - "keywords": [ - "list close find", - "list close" - ] - }, - { - "id": "list.scrollUp", - "title": "list.scrollUp", - "keywords": [ - "list scroll up", - "list scroll" - ] - }, - { - "id": "list.scrollDown", - "title": "list.scrollDown", - "keywords": [ - "list scroll down", - "list scroll" - ] - }, - { - "id": "list.scrollLeft", - "title": "list.scrollLeft", - "keywords": [ - "list scroll left", - "list scroll" - ] - }, - { - "id": "list.scrollRight", - "title": "list.scrollRight", - "keywords": [ - "list scroll right", - "list scroll" - ] - }, - { - "id": "tree.toggleStickyScroll", - "title": "tree.toggleStickyScroll", - "keywords": [ - "tree toggle sticky scroll", - "tree toggle" - ] - }, - { - "id": "workbench.action.navigateLeft", - "title": "workbench.action.navigateLeft", - "keywords": [ - "navigate left" - ] - }, - { - "id": "workbench.action.navigateRight", - "title": "workbench.action.navigateRight", - "keywords": [ - "navigate right" - ] - }, - { - "id": "workbench.action.navigateUp", - "title": "workbench.action.navigateUp", - "keywords": [ - "navigate up" - ] - }, - { - "id": "workbench.action.navigateDown", - "title": "workbench.action.navigateDown", - "keywords": [ - "navigate down" - ] - }, - { - "id": "workbench.action.focusNextPart", - "title": "workbench.action.focusNextPart", - "keywords": [ - "focus next part", - "focus next" - ] - }, - { - "id": "workbench.action.focusPreviousPart", - "title": "workbench.action.focusPreviousPart", - "keywords": [ - "focus previous part", - "focus previous" - ] - }, - { - "id": "workbench.action.newWindow", - "title": "workbench.action.newWindow", - "keywords": [ - "new window" - ] - }, - { - "id": "workbench.action.toggleFullScreen", - "title": "workbench.action.toggleFullScreen", - "keywords": [ - "toggle full screen", - "toggle full" - ] - }, - { - "id": "workbench.action.quickOpenRecent", - "title": "workbench.action.quickOpenRecent", - "keywords": [ - "quick open recent", - "quick open" - ] - }, - { - "id": "workbench.action.openRecent", - "title": "workbench.action.openRecent", - "keywords": [ - "open recent" - ] - }, - { - "id": "workbench.action.reloadWindow", - "title": "workbench.action.reloadWindow", - "keywords": [ - "reload window" - ] - }, - { - "id": "workbench.action.showAboutDialog", - "title": "workbench.action.showAboutDialog", - "keywords": [ - "show about dialog", - "show about" - ] - }, - { - "id": "workbench.action.blur", - "title": "workbench.action.blur", - "keywords": [ - "blur" - ] - }, - { - "id": "workbench.action.quickOpenNavigateNextInRecentFilesPicker", - "title": "workbench.action.quickOpenNavigateNextInRecentFilesPicker", - "keywords": [ - "quick open navigate next in recent files picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigatePreviousInRecentFilesPicker", - "title": "workbench.action.quickOpenNavigatePreviousInRecentFilesPicker", - "keywords": [ - "quick open navigate previous in recent files picker", - "quick open" - ] - }, - { - "id": "workbench.action.toggleConfirmBeforeClose", - "title": "workbench.action.toggleConfirmBeforeClose", - "keywords": [ - "toggle confirm before close", - "toggle confirm" - ] - }, - { - "id": "workbench.action.files.openFileFolderInNewWindow", - "title": "workbench.action.files.openFileFolderInNewWindow", - "keywords": [ - "files open file folder in new window", - "files open" - ] - }, - { - "id": "workbench.action.files.openFolderInNewWindow", - "title": "workbench.action.files.openFolderInNewWindow", - "keywords": [ - "files open folder in new window", - "files open" - ] - }, - { - "id": "workbench.action.files.openFileInNewWindow", - "title": "workbench.action.files.openFileInNewWindow", - "keywords": [ - "files open file in new window", - "files open" - ] - }, - { - "id": "workbench.action.openWorkspaceInNewWindow", - "title": "workbench.action.openWorkspaceInNewWindow", - "keywords": [ - "open workspace in new window", - "open workspace" - ] - }, - { - "id": "addRootFolder", - "title": "addRootFolder", - "keywords": [ - "add root folder", - "add root" - ] - }, - { - "id": "setRootFolder", - "title": "setRootFolder", - "keywords": [ - "set root folder", - "set root" - ] - }, - { - "id": "vscode.openFolder", - "title": "vscode.openFolder", - "keywords": [ - "vscode open folder", - "vscode open" - ] - }, - { - "id": "vscode.newWindow", - "title": "vscode.newWindow", - "keywords": [ - "vscode new window", - "vscode new" - ] - }, - { - "id": "vscode.removeFromRecentlyOpened", - "title": "vscode.removeFromRecentlyOpened", - "keywords": [ - "vscode remove from recently opened", - "vscode remove" - ] - }, - { - "id": "workbench.action.addRootFolder", - "title": "workbench.action.addRootFolder", - "keywords": [ - "add root folder", - "add root" - ] - }, - { - "id": "workbench.action.removeRootFolder", - "title": "workbench.action.removeRootFolder", - "keywords": [ - "remove root folder", - "remove root" - ] - }, - { - "id": "workbench.action.files.openFile", - "title": "workbench.action.files.openFile", - "keywords": [ - "files open file", - "files open" - ] - }, - { - "id": "workbench.action.files.openFolder", - "title": "workbench.action.files.openFolder", - "keywords": [ - "files open folder", - "files open" - ] - }, - { - "id": "workbench.action.files.openFolderViaWorkspace", - "title": "workbench.action.files.openFolderViaWorkspace", - "keywords": [ - "files open folder via workspace", - "files open" - ] - }, - { - "id": "workbench.action.files.openFileFolder", - "title": "workbench.action.files.openFileFolder", - "keywords": [ - "files open file folder", - "files open" - ] - }, - { - "id": "workbench.action.openWorkspace", - "title": "workbench.action.openWorkspace", - "keywords": [ - "open workspace" - ] - }, - { - "id": "workbench.action.openWorkspaceConfigFile", - "title": "workbench.action.openWorkspaceConfigFile", - "keywords": [ - "open workspace config file", - "open workspace" - ] - }, - { - "id": "workbench.action.closeFolder", - "title": "workbench.action.closeFolder", - "keywords": [ - "close folder" - ] - }, - { - "id": "workbench.action.saveWorkspaceAs", - "title": "workbench.action.saveWorkspaceAs", - "keywords": [ - "save workspace as", - "save workspace" - ] - }, - { - "id": "workbench.action.duplicateWorkspaceInNewWindow", - "title": "workbench.action.duplicateWorkspaceInNewWindow", - "keywords": [ - "duplicate workspace in new window", - "duplicate workspace" - ] - }, - { - "id": "workbench.action.closeQuickOpen", - "title": "workbench.action.closeQuickOpen", - "keywords": [ - "close quick open", - "close quick" - ] - }, - { - "id": "workbench.action.acceptSelectedQuickOpenItem", - "title": "workbench.action.acceptSelectedQuickOpenItem", - "keywords": [ - "accept selected quick open item", - "accept selected" - ] - }, - { - "id": "workbench.action.alternativeAcceptSelectedQuickOpenItem", - "title": "workbench.action.alternativeAcceptSelectedQuickOpenItem", - "keywords": [ - "alternative accept selected quick open item", - "alternative accept" - ] - }, - { - "id": "workbench.action.focusQuickOpen", - "title": "workbench.action.focusQuickOpen", - "keywords": [ - "focus quick open", - "focus quick" - ] - }, - { - "id": "workbench.action.quickOpenNavigateNextInFilePicker", - "title": "workbench.action.quickOpenNavigateNextInFilePicker", - "keywords": [ - "quick open navigate next in file picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigatePreviousInFilePicker", - "title": "workbench.action.quickOpenNavigatePreviousInFilePicker", - "keywords": [ - "quick open navigate previous in file picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickPickManyToggle", - "title": "workbench.action.quickPickManyToggle", - "keywords": [ - "quick pick many toggle", - "quick pick" - ] - }, - { - "id": "workbench.action.quickInputBack", - "title": "workbench.action.quickInputBack", - "keywords": [ - "quick input back", - "quick input" - ] - }, - { - "id": "workbench.action.quickOpen", - "title": "workbench.action.quickOpen", - "keywords": [ - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenWithModes", - "title": "workbench.action.quickOpenWithModes", - "keywords": [ - "quick open with modes", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenPreviousEditor", - "title": "workbench.action.quickOpenPreviousEditor", - "keywords": [ - "quick open previous editor", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenSelectNext", - "title": "workbench.action.quickOpenSelectNext", - "keywords": [ - "quick open select next", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenSelectPrevious", - "title": "workbench.action.quickOpenSelectPrevious", - "keywords": [ - "quick open select previous", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigateNext", - "title": "workbench.action.quickOpenNavigateNext", - "keywords": [ - "quick open navigate next", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigatePrevious", - "title": "workbench.action.quickOpenNavigatePrevious", - "keywords": [ - "quick open navigate previous", - "quick open" - ] - }, - { - "id": "remote.tunnel.label", - "title": "remote.tunnel.label", - "keywords": [ - "remote tunnel label", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.forwardInline", - "title": "remote.tunnel.forwardInline", - "keywords": [ - "remote tunnel forward inline", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.forwardCommandPalette", - "title": "remote.tunnel.forwardCommandPalette", - "keywords": [ - "remote tunnel forward command palette", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.closeInline", - "title": "remote.tunnel.closeInline", - "keywords": [ - "remote tunnel close inline", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.closeCommandPalette", - "title": "remote.tunnel.closeCommandPalette", - "keywords": [ - "remote tunnel close command palette", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.open", - "title": "remote.tunnel.open", - "keywords": [ - "remote tunnel open", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.openPreview", - "title": "remote.tunnel.openPreview", - "keywords": [ - "remote tunnel open preview", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.openCommandPalette", - "title": "remote.tunnel.openCommandPalette", - "keywords": [ - "remote tunnel open command palette", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.copyAddressInline", - "title": "remote.tunnel.copyAddressInline", - "keywords": [ - "remote tunnel copy address inline", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.copyAddressCommandPalette", - "title": "remote.tunnel.copyAddressCommandPalette", - "keywords": [ - "remote tunnel copy address command palette", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.changeLocalPort", - "title": "remote.tunnel.changeLocalPort", - "keywords": [ - "remote tunnel change local port", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.setProtocolHttp", - "title": "remote.tunnel.setProtocolHttp", - "keywords": [ - "remote tunnel set protocol http", - "remote tunnel" - ] - }, - { - "id": "remote.tunnel.setProtocolHttps", - "title": "remote.tunnel.setProtocolHttps", - "keywords": [ - "remote tunnel set protocol https", - "remote tunnel" - ] - }, - { - "id": "workbench.action.editor.changeLanguageMode", - "title": "workbench.action.editor.changeLanguageMode", - "keywords": [ - "editor change language mode", - "editor change" - ] - }, - { - "id": "workbench.action.editor.changeEOL", - "title": "workbench.action.editor.changeEOL", - "keywords": [ - "editor change eol", - "editor change" - ] - }, - { - "id": "workbench.action.editor.changeEncoding", - "title": "workbench.action.editor.changeEncoding", - "keywords": [ - "editor change encoding", - "editor change" - ] - }, - { - "id": "workbench.action.navigateForward", - "title": "workbench.action.navigateForward", - "keywords": [ - "navigate forward" - ] - }, - { - "id": "workbench.action.navigateBack", - "title": "workbench.action.navigateBack", - "keywords": [ - "navigate back" - ] - }, - { - "id": "workbench.action.nextEditor", - "title": "workbench.action.nextEditor", - "keywords": [ - "next editor" - ] - }, - { - "id": "workbench.action.previousEditor", - "title": "workbench.action.previousEditor", - "keywords": [ - "previous editor" - ] - }, - { - "id": "workbench.action.nextEditorInGroup", - "title": "workbench.action.nextEditorInGroup", - "keywords": [ - "next editor in group", - "next editor" - ] - }, - { - "id": "workbench.action.previousEditorInGroup", - "title": "workbench.action.previousEditorInGroup", - "keywords": [ - "previous editor in group", - "previous editor" - ] - }, - { - "id": "workbench.action.firstEditorInGroup", - "title": "workbench.action.firstEditorInGroup", - "keywords": [ - "first editor in group", - "first editor" - ] - }, - { - "id": "workbench.action.lastEditorInGroup", - "title": "workbench.action.lastEditorInGroup", - "keywords": [ - "last editor in group", - "last editor" - ] - }, - { - "id": "workbench.action.openNextRecentlyUsedEditor", - "title": "workbench.action.openNextRecentlyUsedEditor", - "keywords": [ - "open next recently used editor", - "open next" - ] - }, - { - "id": "workbench.action.openPreviousRecentlyUsedEditor", - "title": "workbench.action.openPreviousRecentlyUsedEditor", - "keywords": [ - "open previous recently used editor", - "open previous" - ] - }, - { - "id": "workbench.action.openNextRecentlyUsedEditorInGroup", - "title": "workbench.action.openNextRecentlyUsedEditorInGroup", - "keywords": [ - "open next recently used editor in group", - "open next" - ] - }, - { - "id": "workbench.action.openPreviousRecentlyUsedEditorInGroup", - "title": "workbench.action.openPreviousRecentlyUsedEditorInGroup", - "keywords": [ - "open previous recently used editor in group", - "open previous" - ] - }, - { - "id": "workbench.action.reopenClosedEditor", - "title": "workbench.action.reopenClosedEditor", - "keywords": [ - "reopen closed editor", - "reopen closed" - ] - }, - { - "id": "workbench.action.clearRecentFiles", - "title": "workbench.action.clearRecentFiles", - "keywords": [ - "clear recent files", - "clear recent" - ] - }, - { - "id": "workbench.action.showAllEditors", - "title": "workbench.action.showAllEditors", - "keywords": [ - "show all editors", - "show all" - ] - }, - { - "id": "workbench.action.showAllEditorsByMostRecentlyUsed", - "title": "workbench.action.showAllEditorsByMostRecentlyUsed", - "keywords": [ - "show all editors by most recently used", - "show all" - ] - }, - { - "id": "workbench.action.showEditorsInActiveGroup", - "title": "workbench.action.showEditorsInActiveGroup", - "keywords": [ - "show editors in active group", - "show editors" - ] - }, - { - "id": "workbench.action.closeAllEditors", - "title": "workbench.action.closeAllEditors", - "keywords": [ - "close all editors", - "close all" - ] - }, - { - "id": "workbench.action.closeAllGroups", - "title": "workbench.action.closeAllGroups", - "keywords": [ - "close all groups", - "close all" - ] - }, - { - "id": "workbench.action.closeEditorsToTheLeft", - "title": "workbench.action.closeEditorsToTheLeft", - "keywords": [ - "close editors to the left", - "close editors" - ] - }, - { - "id": "workbench.action.closeEditorsInOtherGroups", - "title": "workbench.action.closeEditorsInOtherGroups", - "keywords": [ - "close editors in other groups", - "close editors" - ] - }, - { - "id": "workbench.action.closeEditorInAllGroups", - "title": "workbench.action.closeEditorInAllGroups", - "keywords": [ - "close editor in all groups", - "close editor" - ] - }, - { - "id": "workbench.action.revertAndCloseActiveEditor", - "title": "workbench.action.revertAndCloseActiveEditor", - "keywords": [ - "revert and close active editor", - "revert and" - ] - }, - { - "id": "workbench.action.splitEditor", - "title": "workbench.action.splitEditor", - "keywords": [ - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorOrthogonal", - "title": "workbench.action.splitEditorOrthogonal", - "keywords": [ - "split editor orthogonal", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorLeft", - "title": "workbench.action.splitEditorLeft", - "keywords": [ - "split editor left", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorRight", - "title": "workbench.action.splitEditorRight", - "keywords": [ - "split editor right", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorUp", - "title": "workbench.action.splitEditorUp", - "keywords": [ - "split editor up", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorDown", - "title": "workbench.action.splitEditorDown", - "keywords": [ - "split editor down", - "split editor" - ] - }, - { - "id": "workbench.action.joinTwoGroups", - "title": "workbench.action.joinTwoGroups", - "keywords": [ - "join two groups", - "join two" - ] - }, - { - "id": "workbench.action.joinAllGroups", - "title": "workbench.action.joinAllGroups", - "keywords": [ - "join all groups", - "join all" - ] - }, - { - "id": "workbench.action.navigateEditorGroups", - "title": "workbench.action.navigateEditorGroups", - "keywords": [ - "navigate editor groups", - "navigate editor" - ] - }, - { - "id": "workbench.action.evenEditorWidths", - "title": "workbench.action.evenEditorWidths", - "keywords": [ - "even editor widths", - "even editor" - ] - }, - { - "id": "workbench.action.toggleEditorWidths", - "title": "workbench.action.toggleEditorWidths", - "keywords": [ - "toggle editor widths", - "toggle editor" - ] - }, - { - "id": "workbench.action.maximizeEditorHideSidebar", - "title": "workbench.action.maximizeEditorHideSidebar", - "keywords": [ - "maximize editor hide sidebar", - "maximize editor" - ] - }, - { - "id": "workbench.action.toggleMaximizeEditorGroup", - "title": "workbench.action.toggleMaximizeEditorGroup", - "keywords": [ - "toggle maximize editor group", - "toggle maximize" - ] - }, - { - "id": "workbench.action.minimizeOtherEditors", - "title": "workbench.action.minimizeOtherEditors", - "keywords": [ - "minimize other editors", - "minimize other" - ] - }, - { - "id": "workbench.action.minimizeOtherEditorsHideSidebar", - "title": "workbench.action.minimizeOtherEditorsHideSidebar", - "keywords": [ - "minimize other editors hide sidebar", - "minimize other" - ] - }, - { - "id": "workbench.action.moveEditorLeftInGroup", - "title": "workbench.action.moveEditorLeftInGroup", - "keywords": [ - "move editor left in group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorRightInGroup", - "title": "workbench.action.moveEditorRightInGroup", - "keywords": [ - "move editor right in group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToStart", - "title": "workbench.action.moveEditorToStart", - "keywords": [ - "move editor to start", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToEnd", - "title": "workbench.action.moveEditorToEnd", - "keywords": [ - "move editor to end", - "move editor" - ] - }, - { - "id": "workbench.action.moveActiveEditorGroupLeft", - "title": "workbench.action.moveActiveEditorGroupLeft", - "keywords": [ - "move active editor group left", - "move active" - ] - }, - { - "id": "workbench.action.moveActiveEditorGroupRight", - "title": "workbench.action.moveActiveEditorGroupRight", - "keywords": [ - "move active editor group right", - "move active" - ] - }, - { - "id": "workbench.action.moveActiveEditorGroupUp", - "title": "workbench.action.moveActiveEditorGroupUp", - "keywords": [ - "move active editor group up", - "move active" - ] - }, - { - "id": "workbench.action.moveActiveEditorGroupDown", - "title": "workbench.action.moveActiveEditorGroupDown", - "keywords": [ - "move active editor group down", - "move active" - ] - }, - { - "id": "workbench.action.duplicateActiveEditorGroupLeft", - "title": "workbench.action.duplicateActiveEditorGroupLeft", - "keywords": [ - "duplicate active editor group left", - "duplicate active" - ] - }, - { - "id": "workbench.action.duplicateActiveEditorGroupRight", - "title": "workbench.action.duplicateActiveEditorGroupRight", - "keywords": [ - "duplicate active editor group right", - "duplicate active" - ] - }, - { - "id": "workbench.action.duplicateActiveEditorGroupUp", - "title": "workbench.action.duplicateActiveEditorGroupUp", - "keywords": [ - "duplicate active editor group up", - "duplicate active" - ] - }, - { - "id": "workbench.action.duplicateActiveEditorGroupDown", - "title": "workbench.action.duplicateActiveEditorGroupDown", - "keywords": [ - "duplicate active editor group down", - "duplicate active" - ] - }, - { - "id": "workbench.action.moveEditorToPreviousGroup", - "title": "workbench.action.moveEditorToPreviousGroup", - "keywords": [ - "move editor to previous group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToNextGroup", - "title": "workbench.action.moveEditorToNextGroup", - "keywords": [ - "move editor to next group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToFirstGroup", - "title": "workbench.action.moveEditorToFirstGroup", - "keywords": [ - "move editor to first group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToLastGroup", - "title": "workbench.action.moveEditorToLastGroup", - "keywords": [ - "move editor to last group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToLeftGroup", - "title": "workbench.action.moveEditorToLeftGroup", - "keywords": [ - "move editor to left group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToRightGroup", - "title": "workbench.action.moveEditorToRightGroup", - "keywords": [ - "move editor to right group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToAboveGroup", - "title": "workbench.action.moveEditorToAboveGroup", - "keywords": [ - "move editor to above group", - "move editor" - ] - }, - { - "id": "workbench.action.moveEditorToBelowGroup", - "title": "workbench.action.moveEditorToBelowGroup", - "keywords": [ - "move editor to below group", - "move editor" - ] - }, - { - "id": "workbench.action.splitEditorToPreviousGroup", - "title": "workbench.action.splitEditorToPreviousGroup", - "keywords": [ - "split editor to previous group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToNextGroup", - "title": "workbench.action.splitEditorToNextGroup", - "keywords": [ - "split editor to next group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToFirstGroup", - "title": "workbench.action.splitEditorToFirstGroup", - "keywords": [ - "split editor to first group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToLastGroup", - "title": "workbench.action.splitEditorToLastGroup", - "keywords": [ - "split editor to last group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToLeftGroup", - "title": "workbench.action.splitEditorToLeftGroup", - "keywords": [ - "split editor to left group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToRightGroup", - "title": "workbench.action.splitEditorToRightGroup", - "keywords": [ - "split editor to right group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToAboveGroup", - "title": "workbench.action.splitEditorToAboveGroup", - "keywords": [ - "split editor to above group", - "split editor" - ] - }, - { - "id": "workbench.action.splitEditorToBelowGroup", - "title": "workbench.action.splitEditorToBelowGroup", - "keywords": [ - "split editor to below group", - "split editor" - ] - }, - { - "id": "workbench.action.focusActiveEditorGroup", - "title": "workbench.action.focusActiveEditorGroup", - "keywords": [ - "focus active editor group", - "focus active" - ] - }, - { - "id": "workbench.action.focusFirstEditorGroup", - "title": "workbench.action.focusFirstEditorGroup", - "keywords": [ - "focus first editor group", - "focus first" - ] - }, - { - "id": "workbench.action.focusLastEditorGroup", - "title": "workbench.action.focusLastEditorGroup", - "keywords": [ - "focus last editor group", - "focus last" - ] - }, - { - "id": "workbench.action.focusPreviousGroup", - "title": "workbench.action.focusPreviousGroup", - "keywords": [ - "focus previous group", - "focus previous" - ] - }, - { - "id": "workbench.action.focusNextGroup", - "title": "workbench.action.focusNextGroup", - "keywords": [ - "focus next group", - "focus next" - ] - }, - { - "id": "workbench.action.focusLeftGroup", - "title": "workbench.action.focusLeftGroup", - "keywords": [ - "focus left group", - "focus left" - ] - }, - { - "id": "workbench.action.focusRightGroup", - "title": "workbench.action.focusRightGroup", - "keywords": [ - "focus right group", - "focus right" - ] - }, - { - "id": "workbench.action.focusAboveGroup", - "title": "workbench.action.focusAboveGroup", - "keywords": [ - "focus above group", - "focus above" - ] - }, - { - "id": "workbench.action.focusBelowGroup", - "title": "workbench.action.focusBelowGroup", - "keywords": [ - "focus below group", - "focus below" - ] - }, - { - "id": "workbench.action.newGroupLeft", - "title": "workbench.action.newGroupLeft", - "keywords": [ - "new group left", - "new group" - ] - }, - { - "id": "workbench.action.newGroupRight", - "title": "workbench.action.newGroupRight", - "keywords": [ - "new group right", - "new group" - ] - }, - { - "id": "workbench.action.newGroupAbove", - "title": "workbench.action.newGroupAbove", - "keywords": [ - "new group above", - "new group" - ] - }, - { - "id": "workbench.action.newGroupBelow", - "title": "workbench.action.newGroupBelow", - "keywords": [ - "new group below", - "new group" - ] - }, - { - "id": "workbench.action.navigateLast", - "title": "workbench.action.navigateLast", - "keywords": [ - "navigate last" - ] - }, - { - "id": "workbench.action.navigateForwardInEditLocations", - "title": "workbench.action.navigateForwardInEditLocations", - "keywords": [ - "navigate forward in edit locations", - "navigate forward" - ] - }, - { - "id": "workbench.action.navigateBackInEditLocations", - "title": "workbench.action.navigateBackInEditLocations", - "keywords": [ - "navigate back in edit locations", - "navigate back" - ] - }, - { - "id": "workbench.action.navigatePreviousInEditLocations", - "title": "workbench.action.navigatePreviousInEditLocations", - "keywords": [ - "navigate previous in edit locations", - "navigate previous" - ] - }, - { - "id": "workbench.action.navigateToLastEditLocation", - "title": "workbench.action.navigateToLastEditLocation", - "keywords": [ - "navigate to last edit location", - "navigate to" - ] - }, - { - "id": "workbench.action.navigateForwardInNavigationLocations", - "title": "workbench.action.navigateForwardInNavigationLocations", - "keywords": [ - "navigate forward in navigation locations", - "navigate forward" - ] - }, - { - "id": "workbench.action.navigateBackInNavigationLocations", - "title": "workbench.action.navigateBackInNavigationLocations", - "keywords": [ - "navigate back in navigation locations", - "navigate back" - ] - }, - { - "id": "workbench.action.navigatePreviousInNavigationLocations", - "title": "workbench.action.navigatePreviousInNavigationLocations", - "keywords": [ - "navigate previous in navigation locations", - "navigate previous" - ] - }, - { - "id": "workbench.action.navigateToLastNavigationLocation", - "title": "workbench.action.navigateToLastNavigationLocation", - "keywords": [ - "navigate to last navigation location", - "navigate to" - ] - }, - { - "id": "workbench.action.clearEditorHistory", - "title": "workbench.action.clearEditorHistory", - "keywords": [ - "clear editor history", - "clear editor" - ] - }, - { - "id": "workbench.action.clearEditorHistoryWithoutConfirm", - "title": "workbench.action.clearEditorHistoryWithoutConfirm", - "keywords": [ - "clear editor history without confirm", - "clear editor" - ] - }, - { - "id": "workbench.action.editorLayoutSingle", - "title": "workbench.action.editorLayoutSingle", - "keywords": [ - "editor layout single", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutTwoColumns", - "title": "workbench.action.editorLayoutTwoColumns", - "keywords": [ - "editor layout two columns", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutThreeColumns", - "title": "workbench.action.editorLayoutThreeColumns", - "keywords": [ - "editor layout three columns", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutTwoRows", - "title": "workbench.action.editorLayoutTwoRows", - "keywords": [ - "editor layout two rows", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutThreeRows", - "title": "workbench.action.editorLayoutThreeRows", - "keywords": [ - "editor layout three rows", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutTwoByTwoGrid", - "title": "workbench.action.editorLayoutTwoByTwoGrid", - "keywords": [ - "editor layout two by two grid", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutTwoRowsRight", - "title": "workbench.action.editorLayoutTwoRowsRight", - "keywords": [ - "editor layout two rows right", - "editor layout" - ] - }, - { - "id": "workbench.action.editorLayoutTwoColumnsBottom", - "title": "workbench.action.editorLayoutTwoColumnsBottom", - "keywords": [ - "editor layout two columns bottom", - "editor layout" - ] - }, - { - "id": "workbench.action.toggleEditorType", - "title": "workbench.action.toggleEditorType", - "keywords": [ - "toggle editor type", - "toggle editor" - ] - }, - { - "id": "workbench.action.reopenTextEditor", - "title": "workbench.action.reopenTextEditor", - "keywords": [ - "reopen text editor", - "reopen text" - ] - }, - { - "id": "workbench.action.quickOpenPreviousRecentlyUsedEditor", - "title": "workbench.action.quickOpenPreviousRecentlyUsedEditor", - "keywords": [ - "quick open previous recently used editor", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenLeastRecentlyUsedEditor", - "title": "workbench.action.quickOpenLeastRecentlyUsedEditor", - "keywords": [ - "quick open least recently used editor", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup", - "title": "workbench.action.quickOpenPreviousRecentlyUsedEditorInGroup", - "keywords": [ - "quick open previous recently used editor in group", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenLeastRecentlyUsedEditorInGroup", - "title": "workbench.action.quickOpenLeastRecentlyUsedEditorInGroup", - "keywords": [ - "quick open least recently used editor in group", - "quick open" - ] - }, - { - "id": "workbench.action.openPreviousEditorFromHistory", - "title": "workbench.action.openPreviousEditorFromHistory", - "keywords": [ - "open previous editor from history", - "open previous" - ] - }, - { - "id": "workbench.action.moveEditorToNewWindow", - "title": "workbench.action.moveEditorToNewWindow", - "keywords": [ - "move editor to new window", - "move editor" - ] - }, - { - "id": "workbench.action.copyEditorToNewWindow", - "title": "workbench.action.copyEditorToNewWindow", - "keywords": [ - "copy editor to new window", - "copy editor" - ] - }, - { - "id": "workbench.action.moveEditorGroupToNewWindow", - "title": "workbench.action.moveEditorGroupToNewWindow", - "keywords": [ - "move editor group to new window", - "move editor" - ] - }, - { - "id": "workbench.action.copyEditorGroupToNewWindow", - "title": "workbench.action.copyEditorGroupToNewWindow", - "keywords": [ - "copy editor group to new window", - "copy editor" - ] - }, - { - "id": "workbench.action.restoreEditorsToMainWindow", - "title": "workbench.action.restoreEditorsToMainWindow", - "keywords": [ - "restore editors to main window", - "restore editors" - ] - }, - { - "id": "workbench.action.newEmptyEditorWindow", - "title": "workbench.action.newEmptyEditorWindow", - "keywords": [ - "new empty editor window", - "new empty" - ] - }, - { - "id": "workbench.action.quickOpenNavigateNextInEditorPicker", - "title": "workbench.action.quickOpenNavigateNextInEditorPicker", - "keywords": [ - "quick open navigate next in editor picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigatePreviousInEditorPicker", - "title": "workbench.action.quickOpenNavigatePreviousInEditorPicker", - "keywords": [ - "quick open navigate previous in editor picker", - "quick open" - ] - }, - { - "id": "moveActiveEditor", - "title": "moveActiveEditor", - "keywords": [ - "move active editor", - "move active" - ] - }, - { - "id": "copyActiveEditor", - "title": "copyActiveEditor", - "keywords": [ - "copy active editor", - "copy active" - ] - }, - { - "id": "layoutEditorGroups", - "title": "layoutEditorGroups", - "keywords": [ - "layout editor groups", - "layout editor" - ] - }, - { - "id": "vscode.setEditorLayout", - "title": "vscode.setEditorLayout", - "keywords": [ - "vscode set editor layout", - "vscode set" - ] - }, - { - "id": "vscode.getEditorLayout", - "title": "vscode.getEditorLayout", - "keywords": [ - "vscode get editor layout", - "vscode get" - ] - }, - { - "id": "workbench.action.compareEditor.openSide", - "title": "workbench.action.compareEditor.openSide", - "keywords": [ - "compare editor open side", - "compare editor" - ] - }, - { - "id": "workbench.action.compareEditor.nextChange", - "title": "workbench.action.compareEditor.nextChange", - "keywords": [ - "compare editor next change", - "compare editor" - ] - }, - { - "id": "workbench.action.compareEditor.previousChange", - "title": "workbench.action.compareEditor.previousChange", - "keywords": [ - "compare editor previous change", - "compare editor" - ] - }, - { - "id": "toggle.diff.renderSideBySide", - "title": "toggle.diff.renderSideBySide", - "keywords": [ - "toggle diff render side by side", - "toggle diff" - ] - }, - { - "id": "workbench.action.compareEditor.focusPrimarySide", - "title": "workbench.action.compareEditor.focusPrimarySide", - "keywords": [ - "compare editor focus primary side", - "compare editor" - ] - }, - { - "id": "workbench.action.compareEditor.focusSecondarySide", - "title": "workbench.action.compareEditor.focusSecondarySide", - "keywords": [ - "compare editor focus secondary side", - "compare editor" - ] - }, - { - "id": "workbench.action.compareEditor.focusOtherSide", - "title": "workbench.action.compareEditor.focusOtherSide", - "keywords": [ - "compare editor focus other side", - "compare editor" - ] - }, - { - "id": "toggle.diff.ignoreTrimWhitespace", - "title": "toggle.diff.ignoreTrimWhitespace", - "keywords": [ - "toggle diff ignore trim whitespace", - "toggle diff" - ] - }, - { - "id": "workbench.action.compareEditor.swapSides", - "title": "workbench.action.compareEditor.swapSides", - "keywords": [ - "compare editor swap sides", - "compare editor" - ] - }, - { - "id": "vscode.open", - "title": "vscode.open", - "keywords": [ - "vscode open" - ] - }, - { - "id": "vscode.diff", - "title": "vscode.diff", - "keywords": [ - "vscode diff" - ] - }, - { - "id": "vscode.changes", - "title": "vscode.changes", - "keywords": [ - "vscode changes" - ] - }, - { - "id": "workbench.action.openEditorAtIndex", - "title": "workbench.action.openEditorAtIndex", - "keywords": [ - "open editor at index", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex1", - "title": "workbench.action.openEditorAtIndex1", - "keywords": [ - "open editor at index1", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex2", - "title": "workbench.action.openEditorAtIndex2", - "keywords": [ - "open editor at index2", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex3", - "title": "workbench.action.openEditorAtIndex3", - "keywords": [ - "open editor at index3", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex4", - "title": "workbench.action.openEditorAtIndex4", - "keywords": [ - "open editor at index4", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex5", - "title": "workbench.action.openEditorAtIndex5", - "keywords": [ - "open editor at index5", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex6", - "title": "workbench.action.openEditorAtIndex6", - "keywords": [ - "open editor at index6", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex7", - "title": "workbench.action.openEditorAtIndex7", - "keywords": [ - "open editor at index7", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex8", - "title": "workbench.action.openEditorAtIndex8", - "keywords": [ - "open editor at index8", - "open editor" - ] - }, - { - "id": "workbench.action.openEditorAtIndex9", - "title": "workbench.action.openEditorAtIndex9", - "keywords": [ - "open editor at index9", - "open editor" - ] - }, - { - "id": "workbench.action.closeActiveEditor", - "title": "workbench.action.closeActiveEditor", - "keywords": [ - "close active editor", - "close active" - ] - }, - { - "id": "workbench.action.closeActivePinnedEditor", - "title": "workbench.action.closeActivePinnedEditor", - "keywords": [ - "close active pinned editor", - "close active" - ] - }, - { - "id": "workbench.action.closeEditorsInGroup", - "title": "workbench.action.closeEditorsInGroup", - "keywords": [ - "close editors in group", - "close editors" - ] - }, - { - "id": "workbench.action.closeGroup", - "title": "workbench.action.closeGroup", - "keywords": [ - "close group" - ] - }, - { - "id": "workbench.action.closeUnmodifiedEditors", - "title": "workbench.action.closeUnmodifiedEditors", - "keywords": [ - "close unmodified editors", - "close unmodified" - ] - }, - { - "id": "workbench.action.closeOtherEditors", - "title": "workbench.action.closeOtherEditors", - "keywords": [ - "close other editors", - "close other" - ] - }, - { - "id": "workbench.action.closeEditorsToTheRight", - "title": "workbench.action.closeEditorsToTheRight", - "keywords": [ - "close editors to the right", - "close editors" - ] - }, - { - "id": "workbench.action.reopenWithEditor", - "title": "workbench.action.reopenWithEditor", - "keywords": [ - "reopen with editor", - "reopen with" - ] - }, - { - "id": "reopenActiveEditorWith", - "title": "reopenActiveEditorWith", - "keywords": [ - "reopen active editor with", - "reopen active" - ] - }, - { - "id": "workbench.action.closeEditorsAndGroup", - "title": "workbench.action.closeEditorsAndGroup", - "keywords": [ - "close editors and group", - "close editors" - ] - }, - { - "id": "workbench.action.keepEditor", - "title": "workbench.action.keepEditor", - "keywords": [ - "keep editor" - ] - }, - { - "id": "workbench.action.toggleKeepEditors", - "title": "workbench.action.toggleKeepEditors", - "keywords": [ - "toggle keep editors", - "toggle keep" - ] - }, - { - "id": "workbench.action.toggleEditorGroupLock", - "title": "workbench.action.toggleEditorGroupLock", - "keywords": [ - "toggle editor group lock", - "toggle editor" - ] - }, - { - "id": "workbench.action.lockEditorGroup", - "title": "workbench.action.lockEditorGroup", - "keywords": [ - "lock editor group", - "lock editor" - ] - }, - { - "id": "workbench.action.unlockEditorGroup", - "title": "workbench.action.unlockEditorGroup", - "keywords": [ - "unlock editor group", - "unlock editor" - ] - }, - { - "id": "workbench.action.pinEditor", - "title": "workbench.action.pinEditor", - "keywords": [ - "pin editor" - ] - }, - { - "id": "workbench.action.unpinEditor", - "title": "workbench.action.unpinEditor", - "keywords": [ - "unpin editor" - ] - }, - { - "id": "workbench.action.showEditorsInGroup", - "title": "workbench.action.showEditorsInGroup", - "keywords": [ - "show editors in group", - "show editors" - ] - }, - { - "id": "workbench.action.splitEditorInGroup", - "title": "workbench.action.splitEditorInGroup", - "keywords": [ - "split editor in group", - "split editor" - ] - }, - { - "id": "workbench.action.joinEditorInGroup", - "title": "workbench.action.joinEditorInGroup", - "keywords": [ - "join editor in group", - "join editor" - ] - }, - { - "id": "workbench.action.toggleSplitEditorInGroup", - "title": "workbench.action.toggleSplitEditorInGroup", - "keywords": [ - "toggle split editor in group", - "toggle split" - ] - }, - { - "id": "workbench.action.toggleSplitEditorInGroupLayout", - "title": "workbench.action.toggleSplitEditorInGroupLayout", - "keywords": [ - "toggle split editor in group layout", - "toggle split" - ] - }, - { - "id": "workbench.action.focusFirstSideEditor", - "title": "workbench.action.focusFirstSideEditor", - "keywords": [ - "focus first side editor", - "focus first" - ] - }, - { - "id": "workbench.action.focusSecondSideEditor", - "title": "workbench.action.focusSecondSideEditor", - "keywords": [ - "focus second side editor", - "focus second" - ] - }, - { - "id": "workbench.action.focusOtherSideEditor", - "title": "workbench.action.focusOtherSideEditor", - "keywords": [ - "focus other side editor", - "focus other" - ] - }, - { - "id": "workbench.action.focusSecondEditorGroup", - "title": "workbench.action.focusSecondEditorGroup", - "keywords": [ - "focus second editor group", - "focus second" - ] - }, - { - "id": "workbench.action.focusThirdEditorGroup", - "title": "workbench.action.focusThirdEditorGroup", - "keywords": [ - "focus third editor group", - "focus third" - ] - }, - { - "id": "workbench.action.focusFourthEditorGroup", - "title": "workbench.action.focusFourthEditorGroup", - "keywords": [ - "focus fourth editor group", - "focus fourth" - ] - }, - { - "id": "workbench.action.focusFifthEditorGroup", - "title": "workbench.action.focusFifthEditorGroup", - "keywords": [ - "focus fifth editor group", - "focus fifth" - ] - }, - { - "id": "workbench.action.focusSixthEditorGroup", - "title": "workbench.action.focusSixthEditorGroup", - "keywords": [ - "focus sixth editor group", - "focus sixth" - ] - }, - { - "id": "workbench.action.focusSeventhEditorGroup", - "title": "workbench.action.focusSeventhEditorGroup", - "keywords": [ - "focus seventh editor group", - "focus seventh" - ] - }, - { - "id": "workbench.action.focusEighthEditorGroup", - "title": "workbench.action.focusEighthEditorGroup", - "keywords": [ - "focus eighth editor group", - "focus eighth" - ] - }, - { - "id": "workbench.action.focusLeftGroupWithoutWrap", - "title": "workbench.action.focusLeftGroupWithoutWrap", - "keywords": [ - "focus left group without wrap", - "focus left" - ] - }, - { - "id": "workbench.action.focusRightGroupWithoutWrap", - "title": "workbench.action.focusRightGroupWithoutWrap", - "keywords": [ - "focus right group without wrap", - "focus right" - ] - }, - { - "id": "workbench.action.focusAboveGroupWithoutWrap", - "title": "workbench.action.focusAboveGroupWithoutWrap", - "keywords": [ - "focus above group without wrap", - "focus above" - ] - }, - { - "id": "workbench.action.focusBelowGroupWithoutWrap", - "title": "workbench.action.focusBelowGroupWithoutWrap", - "keywords": [ - "focus below group without wrap", - "focus below" - ] - }, - { - "id": "workbench.action.moveModalEditorToMain", - "title": "workbench.action.moveModalEditorToMain", - "keywords": [ - "move modal editor to main", - "move modal" - ] - }, - { - "id": "workbench.action.moveModalEditorToWindow", - "title": "workbench.action.moveModalEditorToWindow", - "keywords": [ - "move modal editor to window", - "move modal" - ] - }, - { - "id": "workbench.action.toggleModalEditorSidebar", - "title": "workbench.action.toggleModalEditorSidebar", - "keywords": [ - "toggle modal editor sidebar", - "toggle modal" - ] - }, - { - "id": "workbench.action.toggleModalEditorMaximized", - "title": "workbench.action.toggleModalEditorMaximized", - "keywords": [ - "toggle modal editor maximized", - "toggle modal" - ] - }, - { - "id": "workbench.action.closeModalEditor", - "title": "workbench.action.closeModalEditor", - "keywords": [ - "close modal editor", - "close modal" - ] - }, - { - "id": "workbench.action.navigateModalEditorPrevious", - "title": "workbench.action.navigateModalEditorPrevious", - "keywords": [ - "navigate modal editor previous", - "navigate modal" - ] - }, - { - "id": "workbench.action.navigateModalEditorNext", - "title": "workbench.action.navigateModalEditorNext", - "keywords": [ - "navigate modal editor next", - "navigate modal" - ] - }, - { - "id": "breadcrumbs.toggle", - "title": "breadcrumbs.toggle", - "keywords": [ - "breadcrumbs toggle" - ] - }, - { - "id": "breadcrumbs.focusAndSelect", - "title": "breadcrumbs.focusAndSelect", - "keywords": [ - "breadcrumbs focus and select", - "breadcrumbs focus" - ] - }, - { - "id": "breadcrumbs.focus", - "title": "breadcrumbs.focus", - "keywords": [ - "breadcrumbs focus" - ] - }, - { - "id": "breadcrumbs.toggleToOn", - "title": "breadcrumbs.toggleToOn", - "keywords": [ - "breadcrumbs toggle to on", - "breadcrumbs toggle" - ] - }, - { - "id": "breadcrumbs.focusNext", - "title": "breadcrumbs.focusNext", - "keywords": [ - "breadcrumbs focus next", - "breadcrumbs focus" - ] - }, - { - "id": "breadcrumbs.focusPrevious", - "title": "breadcrumbs.focusPrevious", - "keywords": [ - "breadcrumbs focus previous", - "breadcrumbs focus" - ] - }, - { - "id": "breadcrumbs.focusNextWithPicker", - "title": "breadcrumbs.focusNextWithPicker", - "keywords": [ - "breadcrumbs focus next with picker", - "breadcrumbs focus" - ] - }, - { - "id": "breadcrumbs.focusPreviousWithPicker", - "title": "breadcrumbs.focusPreviousWithPicker", - "keywords": [ - "breadcrumbs focus previous with picker", - "breadcrumbs focus" - ] - }, - { - "id": "breadcrumbs.selectFocused", - "title": "breadcrumbs.selectFocused", - "keywords": [ - "breadcrumbs select focused", - "breadcrumbs select" - ] - }, - { - "id": "breadcrumbs.revealFocused", - "title": "breadcrumbs.revealFocused", - "keywords": [ - "breadcrumbs reveal focused", - "breadcrumbs reveal" - ] - }, - { - "id": "breadcrumbs.selectEditor", - "title": "breadcrumbs.selectEditor", - "keywords": [ - "breadcrumbs select editor", - "breadcrumbs select" - ] - }, - { - "id": "breadcrumbs.revealFocusedFromTreeAside", - "title": "breadcrumbs.revealFocusedFromTreeAside", - "keywords": [ - "breadcrumbs reveal focused from tree aside", - "breadcrumbs reveal" - ] - }, - { - "id": "breadcrumbs.copyPath", - "title": "breadcrumbs.copyPath", - "keywords": [ - "breadcrumbs copy path", - "breadcrumbs copy" - ] - }, - { - "id": "workbench.action.toggleCompactAuxiliaryWindow", - "title": "workbench.action.toggleCompactAuxiliaryWindow", - "keywords": [ - "toggle compact auxiliary window", - "toggle compact" - ] - }, - { - "id": "workbench.action.enableCompactAuxiliaryWindow", - "title": "workbench.action.enableCompactAuxiliaryWindow", - "keywords": [ - "enable compact auxiliary window", - "enable compact" - ] - }, - { - "id": "workbench.action.disableCompactAuxiliaryWindow", - "title": "workbench.action.disableCompactAuxiliaryWindow", - "keywords": [ - "disable compact auxiliary window", - "disable compact" - ] - }, - { - "id": "workbench.action.closeSidebar", - "title": "workbench.action.closeSidebar", - "keywords": [ - "close sidebar" - ] - }, - { - "id": "workbench.action.focusSideBar", - "title": "workbench.action.focusSideBar", - "keywords": [ - "focus side bar", - "focus side" - ] - }, - { - "id": "workbench.action.activityBarLocation.default", - "title": "workbench.action.activityBarLocation.default", - "keywords": [ - "activity bar location default", - "activity bar" - ] - }, - { - "id": "workbench.action.activityBarLocation.top", - "title": "workbench.action.activityBarLocation.top", - "keywords": [ - "activity bar location top", - "activity bar" - ] - }, - { - "id": "workbench.action.activityBarLocation.bottom", - "title": "workbench.action.activityBarLocation.bottom", - "keywords": [ - "activity bar location bottom", - "activity bar" - ] - }, - { - "id": "workbench.action.activityBarLocation.hide", - "title": "workbench.action.activityBarLocation.hide", - "keywords": [ - "activity bar location hide", - "activity bar" - ] - }, - { - "id": "workbench.action.previousSideBarView", - "title": "workbench.action.previousSideBarView", - "keywords": [ - "previous side bar view", - "previous side" - ] - }, - { - "id": "workbench.action.nextSideBarView", - "title": "workbench.action.nextSideBarView", - "keywords": [ - "next side bar view", - "next side" - ] - }, - { - "id": "workbench.action.focusActivityBar", - "title": "workbench.action.focusActivityBar", - "keywords": [ - "focus activity bar", - "focus activity" - ] - }, - { - "id": "workbench.banner.focusBanner", - "title": "workbench.banner.focusBanner", - "keywords": [ - "workbench banner focus banner", - "workbench banner" - ] - }, - { - "id": "workbench.banner.focusNextAction", - "title": "workbench.banner.focusNextAction", - "keywords": [ - "workbench banner focus next action", - "workbench banner" - ] - }, - { - "id": "workbench.banner.focusPreviousAction", - "title": "workbench.banner.focusPreviousAction", - "keywords": [ - "workbench banner focus previous action", - "workbench banner" - ] - }, - { - "id": "workbench.action.focusBanner", - "title": "workbench.action.focusBanner", - "keywords": [ - "focus banner" - ] - }, - { - "id": "workbench.statusBar.focusPrevious", - "title": "workbench.statusBar.focusPrevious", - "keywords": [ - "workbench status bar focus previous", - "workbench status" - ] - }, - { - "id": "workbench.statusBar.focusNext", - "title": "workbench.statusBar.focusNext", - "keywords": [ - "workbench status bar focus next", - "workbench status" - ] - }, - { - "id": "workbench.statusBar.focusFirst", - "title": "workbench.statusBar.focusFirst", - "keywords": [ - "workbench status bar focus first", - "workbench status" - ] - }, - { - "id": "workbench.statusBar.focusLast", - "title": "workbench.statusBar.focusLast", - "keywords": [ - "workbench status bar focus last", - "workbench status" - ] - }, - { - "id": "workbench.statusBar.clearFocus", - "title": "workbench.statusBar.clearFocus", - "keywords": [ - "workbench status bar clear focus", - "workbench status" - ] - }, - { - "id": "workbench.action.focusStatusBar", - "title": "workbench.action.focusStatusBar", - "keywords": [ - "focus status bar", - "focus status" - ] - }, - { - "id": "menu.resetHiddenStates", - "title": "menu.resetHiddenStates", - "keywords": [ - "menu reset hidden states", - "menu reset" - ] - }, - { - "id": "extension.bisect.start", - "title": "extension.bisect.start", - "keywords": [ - "bisect start" - ] - }, - { - "id": "extension.bisect.next", - "title": "extension.bisect.next", - "keywords": [ - "bisect next" - ] - }, - { - "id": "extension.bisect.stop", - "title": "extension.bisect.stop", - "keywords": [ - "bisect stop" - ] - }, - { - "id": "quickInput.pageNext", - "title": "quickInput.pageNext", - "keywords": [ - "quick input page next", - "quick input" - ] - }, - { - "id": "quickInput.pagePrevious", - "title": "quickInput.pagePrevious", - "keywords": [ - "quick input page previous", - "quick input" - ] - }, - { - "id": "quickInput.first", - "title": "quickInput.first", - "keywords": [ - "quick input first", - "quick input" - ] - }, - { - "id": "quickInput.last", - "title": "quickInput.last", - "keywords": [ - "quick input last", - "quick input" - ] - }, - { - "id": "quickInput.next", - "title": "quickInput.next", - "keywords": [ - "quick input next", - "quick input" - ] - }, - { - "id": "quickInput.previous", - "title": "quickInput.previous", - "keywords": [ - "quick input previous", - "quick input" - ] - }, - { - "id": "quickInput.nextSeparatorWithQuickAccessFallback", - "title": "quickInput.nextSeparatorWithQuickAccessFallback", - "keywords": [ - "quick input next separator with quick access fallback", - "quick input" - ] - }, - { - "id": "quickInput.nextSeparator", - "title": "quickInput.nextSeparator", - "keywords": [ - "quick input next separator", - "quick input" - ] - }, - { - "id": "quickInput.previousSeparatorWithQuickAccessFallback", - "title": "quickInput.previousSeparatorWithQuickAccessFallback", - "keywords": [ - "quick input previous separator with quick access fallback", - "quick input" - ] - }, - { - "id": "quickInput.previousSeparator", - "title": "quickInput.previousSeparator", - "keywords": [ - "quick input previous separator", - "quick input" - ] - }, - { - "id": "quickInput.accept", - "title": "quickInput.accept", - "keywords": [ - "quick input accept", - "quick input" - ] - }, - { - "id": "quickInput.acceptInBackground", - "title": "quickInput.acceptInBackground", - "keywords": [ - "quick input accept in background", - "quick input" - ] - }, - { - "id": "quickInput.hide", - "title": "quickInput.hide", - "keywords": [ - "quick input hide", - "quick input" - ] - }, - { - "id": "quickInput.toggleCheckbox", - "title": "quickInput.toggleCheckbox", - "keywords": [ - "quick input toggle checkbox", - "quick input" - ] - }, - { - "id": "quickInput.toggleHover", - "title": "quickInput.toggleHover", - "keywords": [ - "quick input toggle hover", - "quick input" - ] - }, - { - "id": "getContextKeyInfo", - "title": "getContextKeyInfo", - "keywords": [ - "get context key info", - "get context" - ] - }, - { - "id": "workbench.actions.accounts.signIn", - "title": "workbench.actions.accounts.signIn", - "keywords": [ - "workbench actions accounts sign in", - "workbench actions" - ] - }, - { - "id": "editor.action.toggleWordWrap", - "title": "editor.action.toggleWordWrap", - "keywords": [ - "toggle word wrap", - "toggle word" - ] - }, - { - "id": "perfview.show", - "title": "perfview.show", - "keywords": [ - "perfview show" - ] - }, - { - "id": "perf.insta.printAsyncCycles", - "title": "perf.insta.printAsyncCycles", - "keywords": [ - "perf insta print async cycles", - "perf insta" - ] - }, - { - "id": "perf.insta.printTraces", - "title": "perf.insta.printTraces", - "keywords": [ - "perf insta print traces", - "perf insta" - ] - }, - { - "id": "perf.event.profiling", - "title": "perf.event.profiling", - "keywords": [ - "perf event profiling", - "perf event" - ] - }, - { - "id": "workbench.extensions.action.showExtensionsForLanguage", - "title": "workbench.extensions.action.showExtensionsForLanguage", - "keywords": [ - "workbench extensions action show extensions for language", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showExtensionsWithIds", - "title": "workbench.extensions.action.showExtensionsWithIds", - "keywords": [ - "workbench extensions action show extensions with ids", - "workbench extensions" - ] - }, - { - "id": "notebook.developer.addViewZones", - "title": "notebook.developer.addViewZones", - "keywords": [ - "notebook developer add view zones", - "notebook developer" - ] - }, - { - "id": "notebook.toggleLineNumbers", - "title": "notebook.toggleLineNumbers", - "keywords": [ - "notebook toggle line numbers", - "notebook toggle" - ] - }, - { - "id": "notebook.cell.toggleLineNumbers", - "title": "notebook.cell.toggleLineNumbers", - "keywords": [ - "notebook cell toggle line numbers", - "notebook cell" - ] - }, - { - "id": "notebook.cellOuput.showEmptyOutputs", - "title": "notebook.cellOuput.showEmptyOutputs", - "keywords": [ - "notebook cell ouput show empty outputs", - "notebook cell" - ] - }, - { - "id": "notebook.cellOutput.copy", - "title": "notebook.cellOutput.copy", - "keywords": [ - "notebook cell output copy", - "notebook cell" - ] - }, - { - "id": "notebook.cellOutput.openInTextEditor", - "title": "notebook.cellOutput.openInTextEditor", - "keywords": [ - "notebook cell output open in text editor", - "notebook cell" - ] - }, - { - "id": "notebook.cellOutput.saveImage", - "title": "notebook.cellOutput.saveImage", - "keywords": [ - "notebook cell output save image", - "notebook cell" - ] - }, - { - "id": "notebook.cellOutput.openInOutputPreview", - "title": "notebook.cellOutput.openInOutputPreview", - "keywords": [ - "notebook cell output open in output preview", - "notebook cell" - ] - }, - { - "id": "notebook.fold", - "title": "notebook.fold", - "keywords": [ - "notebook fold" - ] - }, - { - "id": "notebook.unfold", - "title": "notebook.unfold", - "keywords": [ - "notebook unfold" - ] - }, - { - "id": "notebook.cell.insertCodeCellAbove", - "title": "notebook.cell.insertCodeCellAbove", - "keywords": [ - "notebook cell insert code cell above", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertCodeCellAboveAndFocusContainer", - "title": "notebook.cell.insertCodeCellAboveAndFocusContainer", - "keywords": [ - "notebook cell insert code cell above and focus container", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertCodeCellBelow", - "title": "notebook.cell.insertCodeCellBelow", - "keywords": [ - "notebook cell insert code cell below", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertCodeCellBelowAndFocusContainer", - "title": "notebook.cell.insertCodeCellBelowAndFocusContainer", - "keywords": [ - "notebook cell insert code cell below and focus container", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertMarkdownCellAbove", - "title": "notebook.cell.insertMarkdownCellAbove", - "keywords": [ - "notebook cell insert markdown cell above", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertMarkdownCellBelow", - "title": "notebook.cell.insertMarkdownCellBelow", - "keywords": [ - "notebook cell insert markdown cell below", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertCodeCellAtTop", - "title": "notebook.cell.insertCodeCellAtTop", - "keywords": [ - "notebook cell insert code cell at top", - "notebook cell" - ] - }, - { - "id": "notebook.cell.insertMarkdownCellAtTop", - "title": "notebook.cell.insertMarkdownCellAtTop", - "keywords": [ - "notebook cell insert markdown cell at top", - "notebook cell" - ] - }, - { - "id": "notebook.renderAllMarkdownCells", - "title": "notebook.renderAllMarkdownCells", - "keywords": [ - "notebook render all markdown cells", - "notebook render" - ] - }, - { - "id": "notebook.execute", - "title": "notebook.execute", - "keywords": [ - "notebook execute" - ] - }, - { - "id": "notebook.cell.execute", - "title": "notebook.cell.execute", - "keywords": [ - "notebook cell execute", - "notebook cell" - ] - }, - { - "id": "notebook.cell.executeCellsAbove", - "title": "notebook.cell.executeCellsAbove", - "keywords": [ - "notebook cell execute cells above", - "notebook cell" - ] - }, - { - "id": "notebook.cell.executeCellAndBelow", - "title": "notebook.cell.executeCellAndBelow", - "keywords": [ - "notebook cell execute cell and below", - "notebook cell" - ] - }, - { - "id": "notebook.cell.executeAndFocusContainer", - "title": "notebook.cell.executeAndFocusContainer", - "keywords": [ - "notebook cell execute and focus container", - "notebook cell" - ] - }, - { - "id": "notebook.cell.cancelExecution", - "title": "notebook.cell.cancelExecution", - "keywords": [ - "notebook cell cancel execution", - "notebook cell" - ] - }, - { - "id": "notebook.cell.executeAndSelectBelow", - "title": "notebook.cell.executeAndSelectBelow", - "keywords": [ - "notebook cell execute and select below", - "notebook cell" - ] - }, - { - "id": "notebook.cell.executeAndInsertBelow", - "title": "notebook.cell.executeAndInsertBelow", - "keywords": [ - "notebook cell execute and insert below", - "notebook cell" - ] - }, - { - "id": "notebook.cancelExecution", - "title": "notebook.cancelExecution", - "keywords": [ - "notebook cancel execution", - "notebook cancel" - ] - }, - { - "id": "notebook.interruptExecution", - "title": "notebook.interruptExecution", - "keywords": [ - "notebook interrupt execution", - "notebook interrupt" - ] - }, - { - "id": "notebook.revealRunningCell", - "title": "notebook.revealRunningCell", - "keywords": [ - "notebook reveal running cell", - "notebook reveal" - ] - }, - { - "id": "notebook.revealLastFailedCell", - "title": "notebook.revealLastFailedCell", - "keywords": [ - "notebook reveal last failed cell", - "notebook reveal" - ] - }, - { - "id": "notebook.outline.toggleShowMarkdownHeadersOnly", - "title": "notebook.outline.toggleShowMarkdownHeadersOnly", - "keywords": [ - "notebook outline toggle show markdown headers only", - "notebook outline" - ] - }, - { - "id": "notebook.outline.toggleCodeCells", - "title": "notebook.outline.toggleCodeCells", - "keywords": [ - "notebook outline toggle code cells", - "notebook outline" - ] - }, - { - "id": "notebook.outline.toggleCodeCellSymbols", - "title": "notebook.outline.toggleCodeCellSymbols", - "keywords": [ - "notebook outline toggle code cell symbols", - "notebook outline" - ] - }, - { - "id": "notebook.section.runSingleCell", - "title": "notebook.section.runSingleCell", - "keywords": [ - "notebook section run single cell", - "notebook section" - ] - }, - { - "id": "notebook.section.runCells", - "title": "notebook.section.runCells", - "keywords": [ - "notebook section run cells", - "notebook section" - ] - }, - { - "id": "notebook.section.foldSection", - "title": "notebook.section.foldSection", - "keywords": [ - "notebook section fold section", - "notebook section" - ] - }, - { - "id": "notebook.section.expandSection", - "title": "notebook.section.expandSection", - "keywords": [ - "notebook section expand section", - "notebook section" - ] - }, - { - "id": "workbench.notebook.layout.select", - "title": "workbench.notebook.layout.select", - "keywords": [ - "workbench notebook layout select", - "workbench notebook" - ] - }, - { - "id": "workbench.notebook.layout.configure", - "title": "workbench.notebook.layout.configure", - "keywords": [ - "workbench notebook layout configure", - "workbench notebook" - ] - }, - { - "id": "workbench.notebook.layout.configure.editorTitle", - "title": "workbench.notebook.layout.configure.editorTitle", - "keywords": [ - "workbench notebook layout configure editor title", - "workbench notebook" - ] - }, - { - "id": "notebook.toggleLineNumbersFromEditorTitle", - "title": "notebook.toggleLineNumbersFromEditorTitle", - "keywords": [ - "notebook toggle line numbers from editor title", - "notebook toggle" - ] - }, - { - "id": "notebook.toggleCellToolbarPositionFromEditorTitle", - "title": "notebook.toggleCellToolbarPositionFromEditorTitle", - "keywords": [ - "notebook toggle cell toolbar position from editor title", - "notebook toggle" - ] - }, - { - "id": "breadcrumbs.toggleFromEditorTitle", - "title": "breadcrumbs.toggleFromEditorTitle", - "keywords": [ - "breadcrumbs toggle from editor title", - "breadcrumbs toggle" - ] - }, - { - "id": "notebook.saveMimeTypeOrder", - "title": "notebook.saveMimeTypeOrder", - "keywords": [ - "notebook save mime type order", - "notebook save" - ] - }, - { - "id": "workbench.notebook.layout.webview.reset", - "title": "workbench.notebook.layout.webview.reset", - "keywords": [ - "workbench notebook layout webview reset", - "workbench notebook" - ] - }, - { - "id": "notebook.action.toggleNotebookStickyScroll", - "title": "notebook.action.toggleNotebookStickyScroll", - "keywords": [ - "notebook action toggle notebook sticky scroll", - "notebook action" - ] - }, - { - "id": "chatEditing.openFileInDiff", - "title": "chatEditing.openFileInDiff", - "keywords": [ - "chat editing open file in diff", - "chat editing" - ] - }, - { - "id": "chatEditing.acceptFile", - "title": "chatEditing.acceptFile", - "keywords": [ - "chat editing accept file", - "chat editing" - ] - }, - { - "id": "chatEditing.discardFile", - "title": "chatEditing.discardFile", - "keywords": [ - "chat editing discard file", - "chat editing" - ] - }, - { - "id": "chatEditing.acceptAllFiles", - "title": "chatEditing.acceptAllFiles", - "keywords": [ - "chat editing accept all files", - "chat editing" - ] - }, - { - "id": "chatEditing.discardAllFiles", - "title": "chatEditing.discardAllFiles", - "keywords": [ - "chat editing discard all files", - "chat editing" - ] - }, - { - "id": "chatEditing.toggleExplanationWidget", - "title": "chatEditing.toggleExplanationWidget", - "keywords": [ - "chat editing toggle explanation widget", - "chat editing" - ] - }, - { - "id": "chatEditing.viewChanges", - "title": "chatEditing.viewChanges", - "keywords": [ - "chat editing view changes", - "chat editing" - ] - }, - { - "id": "workbench.action.chat.undoEdits", - "title": "workbench.action.chat.undoEdits", - "keywords": [ - "chat undo edits", - "chat undo" - ] - }, - { - "id": "workbench.action.chat.restoreCheckpoint", - "title": "workbench.action.chat.restoreCheckpoint", - "keywords": [ - "chat restore checkpoint", - "chat restore" - ] - }, - { - "id": "workbench.action.chat.startOver", - "title": "workbench.action.chat.startOver", - "keywords": [ - "chat start over", - "chat start" - ] - }, - { - "id": "workbench.action.chat.restoreLastCheckpoint", - "title": "workbench.action.chat.restoreLastCheckpoint", - "keywords": [ - "chat restore last checkpoint", - "chat restore" - ] - }, - { - "id": "workbench.action.chat.editRequests", - "title": "workbench.action.chat.editRequests", - "keywords": [ - "chat edit requests", - "chat edit" - ] - }, - { - "id": "chat.openFileUpdatedBySnapshot", - "title": "chat.openFileUpdatedBySnapshot", - "keywords": [ - "chat open file updated by snapshot", - "chat open" - ] - }, - { - "id": "chat.openFileSnapshot", - "title": "chat.openFileSnapshot", - "keywords": [ - "chat open file snapshot", - "chat open" - ] - }, - { - "id": "workbench.action.edits.addFilesFromReferences", - "title": "workbench.action.edits.addFilesFromReferences", - "keywords": [ - "edits add files from references", - "edits add" - ] - }, - { - "id": "chatEditing.viewPreviousEdits", - "title": "chatEditing.viewPreviousEdits", - "keywords": [ - "chat editing view previous edits", - "chat editing" - ] - }, - { - "id": "explorer.newFile", - "title": "explorer.newFile", - "keywords": [ - "explorer new file", - "explorer new" - ] - }, - { - "id": "explorer.newFolder", - "title": "explorer.newFolder", - "keywords": [ - "explorer new folder", - "explorer new" - ] - }, - { - "id": "explorer.download", - "title": "explorer.download", - "keywords": [ - "explorer download" - ] - }, - { - "id": "explorer.upload", - "title": "explorer.upload", - "keywords": [ - "explorer upload" - ] - }, - { - "id": "workbench.action.toggleEditorGroupLayout", - "title": "workbench.action.toggleEditorGroupLayout", - "keywords": [ - "toggle editor group layout", - "toggle editor" - ] - }, - { - "id": "workbench.action.files.saveAll", - "title": "workbench.action.files.saveAll", - "keywords": [ - "files save all", - "files save" - ] - }, - { - "id": "openEditors.closeAll", - "title": "openEditors.closeAll", - "keywords": [ - "open editors close all", - "open editors" - ] - }, - { - "id": "openEditors.newUntitledFile", - "title": "openEditors.newUntitledFile", - "keywords": [ - "open editors new untitled file", - "open editors" - ] - }, - { - "id": "explorer.openToSide", - "title": "explorer.openToSide", - "keywords": [ - "explorer open to side", - "explorer open" - ] - }, - { - "id": "explorer.openAndPassFocus", - "title": "explorer.openAndPassFocus", - "keywords": [ - "explorer open and pass focus", - "explorer open" - ] - }, - { - "id": "workbench.files.action.compareWithSaved", - "title": "workbench.files.action.compareWithSaved", - "keywords": [ - "workbench files action compare with saved", - "workbench files" - ] - }, - { - "id": "selectForCompare", - "title": "selectForCompare", - "keywords": [ - "select for compare", - "select for" - ] - }, - { - "id": "compareSelected", - "title": "compareSelected", - "keywords": [ - "compare selected" - ] - }, - { - "id": "compareFiles", - "title": "compareFiles", - "keywords": [ - "compare files" - ] - }, - { - "id": "copyFilePath", - "title": "copyFilePath", - "keywords": [ - "copy file path", - "copy file" - ] - }, - { - "id": "copyRelativeFilePath", - "title": "copyRelativeFilePath", - "keywords": [ - "copy relative file path", - "copy relative" - ] - }, - { - "id": "workbench.action.files.copyPathOfActiveFile", - "title": "workbench.action.files.copyPathOfActiveFile", - "keywords": [ - "files copy path of active file", - "files copy" - ] - }, - { - "id": "revealInExplorer", - "title": "revealInExplorer", - "keywords": [ - "reveal in explorer", - "reveal in" - ] - }, - { - "id": "explorer.openWith", - "title": "explorer.openWith", - "keywords": [ - "explorer open with", - "explorer open" - ] - }, - { - "id": "workbench.action.files.save", - "title": "workbench.action.files.save", - "keywords": [ - "files save" - ] - }, - { - "id": "workbench.action.files.saveWithoutFormatting", - "title": "workbench.action.files.saveWithoutFormatting", - "keywords": [ - "files save without formatting", - "files save" - ] - }, - { - "id": "workbench.action.files.saveAs", - "title": "workbench.action.files.saveAs", - "keywords": [ - "files save as", - "files save" - ] - }, - { - "id": "saveAll", - "title": "saveAll", - "keywords": [ - "save all" - ] - }, - { - "id": "workbench.files.action.saveAllInGroup", - "title": "workbench.files.action.saveAllInGroup", - "keywords": [ - "workbench files action save all in group", - "workbench files" - ] - }, - { - "id": "workbench.action.files.saveFiles", - "title": "workbench.action.files.saveFiles", - "keywords": [ - "files save files", - "files save" - ] - }, - { - "id": "workbench.action.files.revert", - "title": "workbench.action.files.revert", - "keywords": [ - "files revert" - ] - }, - { - "id": "removeRootFolder", - "title": "removeRootFolder", - "keywords": [ - "remove root folder", - "remove root" - ] - }, - { - "id": "previousCompressedFolder", - "title": "previousCompressedFolder", - "keywords": [ - "previous compressed folder", - "previous compressed" - ] - }, - { - "id": "nextCompressedFolder", - "title": "nextCompressedFolder", - "keywords": [ - "next compressed folder", - "next compressed" - ] - }, - { - "id": "firstCompressedFolder", - "title": "firstCompressedFolder", - "keywords": [ - "first compressed folder", - "first compressed" - ] - }, - { - "id": "lastCompressedFolder", - "title": "lastCompressedFolder", - "keywords": [ - "last compressed folder", - "last compressed" - ] - }, - { - "id": "workbench.action.files.newUntitledFile", - "title": "workbench.action.files.newUntitledFile", - "keywords": [ - "files new untitled file", - "files new" - ] - }, - { - "id": "workbench.action.files.newFile", - "title": "workbench.action.files.newFile", - "keywords": [ - "files new file", - "files new" - ] - }, - { - "id": "workbench.files.action.compareFileWith", - "title": "workbench.files.action.compareFileWith", - "keywords": [ - "workbench files action compare file with", - "workbench files" - ] - }, - { - "id": "workbench.files.action.focusFilesExplorer", - "title": "workbench.files.action.focusFilesExplorer", - "keywords": [ - "workbench files action focus files explorer", - "workbench files" - ] - }, - { - "id": "workbench.files.action.showActiveFileInExplorer", - "title": "workbench.files.action.showActiveFileInExplorer", - "keywords": [ - "workbench files action show active file in explorer", - "workbench files" - ] - }, - { - "id": "workbench.files.action.compareWithClipboard", - "title": "workbench.files.action.compareWithClipboard", - "keywords": [ - "workbench files action compare with clipboard", - "workbench files" - ] - }, - { - "id": "workbench.files.action.compareNewUntitledTextFiles", - "title": "workbench.files.action.compareNewUntitledTextFiles", - "keywords": [ - "workbench files action compare new untitled text files", - "workbench files" - ] - }, - { - "id": "workbench.action.toggleAutoSave", - "title": "workbench.action.toggleAutoSave", - "keywords": [ - "toggle auto save", - "toggle auto" - ] - }, - { - "id": "workbench.action.files.showOpenedFileInNewWindow", - "title": "workbench.action.files.showOpenedFileInNewWindow", - "keywords": [ - "files show opened file in new window", - "files show" - ] - }, - { - "id": "workbench.action.files.setActiveEditorReadonlyInSession", - "title": "workbench.action.files.setActiveEditorReadonlyInSession", - "keywords": [ - "files set active editor readonly in session", - "files set" - ] - }, - { - "id": "workbench.action.files.setActiveEditorWriteableInSession", - "title": "workbench.action.files.setActiveEditorWriteableInSession", - "keywords": [ - "files set active editor writeable in session", - "files set" - ] - }, - { - "id": "workbench.action.files.toggleActiveEditorReadonlyInSession", - "title": "workbench.action.files.toggleActiveEditorReadonlyInSession", - "keywords": [ - "files toggle active editor readonly in session", - "files toggle" - ] - }, - { - "id": "workbench.action.files.resetActiveEditorReadonlyInSession", - "title": "workbench.action.files.resetActiveEditorReadonlyInSession", - "keywords": [ - "files reset active editor readonly in session", - "files reset" - ] - }, - { - "id": "renameFile", - "title": "renameFile", - "keywords": [ - "rename file" - ] - }, - { - "id": "moveFileToTrash", - "title": "moveFileToTrash", - "keywords": [ - "move file to trash", - "move file" - ] - }, - { - "id": "deleteFile", - "title": "deleteFile", - "keywords": [ - "delete file" - ] - }, - { - "id": "filesExplorer.cut", - "title": "filesExplorer.cut", - "keywords": [ - "files explorer cut", - "files explorer" - ] - }, - { - "id": "filesExplorer.copy", - "title": "filesExplorer.copy", - "keywords": [ - "files explorer copy", - "files explorer" - ] - }, - { - "id": "filesExplorer.paste", - "title": "filesExplorer.paste", - "keywords": [ - "files explorer paste", - "files explorer" - ] - }, - { - "id": "filesExplorer.cancelCut", - "title": "filesExplorer.cancelCut", - "keywords": [ - "files explorer cancel cut", - "files explorer" - ] - }, - { - "id": "filesExplorer.openFilePreserveFocus", - "title": "filesExplorer.openFilePreserveFocus", - "keywords": [ - "files explorer open file preserve focus", - "files explorer" - ] - }, - { - "id": "workbench.files.action.acceptLocalChanges", - "title": "workbench.files.action.acceptLocalChanges", - "keywords": [ - "workbench files action accept local changes", - "workbench files" - ] - }, - { - "id": "workbench.files.action.revertLocalChanges", - "title": "workbench.files.action.revertLocalChanges", - "keywords": [ - "workbench files action revert local changes", - "workbench files" - ] - }, - { - "id": "workbench.action.chat.openAgentHostModePicker", - "title": "workbench.action.chat.openAgentHostModePicker", - "keywords": [ - "chat open agent host mode picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openAgentHostAutoApprovePicker", - "title": "workbench.action.chat.openAgentHostAutoApprovePicker", - "keywords": [ - "chat open agent host auto approve picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openAgentHostPermissionModePicker", - "title": "workbench.action.chat.openAgentHostPermissionModePicker", - "keywords": [ - "chat open agent host permission mode picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openAgentHostBranchPicker", - "title": "workbench.action.chat.openAgentHostBranchPicker", - "keywords": [ - "chat open agent host branch picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openAgentHostIsolationPicker", - "title": "workbench.action.chat.openAgentHostIsolationPicker", - "keywords": [ - "chat open agent host isolation picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openAgentHostCustomAgentPicker", - "title": "workbench.action.chat.openAgentHostCustomAgentPicker", - "keywords": [ - "chat open agent host custom agent picker", - "chat open" - ] - }, - { - "id": "workbench.chat.agentHostAgentPicker.configure", - "title": "workbench.chat.agentHostAgentPicker.configure", - "keywords": [ - "workbench chat agent host agent picker configure", - "workbench chat" - ] - }, - { - "id": "chat.inlineResourceAnchor.addFileToChat", - "title": "chat.inlineResourceAnchor.addFileToChat", - "keywords": [ - "chat inline resource anchor add file to chat", - "chat inline" - ] - }, - { - "id": "chat.inlineResourceAnchor.copyResource", - "title": "chat.inlineResourceAnchor.copyResource", - "keywords": [ - "chat inline resource anchor copy resource", - "chat inline" - ] - }, - { - "id": "chat.inlineResourceAnchor.openToSide", - "title": "chat.inlineResourceAnchor.openToSide", - "keywords": [ - "chat inline resource anchor open to side", - "chat inline" - ] - }, - { - "id": "chat.inlineSymbolAnchor.goToDefinition", - "title": "chat.inlineSymbolAnchor.goToDefinition", - "keywords": [ - "chat inline symbol anchor go to definition", - "chat inline" - ] - }, - { - "id": "chat.inlineSymbolAnchor.goToTypeDefinitions", - "title": "chat.inlineSymbolAnchor.goToTypeDefinitions", - "keywords": [ - "chat inline symbol anchor go to type definitions", - "chat inline" - ] - }, - { - "id": "chat.inlineSymbolAnchor.goToImplementations", - "title": "chat.inlineSymbolAnchor.goToImplementations", - "keywords": [ - "chat inline symbol anchor go to implementations", - "chat inline" - ] - }, - { - "id": "chat.inlineSymbolAnchor.goToReferences", - "title": "chat.inlineSymbolAnchor.goToReferences", - "keywords": [ - "chat inline symbol anchor go to references", - "chat inline" - ] - }, - { - "id": "editor.action.inspectTMScopes", - "title": "editor.action.inspectTMScopes", - "keywords": [ - "inspect tmscopes" - ] - }, - { - "id": "chat.toolOutput.save", - "title": "chat.toolOutput.save", - "keywords": [ - "chat tool output save", - "chat tool" - ] - }, - { - "id": "workbench.files.action.createFileFromExplorer", - "title": "workbench.files.action.createFileFromExplorer", - "keywords": [ - "workbench files action create file from explorer", - "workbench files" - ] - }, - { - "id": "workbench.files.action.createFolderFromExplorer", - "title": "workbench.files.action.createFolderFromExplorer", - "keywords": [ - "workbench files action create folder from explorer", - "workbench files" - ] - }, - { - "id": "workbench.files.action.refreshFilesExplorer", - "title": "workbench.files.action.refreshFilesExplorer", - "keywords": [ - "workbench files action refresh files explorer", - "workbench files" - ] - }, - { - "id": "workbench.files.action.collapseExplorerFolders", - "title": "workbench.files.action.collapseExplorerFolders", - "keywords": [ - "workbench files action collapse explorer folders", - "workbench files" - ] - }, - { - "id": "workbench.action.chat.addToChatAction", - "title": "workbench.action.chat.addToChatAction", - "keywords": [ - "chat add to chat action", - "chat add" - ] - }, - { - "id": "workbench.action.chat.copyLink", - "title": "workbench.action.chat.copyLink", - "keywords": [ - "chat copy link", - "chat copy" - ] - }, - { - "id": "workbench.action.terminal.chat.focusChatInstance", - "title": "workbench.action.terminal.chat.focusChatInstance", - "keywords": [ - "terminal chat focus chat instance", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.continueInBackground", - "title": "workbench.action.terminal.chat.continueInBackground", - "keywords": [ - "terminal chat continue in background", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.toggleChatTerminalOutput", - "title": "workbench.action.terminal.chat.toggleChatTerminalOutput", - "keywords": [ - "terminal chat toggle chat terminal output", - "terminal chat" - ] - }, - { - "id": "workbench.action.chat.previousTip", - "title": "workbench.action.chat.previousTip", - "keywords": [ - "chat previous tip", - "chat previous" - ] - }, - { - "id": "workbench.action.chat.nextTip", - "title": "workbench.action.chat.nextTip", - "keywords": [ - "chat next tip", - "chat next" - ] - }, - { - "id": "workbench.action.chat.dismissTipToolbar", - "title": "workbench.action.chat.dismissTipToolbar", - "keywords": [ - "chat dismiss tip toolbar", - "chat dismiss" - ] - }, - { - "id": "workbench.action.chat.dismissTip", - "title": "workbench.action.chat.dismissTip", - "keywords": [ - "chat dismiss tip", - "chat dismiss" - ] - }, - { - "id": "workbench.action.chat.disableTips", - "title": "workbench.action.chat.disableTips", - "keywords": [ - "chat disable tips", - "chat disable" - ] - }, - { - "id": "workbench.action.chat.resetDismissedTips", - "title": "workbench.action.chat.resetDismissedTips", - "keywords": [ - "chat reset dismissed tips", - "chat reset" - ] - }, - { - "id": "inlineChatWidget.statusPlaceholder", - "title": "inlineChatWidget.statusPlaceholder", - "keywords": [ - "inline chat widget status placeholder", - "inline chat" - ] - }, - { - "id": "notebook.action.indentUsingSpaces", - "title": "notebook.action.indentUsingSpaces", - "keywords": [ - "notebook action indent using spaces", - "notebook action" - ] - }, - { - "id": "notebook.action.indentUsingTabs", - "title": "notebook.action.indentUsingTabs", - "keywords": [ - "notebook action indent using tabs", - "notebook action" - ] - }, - { - "id": "notebook.action.changeTabDisplaySize", - "title": "notebook.action.changeTabDisplaySize", - "keywords": [ - "notebook action change tab display size", - "notebook action" - ] - }, - { - "id": "notebook.action.convertIndentationToSpaces", - "title": "notebook.action.convertIndentationToSpaces", - "keywords": [ - "notebook action convert indentation to spaces", - "notebook action" - ] - }, - { - "id": "notebook.action.convertIndentationToTabs", - "title": "notebook.action.convertIndentationToTabs", - "keywords": [ - "notebook action convert indentation to tabs", - "notebook action" - ] - }, - { - "id": "workbench.debug.viewlet.action.addFunctionBreakpointAction", - "title": "workbench.debug.viewlet.action.addFunctionBreakpointAction", - "keywords": [ - "workbench debug viewlet action add function breakpoint action", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.addDataBreakpointOnAddress", - "title": "workbench.debug.viewlet.action.addDataBreakpointOnAddress", - "keywords": [ - "workbench debug viewlet action add data breakpoint on address", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.editDataBreakpointOnAddress", - "title": "workbench.debug.viewlet.action.editDataBreakpointOnAddress", - "keywords": [ - "workbench debug viewlet action edit data breakpoint on address", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.toggleBreakpointsActivatedAction", - "title": "workbench.debug.viewlet.action.toggleBreakpointsActivatedAction", - "keywords": [ - "workbench debug viewlet action toggle breakpoints activated action", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.removeBreakpoint", - "title": "workbench.debug.viewlet.action.removeBreakpoint", - "keywords": [ - "workbench debug viewlet action remove breakpoint", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.removeAllBreakpoints", - "title": "workbench.debug.viewlet.action.removeAllBreakpoints", - "keywords": [ - "workbench debug viewlet action remove all breakpoints", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.enableAllBreakpoints", - "title": "workbench.debug.viewlet.action.enableAllBreakpoints", - "keywords": [ - "workbench debug viewlet action enable all breakpoints", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.disableAllBreakpoints", - "title": "workbench.debug.viewlet.action.disableAllBreakpoints", - "keywords": [ - "workbench debug viewlet action disable all breakpoints", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.reapplyBreakpointsAction", - "title": "workbench.debug.viewlet.action.reapplyBreakpointsAction", - "keywords": [ - "workbench debug viewlet action reapply breakpoints action", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.toggleBreakpointsPresentation", - "title": "workbench.debug.viewlet.action.toggleBreakpointsPresentation", - "keywords": [ - "workbench debug viewlet action toggle breakpoints presentation", - "workbench debug" - ] - }, - { - "id": "debug.editBreakpoint", - "title": "debug.editBreakpoint", - "keywords": [ - "debug edit breakpoint", - "debug edit" - ] - }, - { - "id": "debug.editFunctionBreakpoint", - "title": "debug.editFunctionBreakpoint", - "keywords": [ - "debug edit function breakpoint", - "debug edit" - ] - }, - { - "id": "debug.editFunctionBreakpointHitCount", - "title": "debug.editFunctionBreakpointHitCount", - "keywords": [ - "debug edit function breakpoint hit count", - "debug edit" - ] - }, - { - "id": "debug.editBreakpointMode", - "title": "debug.editBreakpointMode", - "keywords": [ - "debug edit breakpoint mode", - "debug edit" - ] - }, - { - "id": "debug.copyStackTrace", - "title": "debug.copyStackTrace", - "keywords": [ - "debug copy stack trace", - "debug copy" - ] - }, - { - "id": "workbench.action.debug.reverseContinue", - "title": "workbench.action.debug.reverseContinue", - "keywords": [ - "debug reverse continue", - "debug reverse" - ] - }, - { - "id": "workbench.action.debug.stepBack", - "title": "workbench.action.debug.stepBack", - "keywords": [ - "debug step back", - "debug step" - ] - }, - { - "id": "workbench.action.debug.terminateThread", - "title": "workbench.action.debug.terminateThread", - "keywords": [ - "debug terminate thread", - "debug terminate" - ] - }, - { - "id": "debug.jumpToCursor", - "title": "debug.jumpToCursor", - "keywords": [ - "debug jump to cursor", - "debug jump" - ] - }, - { - "id": "workbench.action.debug.callStackTop", - "title": "workbench.action.debug.callStackTop", - "keywords": [ - "debug call stack top", - "debug call" - ] - }, - { - "id": "workbench.action.debug.callStackBottom", - "title": "workbench.action.debug.callStackBottom", - "keywords": [ - "debug call stack bottom", - "debug call" - ] - }, - { - "id": "workbench.action.debug.callStackUp", - "title": "workbench.action.debug.callStackUp", - "keywords": [ - "debug call stack up", - "debug call" - ] - }, - { - "id": "workbench.action.debug.callStackDown", - "title": "workbench.action.debug.callStackDown", - "keywords": [ - "debug call stack down", - "debug call" - ] - }, - { - "id": "workbench.action.debug.nextConsole", - "title": "workbench.action.debug.nextConsole", - "keywords": [ - "debug next console", - "debug next" - ] - }, - { - "id": "workbench.action.debug.prevConsole", - "title": "workbench.action.debug.prevConsole", - "keywords": [ - "debug prev console", - "debug prev" - ] - }, - { - "id": "workbench.action.debug.restart", - "title": "workbench.action.debug.restart", - "keywords": [ - "debug restart" - ] - }, - { - "id": "workbench.action.debug.stepOver", - "title": "workbench.action.debug.stepOver", - "keywords": [ - "debug step over", - "debug step" - ] - }, - { - "id": "workbench.action.debug.stepInto", - "title": "workbench.action.debug.stepInto", - "keywords": [ - "debug step into", - "debug step" - ] - }, - { - "id": "workbench.action.debug.stepOut", - "title": "workbench.action.debug.stepOut", - "keywords": [ - "debug step out", - "debug step" - ] - }, - { - "id": "workbench.action.debug.pause", - "title": "workbench.action.debug.pause", - "keywords": [ - "debug pause" - ] - }, - { - "id": "workbench.action.debug.stepIntoTarget", - "title": "workbench.action.debug.stepIntoTarget", - "keywords": [ - "debug step into target", - "debug step" - ] - }, - { - "id": "workbench.action.debug.disconnect", - "title": "workbench.action.debug.disconnect", - "keywords": [ - "debug disconnect" - ] - }, - { - "id": "workbench.action.debug.disconnectAndSuspend", - "title": "workbench.action.debug.disconnectAndSuspend", - "keywords": [ - "debug disconnect and suspend", - "debug disconnect" - ] - }, - { - "id": "workbench.action.debug.stop", - "title": "workbench.action.debug.stop", - "keywords": [ - "debug stop" - ] - }, - { - "id": "workbench.action.debug.restartFrame", - "title": "workbench.action.debug.restartFrame", - "keywords": [ - "debug restart frame", - "debug restart" - ] - }, - { - "id": "workbench.action.debug.continue", - "title": "workbench.action.debug.continue", - "keywords": [ - "debug continue" - ] - }, - { - "id": "workbench.action.debug.showLoadedScripts", - "title": "workbench.action.debug.showLoadedScripts", - "keywords": [ - "debug show loaded scripts", - "debug show" - ] - }, - { - "id": "debug.startFromConfig", - "title": "debug.startFromConfig", - "keywords": [ - "debug start from config", - "debug start" - ] - }, - { - "id": "workbench.action.debug.focusProcess", - "title": "workbench.action.debug.focusProcess", - "keywords": [ - "debug focus process", - "debug focus" - ] - }, - { - "id": "workbench.action.debug.selectandstart", - "title": "workbench.action.debug.selectandstart", - "keywords": [ - "debug selectandstart" - ] - }, - { - "id": "workbench.action.debug.selectDebugConsole", - "title": "workbench.action.debug.selectDebugConsole", - "keywords": [ - "debug select debug console", - "debug select" - ] - }, - { - "id": "workbench.action.debug.selectDebugSession", - "title": "workbench.action.debug.selectDebugSession", - "keywords": [ - "debug select debug session", - "debug select" - ] - }, - { - "id": "workbench.action.debug.start", - "title": "workbench.action.debug.start", - "keywords": [ - "debug start" - ] - }, - { - "id": "workbench.action.debug.run", - "title": "workbench.action.debug.run", - "keywords": [ - "debug run" - ] - }, - { - "id": "debug.toggleBreakpoint", - "title": "debug.toggleBreakpoint", - "keywords": [ - "debug toggle breakpoint", - "debug toggle" - ] - }, - { - "id": "debug.enableOrDisableBreakpoint", - "title": "debug.enableOrDisableBreakpoint", - "keywords": [ - "debug enable or disable breakpoint", - "debug enable" - ] - }, - { - "id": "debug.renameWatchExpression", - "title": "debug.renameWatchExpression", - "keywords": [ - "debug rename watch expression", - "debug rename" - ] - }, - { - "id": "debug.setWatchExpression", - "title": "debug.setWatchExpression", - "keywords": [ - "debug set watch expression", - "debug set" - ] - }, - { - "id": "debug.setVariable", - "title": "debug.setVariable", - "keywords": [ - "debug set variable", - "debug set" - ] - }, - { - "id": "debug.removeWatchExpression", - "title": "debug.removeWatchExpression", - "keywords": [ - "debug remove watch expression", - "debug remove" - ] - }, - { - "id": "debug.breakWhenValueChanges", - "title": "debug.breakWhenValueChanges", - "keywords": [ - "debug break when value changes", - "debug break" - ] - }, - { - "id": "debug.breakWhenValueIsAccessed", - "title": "debug.breakWhenValueIsAccessed", - "keywords": [ - "debug break when value is accessed", - "debug break" - ] - }, - { - "id": "debug.breakWhenValueIsRead", - "title": "debug.breakWhenValueIsRead", - "keywords": [ - "debug break when value is read", - "debug break" - ] - }, - { - "id": "debug.removeBreakpoint", - "title": "debug.removeBreakpoint", - "keywords": [ - "debug remove breakpoint", - "debug remove" - ] - }, - { - "id": "debug.installAdditionalDebuggers", - "title": "debug.installAdditionalDebuggers", - "keywords": [ - "debug install additional debuggers", - "debug install" - ] - }, - { - "id": "debug.addConfiguration", - "title": "debug.addConfiguration", - "keywords": [ - "debug add configuration", - "debug add" - ] - }, - { - "id": "editor.debug.action.toggleInlineBreakpoint", - "title": "editor.debug.action.toggleInlineBreakpoint", - "keywords": [ - "editor debug action toggle inline breakpoint", - "editor debug" - ] - }, - { - "id": "debug.openBreakpointToSide", - "title": "debug.openBreakpointToSide", - "keywords": [ - "debug open breakpoint to side", - "debug open" - ] - }, - { - "id": "debug.toggleExceptionBreakpoints", - "title": "debug.toggleExceptionBreakpoints", - "keywords": [ - "debug toggle exception breakpoints", - "debug toggle" - ] - }, - { - "id": "debug.openView", - "title": "debug.openView", - "keywords": [ - "debug open view", - "debug open" - ] - }, - { - "id": "debug.attachToCurrentCodeRenderer", - "title": "debug.attachToCurrentCodeRenderer", - "keywords": [ - "debug attach to current code renderer", - "debug attach" - ] - }, - { - "id": "workbench.debug.viewlet.action.copyValue", - "title": "workbench.debug.viewlet.action.copyValue", - "keywords": [ - "workbench debug viewlet action copy value", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.viewMemory", - "title": "workbench.debug.viewlet.action.viewMemory", - "keywords": [ - "workbench debug viewlet action view memory", - "workbench debug" - ] - }, - { - "id": "debug.copyEvaluatePath", - "title": "debug.copyEvaluatePath", - "keywords": [ - "debug copy evaluate path", - "debug copy" - ] - }, - { - "id": "debug.addToWatchExpressions", - "title": "debug.addToWatchExpressions", - "keywords": [ - "debug add to watch expressions", - "debug add" - ] - }, - { - "id": "variables.collapse", - "title": "variables.collapse", - "keywords": [ - "variables collapse" - ] - }, - { - "id": "notebook.clearAllInlineValues", - "title": "notebook.clearAllInlineValues", - "keywords": [ - "notebook clear all inline values", - "notebook clear" - ] - }, - { - "id": "notebook.cell.edit", - "title": "notebook.cell.edit", - "keywords": [ - "notebook cell edit", - "notebook cell" - ] - }, - { - "id": "notebook.cell.quitEdit", - "title": "notebook.cell.quitEdit", - "keywords": [ - "notebook cell quit edit", - "notebook cell" - ] - }, - { - "id": "notebook.quitEditAllCells", - "title": "notebook.quitEditAllCells", - "keywords": [ - "notebook quit edit all cells", - "notebook quit" - ] - }, - { - "id": "notebook.cell.delete", - "title": "notebook.cell.delete", - "keywords": [ - "notebook cell delete", - "notebook cell" - ] - }, - { - "id": "notebook.cell.clearOutputs", - "title": "notebook.cell.clearOutputs", - "keywords": [ - "notebook cell clear outputs", - "notebook cell" - ] - }, - { - "id": "notebook.clearAllCellsOutputs", - "title": "notebook.clearAllCellsOutputs", - "keywords": [ - "notebook clear all cells outputs", - "notebook clear" - ] - }, - { - "id": "notebook.cell.changeLanguage", - "title": "notebook.cell.changeLanguage", - "keywords": [ - "notebook cell change language", - "notebook cell" - ] - }, - { - "id": "notebook.cell.detectLanguage", - "title": "notebook.cell.detectLanguage", - "keywords": [ - "notebook cell detect language", - "notebook cell" - ] - }, - { - "id": "notebook.selectIndentation", - "title": "notebook.selectIndentation", - "keywords": [ - "notebook select indentation", - "notebook select" - ] - }, - { - "id": "notebook.commentSelectedCells", - "title": "notebook.commentSelectedCells", - "keywords": [ - "notebook comment selected cells", - "notebook comment" - ] - }, - { - "id": "notebook.cell.chat.start", - "title": "notebook.cell.chat.start", - "keywords": [ - "notebook cell chat start", - "notebook cell" - ] - }, - { - "id": "notebook.cell.chat.startAtTop", - "title": "notebook.cell.chat.startAtTop", - "keywords": [ - "notebook cell chat start at top", - "notebook cell" - ] - }, - { - "id": "notebook.inlineChat.acceptChangesAndRun", - "title": "notebook.inlineChat.acceptChangesAndRun", - "keywords": [ - "notebook inline chat accept changes and run", - "notebook inline" - ] - }, - { - "id": "notebook.cellOutput.addToChat", - "title": "notebook.cellOutput.addToChat", - "keywords": [ - "notebook cell output add to chat", - "notebook cell" - ] - }, - { - "id": "notebook.chat.selectAndInsertKernelVariable", - "title": "notebook.chat.selectAndInsertKernelVariable", - "keywords": [ - "notebook chat select and insert kernel variable", - "notebook chat" - ] - }, - { - "id": "notebook.openVariablesView", - "title": "notebook.openVariablesView", - "keywords": [ - "notebook open variables view", - "notebook open" - ] - }, - { - "id": "notebook.cell.copy", - "title": "notebook.cell.copy", - "keywords": [ - "notebook cell copy", - "notebook cell" - ] - }, - { - "id": "notebook.cell.cut", - "title": "notebook.cell.cut", - "keywords": [ - "notebook cell cut", - "notebook cell" - ] - }, - { - "id": "notebook.cell.paste", - "title": "notebook.cell.paste", - "keywords": [ - "notebook cell paste", - "notebook cell" - ] - }, - { - "id": "notebook.cell.pasteAbove", - "title": "notebook.cell.pasteAbove", - "keywords": [ - "notebook cell paste above", - "notebook cell" - ] - }, - { - "id": "workbench.action.toggleNotebookClipboardLog", - "title": "workbench.action.toggleNotebookClipboardLog", - "keywords": [ - "toggle notebook clipboard log", - "toggle notebook" - ] - }, - { - "id": "notebook.cell.output.selectAll", - "title": "notebook.cell.output.selectAll", - "keywords": [ - "notebook cell output select all", - "notebook cell" - ] - }, - { - "id": "notebook.hideFind", - "title": "notebook.hideFind", - "keywords": [ - "notebook hide find", - "notebook hide" - ] - }, - { - "id": "notebook.find", - "title": "notebook.find", - "keywords": [ - "notebook find" - ] - }, - { - "id": "notebook.findNext.fromWidget", - "title": "notebook.findNext.fromWidget", - "keywords": [ - "notebook find next from widget", - "notebook find" - ] - }, - { - "id": "notebook.findPrevious.fromWidget", - "title": "notebook.findPrevious.fromWidget", - "keywords": [ - "notebook find previous from widget", - "notebook find" - ] - }, - { - "id": "notebook.selectAllFindMatches", - "title": "notebook.selectAllFindMatches", - "keywords": [ - "notebook select all find matches", - "notebook select" - ] - }, - { - "id": "notebook.addFindMatchToSelection", - "title": "notebook.addFindMatchToSelection", - "keywords": [ - "notebook add find match to selection", - "notebook add" - ] - }, - { - "id": "noteMultiCursor.exit", - "title": "noteMultiCursor.exit", - "keywords": [ - "note multi cursor exit", - "note multi" - ] - }, - { - "id": "noteMultiCursor.deleteLeft", - "title": "noteMultiCursor.deleteLeft", - "keywords": [ - "note multi cursor delete left", - "note multi" - ] - }, - { - "id": "noteMultiCursor.deleteRight", - "title": "noteMultiCursor.deleteRight", - "keywords": [ - "note multi cursor delete right", - "note multi" - ] - }, - { - "id": "notebook.format", - "title": "notebook.format", - "keywords": [ - "notebook format" - ] - }, - { - "id": "notebook.formatCell", - "title": "notebook.formatCell", - "keywords": [ - "notebook format cell", - "notebook format" - ] - }, - { - "id": "workbench.notebook.layout.gettingStarted", - "title": "workbench.notebook.layout.gettingStarted", - "keywords": [ - "workbench notebook layout getting started", - "workbench notebook" - ] - }, - { - "id": "notebook.toggleCellToolbarPosition", - "title": "notebook.toggleCellToolbarPosition", - "keywords": [ - "notebook toggle cell toolbar position", - "notebook toggle" - ] - }, - { - "id": "notebook.cell.nullAction", - "title": "notebook.cell.nullAction", - "keywords": [ - "notebook cell null action", - "notebook cell" - ] - }, - { - "id": "notebook.focusNextEditor", - "title": "notebook.focusNextEditor", - "keywords": [ - "notebook focus next editor", - "notebook focus" - ] - }, - { - "id": "notebook.focusPreviousEditor", - "title": "notebook.focusPreviousEditor", - "keywords": [ - "notebook focus previous editor", - "notebook focus" - ] - }, - { - "id": "notebook.focusTop", - "title": "notebook.focusTop", - "keywords": [ - "notebook focus top", - "notebook focus" - ] - }, - { - "id": "notebook.focusBottom", - "title": "notebook.focusBottom", - "keywords": [ - "notebook focus bottom", - "notebook focus" - ] - }, - { - "id": "notebook.cell.focusInOutput", - "title": "notebook.cell.focusInOutput", - "keywords": [ - "notebook cell focus in output", - "notebook cell" - ] - }, - { - "id": "notebook.cell.focusOutOutput", - "title": "notebook.cell.focusOutOutput", - "keywords": [ - "notebook cell focus out output", - "notebook cell" - ] - }, - { - "id": "notebook.centerActiveCell", - "title": "notebook.centerActiveCell", - "keywords": [ - "notebook center active cell", - "notebook center" - ] - }, - { - "id": "notebook.cell.cursorPageUp", - "title": "notebook.cell.cursorPageUp", - "keywords": [ - "notebook cell cursor page up", - "notebook cell" - ] - }, - { - "id": "notebook.cell.cursorPageUpSelect", - "title": "notebook.cell.cursorPageUpSelect", - "keywords": [ - "notebook cell cursor page up select", - "notebook cell" - ] - }, - { - "id": "notebook.cell.cursorPageDown", - "title": "notebook.cell.cursorPageDown", - "keywords": [ - "notebook cell cursor page down", - "notebook cell" - ] - }, - { - "id": "notebook.cell.cursorPageDownSelect", - "title": "notebook.cell.cursorPageDownSelect", - "keywords": [ - "notebook cell cursor page down select", - "notebook cell" - ] - }, - { - "id": "notebook.setProfile", - "title": "notebook.setProfile", - "keywords": [ - "notebook set profile", - "notebook set" - ] - }, - { - "id": "notebook.cell.moveUp", - "title": "notebook.cell.moveUp", - "keywords": [ - "notebook cell move up", - "notebook cell" - ] - }, - { - "id": "notebook.cell.moveDown", - "title": "notebook.cell.moveDown", - "keywords": [ - "notebook cell move down", - "notebook cell" - ] - }, - { - "id": "notebook.cell.copyUp", - "title": "notebook.cell.copyUp", - "keywords": [ - "notebook cell copy up", - "notebook cell" - ] - }, - { - "id": "notebook.cell.copyDown", - "title": "notebook.cell.copyDown", - "keywords": [ - "notebook cell copy down", - "notebook cell" - ] - }, - { - "id": "notebook.cell.split", - "title": "notebook.cell.split", - "keywords": [ - "notebook cell split", - "notebook cell" - ] - }, - { - "id": "notebook.cell.joinAbove", - "title": "notebook.cell.joinAbove", - "keywords": [ - "notebook cell join above", - "notebook cell" - ] - }, - { - "id": "notebook.cell.joinBelow", - "title": "notebook.cell.joinBelow", - "keywords": [ - "notebook cell join below", - "notebook cell" - ] - }, - { - "id": "notebook.cell.joinSelected", - "title": "notebook.cell.joinSelected", - "keywords": [ - "notebook cell join selected", - "notebook cell" - ] - }, - { - "id": "notebook.cell.changeToCode", - "title": "notebook.cell.changeToCode", - "keywords": [ - "notebook cell change to code", - "notebook cell" - ] - }, - { - "id": "notebook.cell.changeToMarkdown", - "title": "notebook.cell.changeToMarkdown", - "keywords": [ - "notebook cell change to markdown", - "notebook cell" - ] - }, - { - "id": "notebook.cell.collapseCellInput", - "title": "notebook.cell.collapseCellInput", - "keywords": [ - "notebook cell collapse cell input", - "notebook cell" - ] - }, - { - "id": "notebook.cell.expandCellInput", - "title": "notebook.cell.expandCellInput", - "keywords": [ - "notebook cell expand cell input", - "notebook cell" - ] - }, - { - "id": "notebook.cell.collapseCellOutput", - "title": "notebook.cell.collapseCellOutput", - "keywords": [ - "notebook cell collapse cell output", - "notebook cell" - ] - }, - { - "id": "notebook.cell.expandCellOutput", - "title": "notebook.cell.expandCellOutput", - "keywords": [ - "notebook cell expand cell output", - "notebook cell" - ] - }, - { - "id": "notebook.cell.toggleOutputs", - "title": "notebook.cell.toggleOutputs", - "keywords": [ - "notebook cell toggle outputs", - "notebook cell" - ] - }, - { - "id": "notebook.cell.collapseAllCellInputs", - "title": "notebook.cell.collapseAllCellInputs", - "keywords": [ - "notebook cell collapse all cell inputs", - "notebook cell" - ] - }, - { - "id": "notebook.cell.expandAllCellInputs", - "title": "notebook.cell.expandAllCellInputs", - "keywords": [ - "notebook cell expand all cell inputs", - "notebook cell" - ] - }, - { - "id": "notebook.cell.collapseAllCellOutputs", - "title": "notebook.cell.collapseAllCellOutputs", - "keywords": [ - "notebook cell collapse all cell outputs", - "notebook cell" - ] - }, - { - "id": "notebook.cell.expandAllCellOutputs", - "title": "notebook.cell.expandAllCellOutputs", - "keywords": [ - "notebook cell expand all cell outputs", - "notebook cell" - ] - }, - { - "id": "notebook.cell.toggleOutputScrolling", - "title": "notebook.cell.toggleOutputScrolling", - "keywords": [ - "notebook cell toggle output scrolling", - "notebook cell" - ] - }, - { - "id": "notebook.toggleLayoutTroubleshoot", - "title": "notebook.toggleLayoutTroubleshoot", - "keywords": [ - "notebook toggle layout troubleshoot", - "notebook toggle" - ] - }, - { - "id": "notebook.inspectLayout", - "title": "notebook.inspectLayout", - "keywords": [ - "notebook inspect layout", - "notebook inspect" - ] - }, - { - "id": "notebook.clearNotebookEdtitorTypeCache", - "title": "notebook.clearNotebookEdtitorTypeCache", - "keywords": [ - "notebook clear notebook edtitor type cache", - "notebook clear" - ] - }, - { - "id": "breakpointWidget.action.acceptInput", - "title": "breakpointWidget.action.acceptInput", - "keywords": [ - "breakpoint widget action accept input", - "breakpoint widget" - ] - }, - { - "id": "closeBreakpointWidget", - "title": "closeBreakpointWidget", - "keywords": [ - "close breakpoint widget", - "close breakpoint" - ] - }, - { - "id": "notebook.cell.openFailureActions", - "title": "notebook.cell.openFailureActions", - "keywords": [ - "notebook cell open failure actions", - "notebook cell" - ] - }, - { - "id": "notebook.cell.chat.fixError", - "title": "notebook.cell.chat.fixError", - "keywords": [ - "notebook cell chat fix error", - "notebook cell" - ] - }, - { - "id": "notebook.cell.chat.explainError", - "title": "notebook.cell.chat.explainError", - "keywords": [ - "notebook cell chat explain error", - "notebook cell" - ] - }, - { - "id": "notebook.diff.openFile", - "title": "notebook.diff.openFile", - "keywords": [ - "notebook diff open file", - "notebook diff" - ] - }, - { - "id": "notebook.diff.cell.toggleCollapseUnchangedRegions", - "title": "notebook.diff.cell.toggleCollapseUnchangedRegions", - "keywords": [ - "notebook diff cell toggle collapse unchanged regions", - "notebook diff" - ] - }, - { - "id": "notebook.diff.switchToText", - "title": "notebook.diff.switchToText", - "keywords": [ - "notebook diff switch to text", - "notebook diff" - ] - }, - { - "id": "notebook.diffEditor.showUnchangedCells", - "title": "notebook.diffEditor.showUnchangedCells", - "keywords": [ - "notebook diff editor show unchanged cells", - "notebook diff" - ] - }, - { - "id": "notebook.diffEditor.hideUnchangedCells", - "title": "notebook.diffEditor.hideUnchangedCells", - "keywords": [ - "notebook diff editor hide unchanged cells", - "notebook diff" - ] - }, - { - "id": "notebook.diffEditor.2.goToCell", - "title": "notebook.diffEditor.2.goToCell", - "keywords": [ - "notebook diff editor 2 go to cell", - "notebook diff" - ] - }, - { - "id": "notebook.diff.revertMetadata", - "title": "notebook.diff.revertMetadata", - "keywords": [ - "notebook diff revert metadata", - "notebook diff" - ] - }, - { - "id": "notebook.diffEditor.2.cell.revertInput", - "title": "notebook.diffEditor.2.cell.revertInput", - "keywords": [ - "notebook diff editor 2 cell revert input", - "notebook diff" - ] - }, - { - "id": "notebook.diffEditor.2.cell.revertOutputs", - "title": "notebook.diffEditor.2.cell.revertOutputs", - "keywords": [ - "notebook diff editor 2 cell revert outputs", - "notebook diff" - ] - }, - { - "id": "notebook.diffEditor.2.cell.revertMetadata", - "title": "notebook.diffEditor.2.cell.revertMetadata", - "keywords": [ - "notebook diff editor 2 cell revert metadata", - "notebook diff" - ] - }, - { - "id": "notebook.diff.cell.revertMetadata", - "title": "notebook.diff.cell.revertMetadata", - "keywords": [ - "notebook diff cell revert metadata", - "notebook diff" - ] - }, - { - "id": "notebook.diff.cell.switchOutputRenderingStyleToText", - "title": "notebook.diff.cell.switchOutputRenderingStyleToText", - "keywords": [ - "notebook diff cell switch output rendering style to text", - "notebook diff" - ] - }, - { - "id": "notebook.diff.cell.revertOutputs", - "title": "notebook.diff.cell.revertOutputs", - "keywords": [ - "notebook diff cell revert outputs", - "notebook diff" - ] - }, - { - "id": "notebook.toggle.diff.cell.ignoreTrimWhitespace", - "title": "notebook.toggle.diff.cell.ignoreTrimWhitespace", - "keywords": [ - "notebook toggle diff cell ignore trim whitespace", - "notebook toggle" - ] - }, - { - "id": "notebook.diff.cell.revertInput", - "title": "notebook.diff.cell.revertInput", - "keywords": [ - "notebook diff cell revert input", - "notebook diff" - ] - }, - { - "id": "notebook.diff.showOutputs", - "title": "notebook.diff.showOutputs", - "keywords": [ - "notebook diff show outputs", - "notebook diff" - ] - }, - { - "id": "notebook.diff.showMetadata", - "title": "notebook.diff.showMetadata", - "keywords": [ - "notebook diff show metadata", - "notebook diff" - ] - }, - { - "id": "notebook.diff.action.previous", - "title": "notebook.diff.action.previous", - "keywords": [ - "notebook diff action previous", - "notebook diff" - ] - }, - { - "id": "notebook.diff.action.next", - "title": "notebook.diff.action.next", - "keywords": [ - "notebook diff action next", - "notebook diff" - ] - }, - { - "id": "notebook.diff.inline.toggle", - "title": "notebook.diff.inline.toggle", - "keywords": [ - "notebook diff inline toggle", - "notebook diff" - ] - }, - { - "id": "notebook.clearNotebookKernelsMRUCache", - "title": "notebook.clearNotebookKernelsMRUCache", - "keywords": [ - "notebook clear notebook kernels mrucache", - "notebook clear" - ] - }, - { - "id": "editor.action.formatDocument.multiple", - "title": "editor.action.formatDocument.multiple", - "keywords": [ - "format document multiple", - "format document" - ] - }, - { - "id": "editor.action.formatSelection.multiple", - "title": "editor.action.formatSelection.multiple", - "keywords": [ - "format selection multiple", - "format selection" - ] - }, - { - "id": "workbench.action.gotoSymbol", - "title": "workbench.action.gotoSymbol", - "keywords": [ - "goto symbol" - ] - }, - { - "id": "toggle.window.commandCenter", - "title": "toggle.window.commandCenter", - "keywords": [ - "toggle window command center", - "toggle window" - ] - }, - { - "id": "toggle.workbench.navigationControl.enabled", - "title": "toggle.workbench.navigationControl.enabled", - "keywords": [ - "toggle workbench navigation control enabled", - "toggle workbench" - ] - }, - { - "id": "toggle.workbench.layoutControl.enabled", - "title": "toggle.workbench.layoutControl.enabled", - "keywords": [ - "toggle workbench layout control enabled", - "toggle workbench" - ] - }, - { - "id": "toggle.workbench.notifications.showInTitleBar", - "title": "toggle.workbench.notifications.showInTitleBar", - "keywords": [ - "toggle workbench notifications show in title bar", - "toggle workbench" - ] - }, - { - "id": "toggle.window.customTitleBarVisibility", - "title": "toggle.window.customTitleBarVisibility", - "keywords": [ - "toggle window custom title bar visibility", - "toggle window" - ] - }, - { - "id": "toggle.window.customTitleBarVisibility.windowed", - "title": "toggle.window.customTitleBarVisibility.windowed", - "keywords": [ - "toggle window custom title bar visibility windowed", - "toggle window" - ] - }, - { - "id": "toggle.toggleCustomTitleBar", - "title": "toggle.toggleCustomTitleBar", - "keywords": [ - "toggle toggle custom title bar", - "toggle toggle" - ] - }, - { - "id": "showCustomTitleBar", - "title": "showCustomTitleBar", - "keywords": [ - "show custom title bar", - "show custom" - ] - }, - { - "id": "hideCustomTitleBar", - "title": "hideCustomTitleBar", - "keywords": [ - "hide custom title bar", - "hide custom" - ] - }, - { - "id": "hideCustomTitleBarInFullScreen", - "title": "hideCustomTitleBarInFullScreen", - "keywords": [ - "hide custom title bar in full screen", - "hide custom" - ] - }, - { - "id": "toggle.workbench.editor.editorActionsLocation", - "title": "toggle.workbench.editor.editorActionsLocation", - "keywords": [ - "toggle workbench editor editor actions location", - "toggle workbench" - ] - }, - { - "id": "workbench.action.unifiedQuickAccess", - "title": "workbench.action.unifiedQuickAccess", - "keywords": [ - "unified quick access", - "unified quick" - ] - }, - { - "id": "workbench.action.showAgentSessionsQuickAccess", - "title": "workbench.action.showAgentSessionsQuickAccess", - "keywords": [ - "show agent sessions quick access", - "show agent" - ] - }, - { - "id": "workbench.action.showCommandsQuickAccess", - "title": "workbench.action.showCommandsQuickAccess", - "keywords": [ - "show commands quick access", - "show commands" - ] - }, - { - "id": "workbench.action.showFilesQuickAccess", - "title": "workbench.action.showFilesQuickAccess", - "keywords": [ - "show files quick access", - "show files" - ] - }, - { - "id": "agentSession.enterAgentSessionProjection", - "title": "agentSession.enterAgentSessionProjection", - "keywords": [ - "agent session enter agent session projection", - "agent session" - ] - }, - { - "id": "agentSession.exitAgentSessionProjection", - "title": "agentSession.exitAgentSessionProjection", - "keywords": [ - "agent session exit agent session projection", - "agent session" - ] - }, - { - "id": "toggle.chat.unifiedAgentsBar.enabled", - "title": "toggle.chat.unifiedAgentsBar.enabled", - "keywords": [ - "toggle chat unified agents bar enabled", - "toggle chat" - ] - }, - { - "id": "workbench.action.chat.focusAgentSessionsViewer", - "title": "workbench.action.chat.focusAgentSessionsViewer", - "keywords": [ - "chat focus agent sessions viewer", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.history", - "title": "workbench.action.chat.history", - "keywords": [ - "chat history" - ] - }, - { - "id": "workbench.action.chat.archiveAllAgentSessions", - "title": "workbench.action.chat.archiveAllAgentSessions", - "keywords": [ - "chat archive all agent sessions", - "chat archive" - ] - }, - { - "id": "workbench.action.chat.markAllAgentSessionsRead", - "title": "workbench.action.chat.markAllAgentSessionsRead", - "keywords": [ - "chat mark all agent sessions read", - "chat mark" - ] - }, - { - "id": "agentSessionSection.archive", - "title": "agentSessionSection.archive", - "keywords": [ - "agent session section archive", - "agent session" - ] - }, - { - "id": "agentSessionSection.unarchive", - "title": "agentSessionSection.unarchive", - "keywords": [ - "agent session section unarchive", - "agent session" - ] - }, - { - "id": "agentSessionSection.markRead", - "title": "agentSessionSection.markRead", - "keywords": [ - "agent session section mark read", - "agent session" - ] - }, - { - "id": "agentSessionSection.collapseAll", - "title": "agentSessionSection.collapseAll", - "keywords": [ - "agent session section collapse all", - "agent session" - ] - }, - { - "id": "agentSession.archive", - "title": "agentSession.archive", - "keywords": [ - "agent session archive", - "agent session" - ] - }, - { - "id": "agentSession.unarchive", - "title": "agentSession.unarchive", - "keywords": [ - "agent session unarchive", - "agent session" - ] - }, - { - "id": "agentSession.pin", - "title": "agentSession.pin", - "keywords": [ - "agent session pin", - "agent session" - ] - }, - { - "id": "agentSession.unpin", - "title": "agentSession.unpin", - "keywords": [ - "agent session unpin", - "agent session" - ] - }, - { - "id": "agentSession.rename", - "title": "agentSession.rename", - "keywords": [ - "agent session rename", - "agent session" - ] - }, - { - "id": "agentSession.delete", - "title": "agentSession.delete", - "keywords": [ - "agent session delete", - "agent session" - ] - }, - { - "id": "workbench.action.chat.clearHistory", - "title": "workbench.action.chat.clearHistory", - "keywords": [ - "chat clear history", - "chat clear" - ] - }, - { - "id": "agentSession.markUnread", - "title": "agentSession.markUnread", - "keywords": [ - "agent session mark unread", - "agent session" - ] - }, - { - "id": "agentSession.markRead", - "title": "agentSession.markRead", - "keywords": [ - "agent session mark read", - "agent session" - ] - }, - { - "id": "workbench.action.chat.openSessionInNewWindow", - "title": "workbench.action.chat.openSessionInNewWindow", - "keywords": [ - "chat open session in new window", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openSessionInEditorGroup", - "title": "workbench.action.chat.openSessionInEditorGroup", - "keywords": [ - "chat open session in editor group", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openSessionInNewEditorGroup", - "title": "workbench.action.chat.openSessionInNewEditorGroup", - "keywords": [ - "chat open session in new editor group", - "chat open" - ] - }, - { - "id": "agentSessionsViewer.refresh", - "title": "agentSessionsViewer.refresh", - "keywords": [ - "agent sessions viewer refresh", - "agent sessions" - ] - }, - { - "id": "agentSessionsViewer.find", - "title": "agentSessionsViewer.find", - "keywords": [ - "agent sessions viewer find", - "agent sessions" - ] - }, - { - "id": "agentSessions.showAgentSessionsSidebar", - "title": "agentSessions.showAgentSessionsSidebar", - "keywords": [ - "agent sessions show agent sessions sidebar", - "agent sessions" - ] - }, - { - "id": "agentSessions.hideAgentSessionsSidebar", - "title": "agentSessions.hideAgentSessionsSidebar", - "keywords": [ - "agent sessions hide agent sessions sidebar", - "agent sessions" - ] - }, - { - "id": "agentSessions.toggleAgentSessionsSidebar", - "title": "agentSessions.toggleAgentSessionsSidebar", - "keywords": [ - "agent sessions toggle agent sessions sidebar", - "agent sessions" - ] - }, - { - "id": "workbench.action.chat.toggleShowAgentSessions", - "title": "workbench.action.chat.toggleShowAgentSessions", - "keywords": [ - "chat toggle show agent sessions", - "chat toggle" - ] - }, - { - "id": "workbench.action.chat.setAgentSessionsOrientationStacked", - "title": "workbench.action.chat.setAgentSessionsOrientationStacked", - "keywords": [ - "chat set agent sessions orientation stacked", - "chat set" - ] - }, - { - "id": "workbench.action.chat.setAgentSessionsOrientationSideBySide", - "title": "workbench.action.chat.setAgentSessionsOrientationSideBySide", - "keywords": [ - "chat set agent sessions orientation side by side", - "chat set" - ] - }, - { - "id": "aiCustomizationManagement.openFile", - "title": "aiCustomizationManagement.openFile", - "keywords": [ - "ai customization management open file", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.runPrompt", - "title": "aiCustomizationManagement.runPrompt", - "keywords": [ - "ai customization management run prompt", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.revealInOS", - "title": "aiCustomizationManagement.revealInOS", - "keywords": [ - "ai customization management reveal in os", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.delete", - "title": "aiCustomizationManagement.delete", - "keywords": [ - "ai customization management delete", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.copyPath", - "title": "aiCustomizationManagement.copyPath", - "keywords": [ - "ai customization management copy path", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.installChatCustomizationExtension", - "title": "aiCustomizationManagement.installChatCustomizationExtension", - "keywords": [ - "ai customization management install chat customization extension", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.uninstallPlugin", - "title": "aiCustomizationManagement.uninstallPlugin", - "keywords": [ - "ai customization management uninstall plugin", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.showPlugin", - "title": "aiCustomizationManagement.showPlugin", - "keywords": [ - "ai customization management show plugin", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.disableItem", - "title": "aiCustomizationManagement.disableItem", - "keywords": [ - "ai customization management disable item", - "ai customization" - ] - }, - { - "id": "aiCustomizationManagement.enableItem", - "title": "aiCustomizationManagement.enableItem", - "keywords": [ - "ai customization management enable item", - "ai customization" - ] - }, - { - "id": "workbench.action.chat.resetGrowthSession", - "title": "workbench.action.chat.resetGrowthSession", - "keywords": [ - "chat reset growth session", - "chat reset" - ] - }, - { - "id": "search.action.clearHistory", - "title": "search.action.clearHistory", - "keywords": [ - "search action clear history", - "search action" - ] - }, - { - "id": "search.action.cancel", - "title": "search.action.cancel", - "keywords": [ - "search action cancel", - "search action" - ] - }, - { - "id": "search.action.refreshSearchResults", - "title": "search.action.refreshSearchResults", - "keywords": [ - "search action refresh search results", - "search action" - ] - }, - { - "id": "search.action.collapseSearchResults", - "title": "search.action.collapseSearchResults", - "keywords": [ - "search action collapse search results", - "search action" - ] - }, - { - "id": "search.action.expandSearchResults", - "title": "search.action.expandSearchResults", - "keywords": [ - "search action expand search results", - "search action" - ] - }, - { - "id": "search.action.clearSearchResults", - "title": "search.action.clearSearchResults", - "keywords": [ - "search action clear search results", - "search action" - ] - }, - { - "id": "search.action.viewAsTree", - "title": "search.action.viewAsTree", - "keywords": [ - "search action view as tree", - "search action" - ] - }, - { - "id": "search.action.viewAsList", - "title": "search.action.viewAsList", - "keywords": [ - "search action view as list", - "search action" - ] - }, - { - "id": "search.action.searchWithAI", - "title": "search.action.searchWithAI", - "keywords": [ - "search action search with ai", - "search action" - ] - }, - { - "id": "workbench.action.chat.assignSelectedAgent", - "title": "workbench.action.chat.assignSelectedAgent", - "keywords": [ - "chat assign selected agent", - "chat assign" - ] - }, - { - "id": "workbench.action.chat.startParameterizedPrompt", - "title": "workbench.action.chat.startParameterizedPrompt", - "keywords": [ - "chat start parameterized prompt", - "chat start" - ] - }, - { - "id": "workbench.action.chat.open", - "title": "workbench.action.chat.open", - "keywords": [ - "chat open" - ] - }, - { - "id": "workbench.action.chat.openask", - "title": "workbench.action.chat.openask", - "keywords": [ - "chat openask" - ] - }, - { - "id": "workbench.action.chat.openagent", - "title": "workbench.action.chat.openagent", - "keywords": [ - "chat openagent" - ] - }, - { - "id": "workbench.action.chat.openedit", - "title": "workbench.action.chat.openedit", - "keywords": [ - "chat openedit" - ] - }, - { - "id": "workbench.action.chat.toggle", - "title": "workbench.action.chat.toggle", - "keywords": [ - "chat toggle" - ] - }, - { - "id": "workbench.action.openChat", - "title": "workbench.action.openChat", - "keywords": [ - "open chat" - ] - }, - { - "id": "workbench.action.openChat.copilotIcon", - "title": "workbench.action.openChat.copilotIcon", - "keywords": [ - "open chat copilot icon", - "open chat" - ] - }, - { - "id": "workbench.action.openChat.newSessionIcon", - "title": "workbench.action.openChat.newSessionIcon", - "keywords": [ - "open chat new session icon", - "open chat" - ] - }, - { - "id": "workbench.action.openChat.commentIcon", - "title": "workbench.action.openChat.commentIcon", - "keywords": [ - "open chat comment icon", - "open chat" - ] - }, - { - "id": "workbench.action.openChatToSide", - "title": "workbench.action.openChatToSide", - "keywords": [ - "open chat to side", - "open chat" - ] - }, - { - "id": "workbench.action.newChatWindow", - "title": "workbench.action.newChatWindow", - "keywords": [ - "new chat window", - "new chat" - ] - }, - { - "id": "workbench.action.chat.clearInputHistory", - "title": "workbench.action.chat.clearInputHistory", - "keywords": [ - "chat clear input history", - "chat clear" - ] - }, - { - "id": "chat.action.focus", - "title": "chat.action.focus", - "keywords": [ - "chat action focus", - "chat action" - ] - }, - { - "id": "workbench.chat.action.focusLastFocused", - "title": "workbench.chat.action.focusLastFocused", - "keywords": [ - "workbench chat action focus last focused", - "workbench chat" - ] - }, - { - "id": "workbench.action.chat.focusInput", - "title": "workbench.action.chat.focusInput", - "keywords": [ - "chat focus input", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.focusTodosView", - "title": "workbench.action.chat.focusTodosView", - "keywords": [ - "chat focus todos view", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.focusQuestionCarousel", - "title": "workbench.action.chat.focusQuestionCarousel", - "keywords": [ - "chat focus question carousel", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.previousQuestion", - "title": "workbench.action.chat.previousQuestion", - "keywords": [ - "chat previous question", - "chat previous" - ] - }, - { - "id": "workbench.action.chat.nextQuestion", - "title": "workbench.action.chat.nextQuestion", - "keywords": [ - "chat next question", - "chat next" - ] - }, - { - "id": "workbench.action.chat.focusQuestionCarouselTerminal", - "title": "workbench.action.chat.focusQuestionCarouselTerminal", - "keywords": [ - "chat focus question carousel terminal", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.focusTip", - "title": "workbench.action.chat.focusTip", - "keywords": [ - "chat focus tip", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.showContextUsage", - "title": "workbench.action.chat.showContextUsage", - "keywords": [ - "chat show context usage", - "chat show" - ] - }, - { - "id": "workbench.action.chat.toggleShowContextUsage", - "title": "workbench.action.chat.toggleShowContextUsage", - "keywords": [ - "chat toggle show context usage", - "chat toggle" - ] - }, - { - "id": "workbench.action.chat.manageSettings", - "title": "workbench.action.chat.manageSettings", - "keywords": [ - "chat manage settings", - "chat manage" - ] - }, - { - "id": "workbench.action.chat.showExtensionsUsingCopilot", - "title": "workbench.action.chat.showExtensionsUsingCopilot", - "keywords": [ - "chat show extensions using copilot", - "chat show" - ] - }, - { - "id": "workbench.action.chat.configureCodeCompletions", - "title": "workbench.action.chat.configureCodeCompletions", - "keywords": [ - "chat configure code completions", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.openQuotaExceededDialog", - "title": "workbench.action.chat.openQuotaExceededDialog", - "keywords": [ - "chat open quota exceeded dialog", - "chat open" - ] - }, - { - "id": "workbench.action.chat.resetTrustedTools", - "title": "workbench.action.chat.resetTrustedTools", - "keywords": [ - "chat reset trusted tools", - "chat reset" - ] - }, - { - "id": "workbench.action.chat.generateAgentInstructions", - "title": "workbench.action.chat.generateAgentInstructions", - "keywords": [ - "chat generate agent instructions", - "chat generate" - ] - }, - { - "id": "workbench.action.chat.generateOnDemandInstructions", - "title": "workbench.action.chat.generateOnDemandInstructions", - "keywords": [ - "chat generate on demand instructions", - "chat generate" - ] - }, - { - "id": "workbench.action.chat.generatePrompt", - "title": "workbench.action.chat.generatePrompt", - "keywords": [ - "chat generate prompt", - "chat generate" - ] - }, - { - "id": "workbench.action.chat.generateSkill", - "title": "workbench.action.chat.generateSkill", - "keywords": [ - "chat generate skill", - "chat generate" - ] - }, - { - "id": "workbench.action.chat.generateAgent", - "title": "workbench.action.chat.generateAgent", - "keywords": [ - "chat generate agent", - "chat generate" - ] - }, - { - "id": "workbench.action.chat.generateHook", - "title": "workbench.action.chat.generateHook", - "keywords": [ - "chat generate hook", - "chat generate" - ] - }, - { - "id": "workbench.action.chat.insertForkConversationCommand", - "title": "workbench.action.chat.insertForkConversationCommand", - "keywords": [ - "chat insert fork conversation command", - "chat insert" - ] - }, - { - "id": "workbench.action.chat.insertTroubleshootCommand", - "title": "workbench.action.chat.insertTroubleshootCommand", - "keywords": [ - "chat insert troubleshoot command", - "chat insert" - ] - }, - { - "id": "workbench.action.chat.openFeatureSettings", - "title": "workbench.action.chat.openFeatureSettings", - "keywords": [ - "chat open feature settings", - "chat open" - ] - }, - { - "id": "workbench.action.chat.focusConfirmation", - "title": "workbench.action.chat.focusConfirmation", - "keywords": [ - "chat focus confirmation", - "chat focus" - ] - }, - { - "id": "workbench.action.chat.toggleThinkingContentAccessibleView", - "title": "workbench.action.chat.toggleThinkingContentAccessibleView", - "keywords": [ - "chat toggle thinking content accessible view", - "chat toggle" - ] - }, - { - "id": "workbench.action.chat.copyAll", - "title": "workbench.action.chat.copyAll", - "keywords": [ - "chat copy all", - "chat copy" - ] - }, - { - "id": "workbench.action.chat.copyItem", - "title": "workbench.action.chat.copyItem", - "keywords": [ - "chat copy item", - "chat copy" - ] - }, - { - "id": "workbench.action.chat.copyFinalResponse", - "title": "workbench.action.chat.copyFinalResponse", - "keywords": [ - "chat copy final response", - "chat copy" - ] - }, - { - "id": "workbench.action.chat.copyKatexMathSource", - "title": "workbench.action.chat.copyKatexMathSource", - "keywords": [ - "chat copy katex math source", - "chat copy" - ] - }, - { - "id": "workbench.action.chat.openAgentDebugPanel", - "title": "workbench.action.chat.openAgentDebugPanel", - "keywords": [ - "chat open agent debug panel", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openAgentDebugPanelForSession", - "title": "workbench.action.chat.openAgentDebugPanelForSession", - "keywords": [ - "chat open agent debug panel for session", - "chat open" - ] - }, - { - "id": "workbench.action.chat.exportAgentDebugLog", - "title": "workbench.action.chat.exportAgentDebugLog", - "keywords": [ - "chat export agent debug log", - "chat export" - ] - }, - { - "id": "workbench.action.chat.importAgentDebugLog", - "title": "workbench.action.chat.importAgentDebugLog", - "keywords": [ - "chat import agent debug log", - "chat import" - ] - }, - { - "id": "workbench.action.chat.copyCodeBlock", - "title": "workbench.action.chat.copyCodeBlock", - "keywords": [ - "chat copy code block", - "chat copy" - ] - }, - { - "id": "workbench.action.chat.applyInEditor", - "title": "workbench.action.chat.applyInEditor", - "keywords": [ - "chat apply in editor", - "chat apply" - ] - }, - { - "id": "workbench.action.chat.insertCodeBlock", - "title": "workbench.action.chat.insertCodeBlock", - "keywords": [ - "chat insert code block", - "chat insert" - ] - }, - { - "id": "workbench.action.chat.insertIntoNewFile", - "title": "workbench.action.chat.insertIntoNewFile", - "keywords": [ - "chat insert into new file", - "chat insert" - ] - }, - { - "id": "workbench.action.chat.runInTerminal", - "title": "workbench.action.chat.runInTerminal", - "keywords": [ - "chat run in terminal", - "chat run" - ] - }, - { - "id": "workbench.action.chat.nextCodeBlock", - "title": "workbench.action.chat.nextCodeBlock", - "keywords": [ - "chat next code block", - "chat next" - ] - }, - { - "id": "workbench.action.chat.previousCodeBlock", - "title": "workbench.action.chat.previousCodeBlock", - "keywords": [ - "chat previous code block", - "chat previous" - ] - }, - { - "id": "workbench.action.chat.applyCompareEdits", - "title": "workbench.action.chat.applyCompareEdits", - "keywords": [ - "chat apply compare edits", - "chat apply" - ] - }, - { - "id": "workbench.action.chat.discardCompareEdits", - "title": "workbench.action.chat.discardCompareEdits", - "keywords": [ - "chat discard compare edits", - "chat discard" - ] - }, - { - "id": "workbench.action.chat.toggleCompareBlockDiffViewMode", - "title": "workbench.action.chat.toggleCompareBlockDiffViewMode", - "keywords": [ - "chat toggle compare block diff view mode", - "chat toggle" - ] - }, - { - "id": "workbench.action.chat.openCompareBlockInDiffEditor", - "title": "workbench.action.chat.openCompareBlockInDiffEditor", - "keywords": [ - "chat open compare block in diff editor", - "chat open" - ] - }, - { - "id": "workbench.action.chat.nextFileTree", - "title": "workbench.action.chat.nextFileTree", - "keywords": [ - "chat next file tree", - "chat next" - ] - }, - { - "id": "workbench.action.chat.previousFileTree", - "title": "workbench.action.chat.previousFileTree", - "keywords": [ - "chat previous file tree", - "chat previous" - ] - }, - { - "id": "workbench.action.chat.nextUserPrompt", - "title": "workbench.action.chat.nextUserPrompt", - "keywords": [ - "chat next user prompt", - "chat next" - ] - }, - { - "id": "workbench.action.chat.previousUserPrompt", - "title": "workbench.action.chat.previousUserPrompt", - "keywords": [ - "chat previous user prompt", - "chat previous" - ] - }, - { - "id": "workbench.action.chat.markHelpful", - "title": "workbench.action.chat.markHelpful", - "keywords": [ - "chat mark helpful", - "chat mark" - ] - }, - { - "id": "workbench.action.chat.markUnhelpful", - "title": "workbench.action.chat.markUnhelpful", - "keywords": [ - "chat mark unhelpful", - "chat mark" - ] - }, - { - "id": "workbench.action.chat.reportIssueForBug", - "title": "workbench.action.chat.reportIssueForBug", - "keywords": [ - "chat report issue for bug", - "chat report" - ] - }, - { - "id": "workbench.action.chat.retry", - "title": "workbench.action.chat.retry", - "keywords": [ - "chat retry" - ] - }, - { - "id": "workbench.action.chat.insertIntoNotebook", - "title": "workbench.action.chat.insertIntoNotebook", - "keywords": [ - "chat insert into notebook", - "chat insert" - ] - }, - { - "id": "workbench.action.chat.submit", - "title": "workbench.action.chat.submit", - "keywords": [ - "chat submit" - ] - }, - { - "id": "workbench.action.edits.submit", - "title": "workbench.action.edits.submit", - "keywords": [ - "edits submit" - ] - }, - { - "id": "workbench.action.chat.submitWithoutDispatching", - "title": "workbench.action.chat.submitWithoutDispatching", - "keywords": [ - "chat submit without dispatching", - "chat submit" - ] - }, - { - "id": "workbench.action.chat.cancel", - "title": "workbench.action.chat.cancel", - "keywords": [ - "chat cancel" - ] - }, - { - "id": "workbench.action.chat.sendToNewChat", - "title": "workbench.action.chat.sendToNewChat", - "keywords": [ - "chat send to new chat", - "chat send" - ] - }, - { - "id": "workbench.action.chat.submitWithCodebase", - "title": "workbench.action.chat.submitWithCodebase", - "keywords": [ - "chat submit with codebase", - "chat submit" - ] - }, - { - "id": "workbench.action.chat.toggleAgentMode", - "title": "workbench.action.chat.toggleAgentMode", - "keywords": [ - "chat toggle agent mode", - "chat toggle" - ] - }, - { - "id": "workbench.action.chat.switchToNextModel", - "title": "workbench.action.chat.switchToNextModel", - "keywords": [ - "chat switch to next model", - "chat switch" - ] - }, - { - "id": "workbench.action.chat.switchToNextPinnedModel", - "title": "workbench.action.chat.switchToNextPinnedModel", - "keywords": [ - "chat switch to next pinned model", - "chat switch" - ] - }, - { - "id": "workbench.action.chat.openModelPicker", - "title": "workbench.action.chat.openModelPicker", - "keywords": [ - "chat open model picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openPermissionPicker", - "title": "workbench.action.chat.openPermissionPicker", - "keywords": [ - "chat open permission picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openModePicker", - "title": "workbench.action.chat.openModePicker", - "keywords": [ - "chat open mode picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openSessionTargetPicker", - "title": "workbench.action.chat.openSessionTargetPicker", - "keywords": [ - "chat open session target picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openDelegationPicker", - "title": "workbench.action.chat.openDelegationPicker", - "keywords": [ - "chat open delegation picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openWorkspacePicker", - "title": "workbench.action.chat.openWorkspacePicker", - "keywords": [ - "chat open workspace picker", - "chat open" - ] - }, - { - "id": "workbench.action.chat.chatSessionPrimaryPicker", - "title": "workbench.action.chat.chatSessionPrimaryPicker", - "keywords": [ - "chat chat session primary picker", - "chat chat" - ] - }, - { - "id": "workbench.action.chat.changeModel", - "title": "workbench.action.chat.changeModel", - "keywords": [ - "chat change model", - "chat change" - ] - }, - { - "id": "workbench.edit.chat.cancel", - "title": "workbench.edit.chat.cancel", - "keywords": [ - "workbench edit chat cancel", - "workbench edit" - ] - }, - { - "id": "workbench.action.chat.getHandoffs", - "title": "workbench.action.chat.getHandoffs", - "keywords": [ - "chat get handoffs", - "chat get" - ] - }, - { - "id": "workbench.action.chat.executeHandoff", - "title": "workbench.action.chat.executeHandoff", - "keywords": [ - "chat execute handoff", - "chat execute" - ] - }, - { - "id": "workbench.action.chat.queueMessage", - "title": "workbench.action.chat.queueMessage", - "keywords": [ - "chat queue message", - "chat queue" - ] - }, - { - "id": "workbench.action.chat.steerWithMessage", - "title": "workbench.action.chat.steerWithMessage", - "keywords": [ - "chat steer with message", - "chat steer" - ] - }, - { - "id": "workbench.action.chat.removePendingRequest", - "title": "workbench.action.chat.removePendingRequest", - "keywords": [ - "chat remove pending request", - "chat remove" - ] - }, - { - "id": "workbench.action.chat.editPendingRequest", - "title": "workbench.action.chat.editPendingRequest", - "keywords": [ - "chat edit pending request", - "chat edit" - ] - }, - { - "id": "workbench.action.chat.sendPendingImmediately", - "title": "workbench.action.chat.sendPendingImmediately", - "keywords": [ - "chat send pending immediately", - "chat send" - ] - }, - { - "id": "workbench.action.chat.removeAllPendingRequests", - "title": "workbench.action.chat.removeAllPendingRequests", - "keywords": [ - "chat remove all pending requests", - "chat remove" - ] - }, - { - "id": "workbench.action.quickchat.toggle", - "title": "workbench.action.quickchat.toggle", - "keywords": [ - "quickchat toggle" - ] - }, - { - "id": "workbench.action.openQuickChat", - "title": "workbench.action.openQuickChat", - "keywords": [ - "open quick chat", - "open quick" - ] - }, - { - "id": "workbench.action.quickchat.openInChatView", - "title": "workbench.action.quickchat.openInChatView", - "keywords": [ - "quickchat open in chat view", - "quickchat open" - ] - }, - { - "id": "workbench.action.quickchat.close", - "title": "workbench.action.quickchat.close", - "keywords": [ - "quickchat close" - ] - }, - { - "id": "workbench.action.chat.export", - "title": "workbench.action.chat.export", - "keywords": [ - "chat export" - ] - }, - { - "id": "workbench.action.chat.import", - "title": "workbench.action.chat.import", - "keywords": [ - "chat import" - ] - }, - { - "id": "workbench.action.chat.openInEditor", - "title": "workbench.action.chat.openInEditor", - "keywords": [ - "chat open in editor", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openInNewWindow", - "title": "workbench.action.chat.openInNewWindow", - "keywords": [ - "chat open in new window", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openInSidebar", - "title": "workbench.action.chat.openInSidebar", - "keywords": [ - "chat open in sidebar", - "chat open" - ] - }, - { - "id": "workbench.action.chatEditor.newChat", - "title": "workbench.action.chatEditor.newChat", - "keywords": [ - "chat editor new chat", - "chat editor" - ] - }, - { - "id": "workbench.action.chat.newChat", - "title": "workbench.action.chat.newChat", - "keywords": [ - "chat new chat", - "chat new" - ] - }, - { - "id": "workbench.action.chat.newChat.copilotIcon", - "title": "workbench.action.chat.newChat.copilotIcon", - "keywords": [ - "chat new chat copilot icon", - "chat new" - ] - }, - { - "id": "workbench.action.chat.newChat.newSessionIcon", - "title": "workbench.action.chat.newChat.newSessionIcon", - "keywords": [ - "chat new chat new session icon", - "chat new" - ] - }, - { - "id": "workbench.action.chat.newChat.commentIcon", - "title": "workbench.action.chat.newChat.commentIcon", - "keywords": [ - "chat new chat comment icon", - "chat new" - ] - }, - { - "id": "workbench.action.chat.newEditSession", - "title": "workbench.action.chat.newEditSession", - "keywords": [ - "chat new edit session", - "chat new" - ] - }, - { - "id": "workbench.action.chat.newLocalChat", - "title": "workbench.action.chat.newLocalChat", - "keywords": [ - "chat new local chat", - "chat new" - ] - }, - { - "id": "workbench.action.chat.undoEdit", - "title": "workbench.action.chat.undoEdit", - "keywords": [ - "chat undo edit", - "chat undo" - ] - }, - { - "id": "workbench.action.chat.redoEdit", - "title": "workbench.action.chat.redoEdit", - "keywords": [ - "chat redo edit", - "chat redo" - ] - }, - { - "id": "workbench.action.chat.redoEdit2", - "title": "workbench.action.chat.redoEdit2", - "keywords": [ - "chat redo edit2", - "chat redo" - ] - }, - { - "id": "workbench.action.chat.attachContext", - "title": "workbench.action.chat.attachContext", - "keywords": [ - "chat attach context", - "chat attach" - ] - }, - { - "id": "workbench.action.chat.attachFile", - "title": "workbench.action.chat.attachFile", - "keywords": [ - "chat attach file", - "chat attach" - ] - }, - { - "id": "workbench.action.chat.attachFolder", - "title": "workbench.action.chat.attachFolder", - "keywords": [ - "chat attach folder", - "chat attach" - ] - }, - { - "id": "workbench.action.chat.attachSelection", - "title": "workbench.action.chat.attachSelection", - "keywords": [ - "chat attach selection", - "chat attach" - ] - }, - { - "id": "workbench.action.chat.insertSearchResults", - "title": "workbench.action.chat.insertSearchResults", - "keywords": [ - "chat insert search results", - "chat insert" - ] - }, - { - "id": "workbench.action.chat.attachPinnedEditors", - "title": "workbench.action.chat.attachPinnedEditors", - "keywords": [ - "chat attach pinned editors", - "chat attach" - ] - }, - { - "id": "workbench.action.chat.createPlugin", - "title": "workbench.action.chat.createPlugin", - "keywords": [ - "chat create plugin", - "chat create" - ] - }, - { - "id": "workbench.action.chat.run-in-new-chat.prompt.current", - "title": "workbench.action.chat.run-in-new-chat.prompt.current", - "keywords": [ - "chat run in new chat prompt current", - "chat run" - ] - }, - { - "id": "workbench.action.chat.run.prompt.current", - "title": "workbench.action.chat.run.prompt.current", - "keywords": [ - "chat run prompt current", - "chat run" - ] - }, - { - "id": "workbench.action.chat.run.prompt", - "title": "workbench.action.chat.run.prompt", - "keywords": [ - "chat run prompt", - "chat run" - ] - }, - { - "id": "workbench.action.chat.configure.prompts", - "title": "workbench.action.chat.configure.prompts", - "keywords": [ - "chat configure prompts", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.configure.instructions", - "title": "workbench.action.chat.configure.instructions", - "keywords": [ - "chat configure instructions", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.configure.skills", - "title": "workbench.action.chat.configure.skills", - "keywords": [ - "chat configure skills", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.configure.hooks", - "title": "workbench.action.chat.configure.hooks", - "keywords": [ - "chat configure hooks", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.save-as-prompt", - "title": "workbench.action.chat.save-as-prompt", - "keywords": [ - "chat save as prompt", - "chat save" - ] - }, - { - "id": "workbench.action.chat.save-as-instructions", - "title": "workbench.action.chat.save-as-instructions", - "keywords": [ - "chat save as instructions", - "chat save" - ] - }, - { - "id": "workbench.action.chat.save-as-agent", - "title": "workbench.action.chat.save-as-agent", - "keywords": [ - "chat save as agent", - "chat save" - ] - }, - { - "id": "workbench.action.chat.configure.customagents", - "title": "workbench.action.chat.configure.customagents", - "keywords": [ - "chat configure customagents", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.configure.customagents.disabled", - "title": "workbench.action.chat.configure.customagents.disabled", - "keywords": [ - "chat configure customagents disabled", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.picker.customagents", - "title": "workbench.action.chat.picker.customagents", - "keywords": [ - "chat picker customagents", - "chat picker" - ] - }, - { - "id": "workbench.action.chat.picker.customagents.disabled", - "title": "workbench.action.chat.picker.customagents.disabled", - "keywords": [ - "chat picker customagents disabled", - "chat picker" - ] - }, - { - "id": "workbench.command.new.prompt", - "title": "workbench.command.new.prompt", - "keywords": [ - "workbench command new prompt", - "workbench command" - ] - }, - { - "id": "workbench.command.new.instructions", - "title": "workbench.command.new.instructions", - "keywords": [ - "workbench command new instructions", - "workbench command" - ] - }, - { - "id": "workbench.command.new.agent", - "title": "workbench.command.new.agent", - "keywords": [ - "workbench command new agent", - "workbench command" - ] - }, - { - "id": "workbench.command.new.skill", - "title": "workbench.command.new.skill", - "keywords": [ - "workbench command new skill", - "workbench command" - ] - }, - { - "id": "workbench.command.new.untitled.prompt", - "title": "workbench.command.new.untitled.prompt", - "keywords": [ - "workbench command new untitled prompt", - "workbench command" - ] - }, - { - "id": "workbench.action.chat.logInputHistory", - "title": "workbench.action.chat.logInputHistory", - "keywords": [ - "chat log input history", - "chat log" - ] - }, - { - "id": "workbench.action.chat.logChatIndex", - "title": "workbench.action.chat.logChatIndex", - "keywords": [ - "chat log chat index", - "chat log" - ] - }, - { - "id": "workbench.action.chat.inspectChatModel", - "title": "workbench.action.chat.inspectChatModel", - "keywords": [ - "chat inspect chat model", - "chat inspect" - ] - }, - { - "id": "workbench.action.chat.inspectChatModelReferences", - "title": "workbench.action.chat.inspectChatModelReferences", - "keywords": [ - "chat inspect chat model references", - "chat inspect" - ] - }, - { - "id": "workbench.action.chat.clearRecentlyUsedLanguageModels", - "title": "workbench.action.chat.clearRecentlyUsedLanguageModels", - "keywords": [ - "chat clear recently used language models", - "chat clear" - ] - }, - { - "id": "workbench.action.chat.resetPermissionWarningDialogs", - "title": "workbench.action.chat.resetPermissionWarningDialogs", - "keywords": [ - "chat reset permission warning dialogs", - "chat reset" - ] - }, - { - "id": "workbench.action.chat.openCopilotCliStateFile", - "title": "workbench.action.chat.openCopilotCliStateFile", - "keywords": [ - "chat open copilot cli state file", - "chat open" - ] - }, - { - "id": "chatEditor.action.navigateNext", - "title": "chatEditor.action.navigateNext", - "keywords": [ - "chat editor action navigate next", - "chat editor" - ] - }, - { - "id": "chatEditor.action.navigatePrevious", - "title": "chatEditor.action.navigatePrevious", - "keywords": [ - "chat editor action navigate previous", - "chat editor" - ] - }, - { - "id": "chatEditor.action.reviewChanges", - "title": "chatEditor.action.reviewChanges", - "keywords": [ - "chat editor action review changes", - "chat editor" - ] - }, - { - "id": "chatEditor.action.accept", - "title": "chatEditor.action.accept", - "keywords": [ - "chat editor action accept", - "chat editor" - ] - }, - { - "id": "chatEditor.action.reject", - "title": "chatEditor.action.reject", - "keywords": [ - "chat editor action reject", - "chat editor" - ] - }, - { - "id": "chatEditor.action.acceptAllEdits", - "title": "chatEditor.action.acceptAllEdits", - "keywords": [ - "chat editor action accept all edits", - "chat editor" - ] - }, - { - "id": "chatEditor.action.acceptHunk", - "title": "chatEditor.action.acceptHunk", - "keywords": [ - "chat editor action accept hunk", - "chat editor" - ] - }, - { - "id": "chatEditor.action.undoHunk", - "title": "chatEditor.action.undoHunk", - "keywords": [ - "chat editor action undo hunk", - "chat editor" - ] - }, - { - "id": "chatEditor.action.toggleDiff", - "title": "chatEditor.action.toggleDiff", - "keywords": [ - "chat editor action toggle diff", - "chat editor" - ] - }, - { - "id": "chatEditor.action.showAccessibleDiffView", - "title": "chatEditor.action.showAccessibleDiffView", - "keywords": [ - "chat editor action show accessible diff view", - "chat editor" - ] - }, - { - "id": "chatEditing.multidiff.acceptAllFiles", - "title": "chatEditing.multidiff.acceptAllFiles", - "keywords": [ - "chat editing multidiff accept all files", - "chat editing" - ] - }, - { - "id": "chatEditing.multidiff.discardAllFiles", - "title": "chatEditing.multidiff.discardAllFiles", - "keywords": [ - "chat editing multidiff discard all files", - "chat editing" - ] - }, - { - "id": "chatEditing.multidiff.explain", - "title": "chatEditing.multidiff.explain", - "keywords": [ - "chat editing multidiff explain", - "chat editing" - ] - }, - { - "id": "workbench.action.chat.acceptElicitation", - "title": "workbench.action.chat.acceptElicitation", - "keywords": [ - "chat accept elicitation", - "chat accept" - ] - }, - { - "id": "workbench.action.chat.acceptTool", - "title": "workbench.action.chat.acceptTool", - "keywords": [ - "chat accept tool", - "chat accept" - ] - }, - { - "id": "workbench.action.chat.skipTool", - "title": "workbench.action.chat.skipTool", - "keywords": [ - "chat skip tool", - "chat skip" - ] - }, - { - "id": "workbench.action.chat.configureTools", - "title": "workbench.action.chat.configureTools", - "keywords": [ - "chat configure tools", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.manageLanguageModelAuthentication", - "title": "workbench.action.chat.manageLanguageModelAuthentication", - "keywords": [ - "chat manage language model authentication", - "chat manage" - ] - }, - { - "id": "lm.addLanguageModelsProviderGroup", - "title": "lm.addLanguageModelsProviderGroup", - "keywords": [ - "lm add language models provider group", - "lm add" - ] - }, - { - "id": "lm.migrateLanguageModelsProviderGroup", - "title": "lm.migrateLanguageModelsProviderGroup", - "keywords": [ - "lm migrate language models provider group", - "lm migrate" - ] - }, - { - "id": "workbench.action.chat.managePlugins", - "title": "workbench.action.chat.managePlugins", - "keywords": [ - "chat manage plugins", - "chat manage" - ] - }, - { - "id": "workbench.action.chat.installPluginFromSource", - "title": "workbench.action.chat.installPluginFromSource", - "keywords": [ - "chat install plugin from source", - "chat install" - ] - }, - { - "id": "workbench.action.chat.managePluginMarketplaces", - "title": "workbench.action.chat.managePluginMarketplaces", - "keywords": [ - "chat manage plugin marketplaces", - "chat manage" - ] - }, - { - "id": "planReviewFeedback.action.navigatePrevious", - "title": "planReviewFeedback.action.navigatePrevious", - "keywords": [ - "plan review feedback action navigate previous", - "plan review" - ] - }, - { - "id": "planReviewFeedback.action.navigateNext", - "title": "planReviewFeedback.action.navigateNext", - "keywords": [ - "plan review feedback action navigate next", - "plan review" - ] - }, - { - "id": "planReviewFeedback.action.clearAll", - "title": "planReviewFeedback.action.clearAll", - "keywords": [ - "plan review feedback action clear all", - "plan review" - ] - }, - { - "id": "chat.configureToolSets", - "title": "chat.configureToolSets", - "keywords": [ - "chat configure tool sets", - "chat configure" - ] - }, - { - "id": "workbench.action.chat.forkConversation", - "title": "workbench.action.chat.forkConversation", - "keywords": [ - "chat fork conversation", - "chat fork" - ] - }, - { - "id": "workbench.action.chat.exportAgentHostDebugLogs", - "title": "workbench.action.chat.exportAgentHostDebugLogs", - "keywords": [ - "chat export agent host debug logs", - "chat export" - ] - }, - { - "id": "editor.action.extensioneditor.showfind", - "title": "editor.action.extensioneditor.showfind", - "keywords": [ - "extensioneditor showfind" - ] - }, - { - "id": "editor.action.extensioneditor.findNext", - "title": "editor.action.extensioneditor.findNext", - "keywords": [ - "extensioneditor find next", - "extensioneditor find" - ] - }, - { - "id": "editor.action.extensioneditor.findPrevious", - "title": "editor.action.extensioneditor.findPrevious", - "keywords": [ - "extensioneditor find previous", - "extensioneditor find" - ] - }, - { - "id": "extension.open", - "title": "extension.open", - "keywords": [ - "open" - ] - }, - { - "id": "workbench.extensions.installExtension", - "title": "workbench.extensions.installExtension", - "keywords": [ - "workbench extensions install extension", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.uninstallExtension", - "title": "workbench.extensions.uninstallExtension", - "keywords": [ - "workbench extensions uninstall extension", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.search", - "title": "workbench.extensions.search", - "keywords": [ - "workbench extensions search", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.actions.gallery.signIn", - "title": "workbench.extensions.actions.gallery.signIn", - "keywords": [ - "workbench extensions actions gallery sign in", - "workbench extensions" - ] - }, - { - "id": "interactiveEditor.start", - "title": "interactiveEditor.start", - "keywords": [ - "interactive editor start", - "interactive editor" - ] - }, - { - "id": "inlineChat2.keep", - "title": "inlineChat2.keep", - "keywords": [ - "inline chat2 keep", - "inline chat2" - ] - }, - { - "id": "inlineChat2.close", - "title": "inlineChat2.close", - "keywords": [ - "inline chat2 close", - "inline chat2" - ] - }, - { - "id": "inlineChat2.cancel", - "title": "inlineChat2.cancel", - "keywords": [ - "inline chat2 cancel", - "inline chat2" - ] - }, - { - "id": "inlineChat2.continueInChat", - "title": "inlineChat2.continueInChat", - "keywords": [ - "inline chat2 continue in chat", - "inline chat2" - ] - }, - { - "id": "inlineChat2.rephrase", - "title": "inlineChat2.rephrase", - "keywords": [ - "inline chat2 rephrase", - "inline chat2" - ] - }, - { - "id": "inlineChat.start", - "title": "inlineChat.start", - "keywords": [ - "inline chat start", - "inline chat" - ] - }, - { - "id": "inlineChat.askInChat", - "title": "inlineChat.askInChat", - "keywords": [ - "inline chat ask in chat", - "inline chat" - ] - }, - { - "id": "inlineChat.focus", - "title": "inlineChat.focus", - "keywords": [ - "inline chat focus", - "inline chat" - ] - }, - { - "id": "inlineChat.fixDiagnostics", - "title": "inlineChat.fixDiagnostics", - "keywords": [ - "inline chat fix diagnostics", - "inline chat" - ] - }, - { - "id": "inlineChat.dismissEditorAffordance", - "title": "inlineChat.dismissEditorAffordance", - "keywords": [ - "inline chat dismiss editor affordance", - "inline chat" - ] - }, - { - "id": "workbench.mcp.listServer", - "title": "workbench.mcp.listServer", - "keywords": [ - "workbench mcp list server", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.serverOptions", - "title": "workbench.mcp.serverOptions", - "keywords": [ - "workbench mcp server options", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.serverOptionsInConfirmation", - "title": "workbench.mcp.serverOptionsInConfirmation", - "keywords": [ - "workbench mcp server options in confirmation", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.resetTrust", - "title": "workbench.mcp.resetTrust", - "keywords": [ - "workbench mcp reset trust", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.resetCachedTools", - "title": "workbench.mcp.resetCachedTools", - "keywords": [ - "workbench mcp reset cached tools", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.addConfiguration", - "title": "workbench.mcp.addConfiguration", - "keywords": [ - "workbench mcp add configuration", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.installFromManifest", - "title": "workbench.mcp.installFromManifest", - "keywords": [ - "workbench mcp install from manifest", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.removeStoredInput", - "title": "workbench.mcp.removeStoredInput", - "keywords": [ - "workbench mcp remove stored input", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.editStoredInput", - "title": "workbench.mcp.editStoredInput", - "keywords": [ - "workbench mcp edit stored input", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.setOAuthClientSecret", - "title": "workbench.mcp.setOAuthClientSecret", - "keywords": [ - "workbench mcp set oauth client secret", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.startServer", - "title": "workbench.mcp.startServer", - "keywords": [ - "workbench mcp start server", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.stopServer", - "title": "workbench.mcp.stopServer", - "keywords": [ - "workbench mcp stop server", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.showOutput", - "title": "workbench.mcp.showOutput", - "keywords": [ - "workbench mcp show output", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.restartServer", - "title": "workbench.mcp.restartServer", - "keywords": [ - "workbench mcp restart server", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.showConfiguration", - "title": "workbench.mcp.showConfiguration", - "keywords": [ - "workbench mcp show configuration", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.browseServers", - "title": "workbench.mcp.browseServers", - "keywords": [ - "workbench mcp browse servers", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.openUserMcpJson", - "title": "workbench.mcp.openUserMcpJson", - "keywords": [ - "workbench mcp open user mcp json", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.openRemoteUserMcpJson", - "title": "workbench.mcp.openRemoteUserMcpJson", - "keywords": [ - "workbench mcp open remote user mcp json", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.openWorkspaceMcpJson", - "title": "workbench.mcp.openWorkspaceMcpJson", - "keywords": [ - "workbench mcp open workspace mcp json", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.openWorkspaceFolderMcpJson", - "title": "workbench.mcp.openWorkspaceFolderMcpJson", - "keywords": [ - "workbench mcp open workspace folder mcp json", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.showInstalledServers", - "title": "workbench.mcp.showInstalledServers", - "keywords": [ - "workbench mcp show installed servers", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.browseResources", - "title": "workbench.mcp.browseResources", - "keywords": [ - "workbench mcp browse resources", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.configureSamplingModels", - "title": "workbench.mcp.configureSamplingModels", - "keywords": [ - "workbench mcp configure sampling models", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.startPromptForServer", - "title": "workbench.mcp.startPromptForServer", - "keywords": [ - "workbench mcp start prompt for server", - "workbench mcp" - ] - }, - { - "id": "workbench.mcp.skipAutostart", - "title": "workbench.mcp.skipAutostart", - "keywords": [ - "workbench mcp skip autostart", - "workbench mcp" - ] - }, - { - "id": "workbench.action.chat.openImageInCarousel", - "title": "workbench.action.chat.openImageInCarousel", - "keywords": [ - "chat open image in carousel", - "chat open" - ] - }, - { - "id": "workbench.action.openImagesInCarousel", - "title": "workbench.action.openImagesInCarousel", - "keywords": [ - "open images in carousel", - "open images" - ] - }, - { - "id": "interactive.execute", - "title": "interactive.execute", - "keywords": [ - "interactive execute" - ] - }, - { - "id": "interactive.input.clear", - "title": "interactive.input.clear", - "keywords": [ - "interactive input clear", - "interactive input" - ] - }, - { - "id": "interactive.history.previous", - "title": "interactive.history.previous", - "keywords": [ - "interactive history previous", - "interactive history" - ] - }, - { - "id": "interactive.history.next", - "title": "interactive.history.next", - "keywords": [ - "interactive history next", - "interactive history" - ] - }, - { - "id": "interactive.scrollToTop", - "title": "interactive.scrollToTop", - "keywords": [ - "interactive scroll to top", - "interactive scroll" - ] - }, - { - "id": "interactive.scrollToBottom", - "title": "interactive.scrollToBottom", - "keywords": [ - "interactive scroll to bottom", - "interactive scroll" - ] - }, - { - "id": "interactive.input.focus", - "title": "interactive.input.focus", - "keywords": [ - "interactive input focus", - "interactive input" - ] - }, - { - "id": "interactive.history.focus", - "title": "interactive.history.focus", - "keywords": [ - "interactive history focus", - "interactive history" - ] - }, - { - "id": "repl.action.acceptInput", - "title": "repl.action.acceptInput", - "keywords": [ - "repl action accept input", - "repl action" - ] - }, - { - "id": "repl.action.copyAll", - "title": "repl.action.copyAll", - "keywords": [ - "repl action copy all", - "repl action" - ] - }, - { - "id": "repl.action.filter", - "title": "repl.action.filter", - "keywords": [ - "repl action filter", - "repl action" - ] - }, - { - "id": "repl.action.find", - "title": "repl.action.find", - "keywords": [ - "repl action find", - "repl action" - ] - }, - { - "id": "workbench.action.debug.selectRepl", - "title": "workbench.action.debug.selectRepl", - "keywords": [ - "debug select repl", - "debug select" - ] - }, - { - "id": "workbench.debug.panel.action.clearReplAction", - "title": "workbench.debug.panel.action.clearReplAction", - "keywords": [ - "workbench debug panel action clear repl action", - "workbench debug" - ] - }, - { - "id": "debug.collapseRepl", - "title": "debug.collapseRepl", - "keywords": [ - "debug collapse repl", - "debug collapse" - ] - }, - { - "id": "debug.replPaste", - "title": "debug.replPaste", - "keywords": [ - "debug repl paste", - "debug repl" - ] - }, - { - "id": "workbench.debug.action.copyAll", - "title": "workbench.debug.action.copyAll", - "keywords": [ - "workbench debug action copy all", - "workbench debug" - ] - }, - { - "id": "debug.replCopy", - "title": "debug.replCopy", - "keywords": [ - "debug repl copy", - "debug repl" - ] - }, - { - "id": "workbench.debug.action.focusRepl", - "title": "workbench.debug.action.focusRepl", - "keywords": [ - "workbench debug action focus repl", - "workbench debug" - ] - }, - { - "id": "repl.focusLastItemExecuted", - "title": "repl.focusLastItemExecuted", - "keywords": [ - "repl focus last item executed", - "repl focus" - ] - }, - { - "id": "repl.input.focus", - "title": "repl.input.focus", - "keywords": [ - "repl input focus", - "repl input" - ] - }, - { - "id": "repl.execute", - "title": "repl.execute", - "keywords": [ - "repl execute" - ] - }, - { - "id": "list.find.replInputFocus", - "title": "list.find.replInputFocus", - "keywords": [ - "list find repl input focus", - "list find" - ] - }, - { - "id": "testing.toggleInlineCoverage", - "title": "testing.toggleInlineCoverage", - "keywords": [ - "testing toggle inline coverage", - "testing toggle" - ] - }, - { - "id": "testing.coverageToggleToolbar", - "title": "testing.coverageToggleToolbar", - "keywords": [ - "testing coverage toggle toolbar", - "testing coverage" - ] - }, - { - "id": "testing.coverageFilterToTestInEditor", - "title": "testing.coverageFilterToTestInEditor", - "keywords": [ - "testing coverage filter to test in editor", - "testing coverage" - ] - }, - { - "id": "testing.toggleCoverageInExplorer", - "title": "testing.toggleCoverageInExplorer", - "keywords": [ - "testing toggle coverage in explorer", - "testing toggle" - ] - }, - { - "id": "testing.coverage.goToNextMissedLine", - "title": "testing.coverage.goToNextMissedLine", - "keywords": [ - "testing coverage go to next missed line", - "testing coverage" - ] - }, - { - "id": "testing.coverage.goToPreviousMissedLine", - "title": "testing.coverage.goToPreviousMissedLine", - "keywords": [ - "testing coverage go to previous missed line", - "testing coverage" - ] - }, - { - "id": "testing.coverageFilterToTest", - "title": "testing.coverageFilterToTest", - "keywords": [ - "testing coverage filter to test", - "testing coverage" - ] - }, - { - "id": "testing.coverageViewChangeSorting", - "title": "testing.coverageViewChangeSorting", - "keywords": [ - "testing coverage view change sorting", - "testing coverage" - ] - }, - { - "id": "testing.coverageViewCollapseAll", - "title": "testing.coverageViewCollapseAll", - "keywords": [ - "testing coverage view collapse all", - "testing coverage" - ] - }, - { - "id": "vscode.pickMultipleTestProfiles", - "title": "vscode.pickMultipleTestProfiles", - "keywords": [ - "vscode pick multiple test profiles", - "vscode pick" - ] - }, - { - "id": "vscode.pickTestProfile", - "title": "vscode.pickTestProfile", - "keywords": [ - "vscode pick test profile", - "vscode pick" - ] - }, - { - "id": "callStackWidget.goToFile", - "title": "callStackWidget.goToFile", - "keywords": [ - "call stack widget go to file", - "call stack" - ] - }, - { - "id": "testing.callStack.run", - "title": "testing.callStack.run", - "keywords": [ - "testing call stack run", - "testing call" - ] - }, - { - "id": "testing.callStack.debug", - "title": "testing.callStack.debug", - "keywords": [ - "testing call stack debug", - "testing call" - ] - }, - { - "id": "testing.cancelTestRefresh", - "title": "testing.cancelTestRefresh", - "keywords": [ - "testing cancel test refresh", - "testing cancel" - ] - }, - { - "id": "testing.cancelRun", - "title": "testing.cancelRun", - "keywords": [ - "testing cancel run", - "testing cancel" - ] - }, - { - "id": "testing.coverage.close", - "title": "testing.coverage.close", - "keywords": [ - "testing coverage close", - "testing coverage" - ] - }, - { - "id": "testing.clearTestResults", - "title": "testing.clearTestResults", - "keywords": [ - "testing clear test results", - "testing clear" - ] - }, - { - "id": "testing.collapseAll", - "title": "testing.collapseAll", - "keywords": [ - "testing collapse all", - "testing collapse" - ] - }, - { - "id": "testing.configureProfile", - "title": "testing.configureProfile", - "keywords": [ - "testing configure profile", - "testing configure" - ] - }, - { - "id": "testing.toggleContinuousRunForTest", - "title": "testing.toggleContinuousRunForTest", - "keywords": [ - "testing toggle continuous run for test", - "testing toggle" - ] - }, - { - "id": "testing.continuousRunUsingForTest", - "title": "testing.continuousRunUsingForTest", - "keywords": [ - "testing continuous run using for test", - "testing continuous" - ] - }, - { - "id": "testing.coverage", - "title": "testing.coverage", - "keywords": [ - "testing coverage" - ] - }, - { - "id": "testing.coverageAll", - "title": "testing.coverageAll", - "keywords": [ - "testing coverage all", - "testing coverage" - ] - }, - { - "id": "testing.coverageAtCursor", - "title": "testing.coverageAtCursor", - "keywords": [ - "testing coverage at cursor", - "testing coverage" - ] - }, - { - "id": "testing.coverageCurrentFile", - "title": "testing.coverageCurrentFile", - "keywords": [ - "testing coverage current file", - "testing coverage" - ] - }, - { - "id": "testing.coverageLastRun", - "title": "testing.coverageLastRun", - "keywords": [ - "testing coverage last run", - "testing coverage" - ] - }, - { - "id": "testing.coverageSelected", - "title": "testing.coverageSelected", - "keywords": [ - "testing coverage selected", - "testing coverage" - ] - }, - { - "id": "testing.coverage.uri", - "title": "testing.coverage.uri", - "keywords": [ - "testing coverage uri", - "testing coverage" - ] - }, - { - "id": "testing.debug", - "title": "testing.debug", - "keywords": [ - "testing debug" - ] - }, - { - "id": "testing.debugAll", - "title": "testing.debugAll", - "keywords": [ - "testing debug all", - "testing debug" - ] - }, - { - "id": "testing.debugAtCursor", - "title": "testing.debugAtCursor", - "keywords": [ - "testing debug at cursor", - "testing debug" - ] - }, - { - "id": "testing.debugCurrentFile", - "title": "testing.debugCurrentFile", - "keywords": [ - "testing debug current file", - "testing debug" - ] - }, - { - "id": "testing.debugFailTests", - "title": "testing.debugFailTests", - "keywords": [ - "testing debug fail tests", - "testing debug" - ] - }, - { - "id": "testing.debugLastRun", - "title": "testing.debugLastRun", - "keywords": [ - "testing debug last run", - "testing debug" - ] - }, - { - "id": "testing.debugSelected", - "title": "testing.debugSelected", - "keywords": [ - "testing debug selected", - "testing debug" - ] - }, - { - "id": "testing.debug.uri", - "title": "testing.debug.uri", - "keywords": [ - "testing debug uri", - "testing debug" - ] - }, - { - "id": "testing.getSelectedProfiles", - "title": "testing.getSelectedProfiles", - "keywords": [ - "testing get selected profiles", - "testing get" - ] - }, - { - "id": "testing.goToRelatedCode", - "title": "testing.goToRelatedCode", - "keywords": [ - "testing go to related code", - "testing go" - ] - }, - { - "id": "testing.goToRelatedTest", - "title": "testing.goToRelatedTest", - "keywords": [ - "testing go to related test", - "testing go" - ] - }, - { - "id": "testing.editFocusedTest", - "title": "testing.editFocusedTest", - "keywords": [ - "testing edit focused test", - "testing edit" - ] - }, - { - "id": "testing.hideTest", - "title": "testing.hideTest", - "keywords": [ - "testing hide test", - "testing hide" - ] - }, - { - "id": "testing.openCoverage", - "title": "testing.openCoverage", - "keywords": [ - "testing open coverage", - "testing open" - ] - }, - { - "id": "testing.openOutputPeek", - "title": "testing.openOutputPeek", - "keywords": [ - "testing open output peek", - "testing open" - ] - }, - { - "id": "testing.peekRelatedCode", - "title": "testing.peekRelatedCode", - "keywords": [ - "testing peek related code", - "testing peek" - ] - }, - { - "id": "testing.peekRelatedTest", - "title": "testing.peekRelatedTest", - "keywords": [ - "testing peek related test", - "testing peek" - ] - }, - { - "id": "testing.refreshTests", - "title": "testing.refreshTests", - "keywords": [ - "testing refresh tests", - "testing refresh" - ] - }, - { - "id": "testing.reRunFailTests", - "title": "testing.reRunFailTests", - "keywords": [ - "testing re run fail tests", - "testing re" - ] - }, - { - "id": "testing.reRunLastRun", - "title": "testing.reRunLastRun", - "keywords": [ - "testing re run last run", - "testing re" - ] - }, - { - "id": "testing.run", - "title": "testing.run", - "keywords": [ - "testing run" - ] - }, - { - "id": "testing.runAll", - "title": "testing.runAll", - "keywords": [ - "testing run all", - "testing run" - ] - }, - { - "id": "testing.runAtCursor", - "title": "testing.runAtCursor", - "keywords": [ - "testing run at cursor", - "testing run" - ] - }, - { - "id": "testing.runCurrentFile", - "title": "testing.runCurrentFile", - "keywords": [ - "testing run current file", - "testing run" - ] - }, - { - "id": "testing.runSelected", - "title": "testing.runSelected", - "keywords": [ - "testing run selected", - "testing run" - ] - }, - { - "id": "testing.run.uri", - "title": "testing.run.uri", - "keywords": [ - "testing run uri", - "testing run" - ] - }, - { - "id": "testing.runUsing", - "title": "testing.runUsing", - "keywords": [ - "testing run using", - "testing run" - ] - }, - { - "id": "testing.searchForTestExtension", - "title": "testing.searchForTestExtension", - "keywords": [ - "testing search for test extension", - "testing search" - ] - }, - { - "id": "testing.selectDefaultTestProfiles", - "title": "testing.selectDefaultTestProfiles", - "keywords": [ - "testing select default test profiles", - "testing select" - ] - }, - { - "id": "testing.showMostRecentOutput", - "title": "testing.showMostRecentOutput", - "keywords": [ - "testing show most recent output", - "testing show" - ] - }, - { - "id": "testing.startContinuousRun", - "title": "testing.startContinuousRun", - "keywords": [ - "testing start continuous run", - "testing start" - ] - }, - { - "id": "testing.stopContinuousRun", - "title": "testing.stopContinuousRun", - "keywords": [ - "testing stop continuous run", - "testing stop" - ] - }, - { - "id": "testing.sortByDuration", - "title": "testing.sortByDuration", - "keywords": [ - "testing sort by duration", - "testing sort" - ] - }, - { - "id": "testing.sortByLocation", - "title": "testing.sortByLocation", - "keywords": [ - "testing sort by location", - "testing sort" - ] - }, - { - "id": "testing.sortByStatus", - "title": "testing.sortByStatus", - "keywords": [ - "testing sort by status", - "testing sort" - ] - }, - { - "id": "testing.viewAsList", - "title": "testing.viewAsList", - "keywords": [ - "testing view as list", - "testing view" - ] - }, - { - "id": "testing.viewAsTree", - "title": "testing.viewAsTree", - "keywords": [ - "testing view as tree", - "testing view" - ] - }, - { - "id": "testing.toggleInlineTestOutput", - "title": "testing.toggleInlineTestOutput", - "keywords": [ - "testing toggle inline test output", - "testing toggle" - ] - }, - { - "id": "testing.toggleResultsViewLayout", - "title": "testing.toggleResultsViewLayout", - "keywords": [ - "testing toggle results view layout", - "testing toggle" - ] - }, - { - "id": "testing.unhideAllTests", - "title": "testing.unhideAllTests", - "keywords": [ - "testing unhide all tests", - "testing unhide" - ] - }, - { - "id": "testing.unhideTest", - "title": "testing.unhideTest", - "keywords": [ - "testing unhide test", - "testing unhide" - ] - }, - { - "id": "testing.reRunFailedFromLastRun", - "title": "testing.reRunFailedFromLastRun", - "keywords": [ - "testing re run failed from last run", - "testing re" - ] - }, - { - "id": "testing.debugFailedFromLastRun", - "title": "testing.debugFailedFromLastRun", - "keywords": [ - "testing debug failed from last run", - "testing debug" - ] - }, - { - "id": "testing.openMessageInEditor", - "title": "testing.openMessageInEditor", - "keywords": [ - "testing open message in editor", - "testing open" - ] - }, - { - "id": "testing.goToPreviousMessage", - "title": "testing.goToPreviousMessage", - "keywords": [ - "testing go to previous message", - "testing go" - ] - }, - { - "id": "testing.goToNextMessage", - "title": "testing.goToNextMessage", - "keywords": [ - "testing go to next message", - "testing go" - ] - }, - { - "id": "editor.closeTestPeek", - "title": "editor.closeTestPeek", - "keywords": [ - "editor close test peek", - "editor close" - ] - }, - { - "id": "testing.toggleTestingPeekHistory", - "title": "testing.toggleTestingPeekHistory", - "keywords": [ - "testing toggle testing peek history", - "testing toggle" - ] - }, - { - "id": "testing.collapsePeekStack", - "title": "testing.collapsePeekStack", - "keywords": [ - "testing collapse peek stack", - "testing collapse" - ] - }, - { - "id": "testing.startContinuousRunFromExtension", - "title": "testing.startContinuousRunFromExtension", - "keywords": [ - "testing start continuous run from extension", - "testing start" - ] - }, - { - "id": "testing.stopContinuousRunFromExtension", - "title": "testing.stopContinuousRunFromExtension", - "keywords": [ - "testing stop continuous run from extension", - "testing stop" - ] - }, - { - "id": "vscode.peekTestError", - "title": "vscode.peekTestError", - "keywords": [ - "vscode peek test error", - "vscode peek" - ] - }, - { - "id": "vscode.revealTest", - "title": "vscode.revealTest", - "keywords": [ - "vscode reveal test", - "vscode reveal" - ] - }, - { - "id": "vscode.runTestsById", - "title": "vscode.runTestsById", - "keywords": [ - "vscode run tests by id", - "vscode run" - ] - }, - { - "id": "vscode.testing.getControllersWithTests", - "title": "vscode.testing.getControllersWithTests", - "keywords": [ - "vscode testing get controllers with tests", - "vscode testing" - ] - }, - { - "id": "vscode.testing.getTestsInFile", - "title": "vscode.testing.getTestsInFile", - "keywords": [ - "vscode testing get tests in file", - "vscode testing" - ] - }, - { - "id": "workbench.action.setLogLevel", - "title": "workbench.action.setLogLevel", - "keywords": [ - "set log level", - "set log" - ] - }, - { - "id": "workbench.action.setDefaultLogLevel", - "title": "workbench.action.setDefaultLogLevel", - "keywords": [ - "set default log level", - "set default" - ] - }, - { - "id": "workbench.action.clearCommandHistory", - "title": "workbench.action.clearCommandHistory", - "keywords": [ - "clear command history", - "clear command" - ] - }, - { - "id": "workbench.action.showCommands", - "title": "workbench.action.showCommands", - "keywords": [ - "show commands" - ] - }, - { - "id": "workbench.action.openView", - "title": "workbench.action.openView", - "keywords": [ - "open view" - ] - }, - { - "id": "workbench.action.quickOpenView", - "title": "workbench.action.quickOpenView", - "keywords": [ - "quick open view", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigateNextInViewPicker", - "title": "workbench.action.quickOpenNavigateNextInViewPicker", - "keywords": [ - "quick open navigate next in view picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigatePreviousInViewPicker", - "title": "workbench.action.quickOpenNavigatePreviousInViewPicker", - "keywords": [ - "quick open navigate previous in view picker", - "quick open" - ] - }, - { - "id": "refactorPreview.apply", - "title": "refactorPreview.apply", - "keywords": [ - "refactor preview apply", - "refactor preview" - ] - }, - { - "id": "refactorPreview.discard", - "title": "refactorPreview.discard", - "keywords": [ - "refactor preview discard", - "refactor preview" - ] - }, - { - "id": "refactorPreview.toggleCheckedState", - "title": "refactorPreview.toggleCheckedState", - "keywords": [ - "refactor preview toggle checked state", - "refactor preview" - ] - }, - { - "id": "refactorPreview.groupByFile", - "title": "refactorPreview.groupByFile", - "keywords": [ - "refactor preview group by file", - "refactor preview" - ] - }, - { - "id": "refactorPreview.groupByType", - "title": "refactorPreview.groupByType", - "keywords": [ - "refactor preview group by type", - "refactor preview" - ] - }, - { - "id": "refactorPreview.toggleGrouping", - "title": "refactorPreview.toggleGrouping", - "keywords": [ - "refactor preview toggle grouping", - "refactor preview" - ] - }, - { - "id": "search.action.copyMatch", - "title": "search.action.copyMatch", - "keywords": [ - "search action copy match", - "search action" - ] - }, - { - "id": "search.action.copyPath", - "title": "search.action.copyPath", - "keywords": [ - "search action copy path", - "search action" - ] - }, - { - "id": "search.action.copyAll", - "title": "search.action.copyAll", - "keywords": [ - "search action copy all", - "search action" - ] - }, - { - "id": "search.action.getSearchResults", - "title": "search.action.getSearchResults", - "keywords": [ - "search action get search results", - "search action" - ] - }, - { - "id": "search.action.restrictSearchToFolder", - "title": "search.action.restrictSearchToFolder", - "keywords": [ - "search action restrict search to folder", - "search action" - ] - }, - { - "id": "search.action.expandRecursively", - "title": "search.action.expandRecursively", - "keywords": [ - "search action expand recursively", - "search action" - ] - }, - { - "id": "search.action.excludeFromSearch", - "title": "search.action.excludeFromSearch", - "keywords": [ - "search action exclude from search", - "search action" - ] - }, - { - "id": "search.action.excludeFileTypeFromSearch", - "title": "search.action.excludeFileTypeFromSearch", - "keywords": [ - "search action exclude file type from search", - "search action" - ] - }, - { - "id": "search.action.includeFileTypeInSearch", - "title": "search.action.includeFileTypeInSearch", - "keywords": [ - "search action include file type in search", - "search action" - ] - }, - { - "id": "search.action.revealInSideBar", - "title": "search.action.revealInSideBar", - "keywords": [ - "search action reveal in side bar", - "search action" - ] - }, - { - "id": "workbench.action.findInFiles", - "title": "workbench.action.findInFiles", - "keywords": [ - "find in files", - "find in" - ] - }, - { - "id": "filesExplorer.findInFolder", - "title": "filesExplorer.findInFolder", - "keywords": [ - "files explorer find in folder", - "files explorer" - ] - }, - { - "id": "filesExplorer.findInWorkspace", - "title": "filesExplorer.findInWorkspace", - "keywords": [ - "files explorer find in workspace", - "files explorer" - ] - }, - { - "id": "workbench.action.search.toggleQueryDetails", - "title": "workbench.action.search.toggleQueryDetails", - "keywords": [ - "search toggle query details", - "search toggle" - ] - }, - { - "id": "closeReplaceInFilesWidget", - "title": "closeReplaceInFilesWidget", - "keywords": [ - "close replace in files widget", - "close replace" - ] - }, - { - "id": "toggleSearchCaseSensitive", - "title": "toggleSearchCaseSensitive", - "keywords": [ - "toggle search case sensitive", - "toggle search" - ] - }, - { - "id": "toggleSearchWholeWord", - "title": "toggleSearchWholeWord", - "keywords": [ - "toggle search whole word", - "toggle search" - ] - }, - { - "id": "toggleSearchRegex", - "title": "toggleSearchRegex", - "keywords": [ - "toggle search regex", - "toggle search" - ] - }, - { - "id": "toggleSearchPreserveCase", - "title": "toggleSearchPreserveCase", - "keywords": [ - "toggle search preserve case", - "toggle search" - ] - }, - { - "id": "search.action.openResult", - "title": "search.action.openResult", - "keywords": [ - "search action open result", - "search action" - ] - }, - { - "id": "search.action.openResultToSide", - "title": "search.action.openResultToSide", - "keywords": [ - "search action open result to side", - "search action" - ] - }, - { - "id": "addCursorsAtSearchResults", - "title": "addCursorsAtSearchResults", - "keywords": [ - "add cursors at search results", - "add cursors" - ] - }, - { - "id": "search.focus.nextInputBox", - "title": "search.focus.nextInputBox", - "keywords": [ - "search focus next input box", - "search focus" - ] - }, - { - "id": "search.focus.previousInputBox", - "title": "search.focus.previousInputBox", - "keywords": [ - "search focus previous input box", - "search focus" - ] - }, - { - "id": "search.action.focusSearchFromResults", - "title": "search.action.focusSearchFromResults", - "keywords": [ - "search action focus search from results", - "search action" - ] - }, - { - "id": "workbench.action.toggleSearchOnType", - "title": "workbench.action.toggleSearchOnType", - "keywords": [ - "toggle search on type", - "toggle search" - ] - }, - { - "id": "search.action.focusSearchList", - "title": "search.action.focusSearchList", - "keywords": [ - "search action focus search list", - "search action" - ] - }, - { - "id": "search.action.focusNextSearchResult", - "title": "search.action.focusNextSearchResult", - "keywords": [ - "search action focus next search result", - "search action" - ] - }, - { - "id": "search.action.focusPreviousSearchResult", - "title": "search.action.focusPreviousSearchResult", - "keywords": [ - "search action focus previous search result", - "search action" - ] - }, - { - "id": "workbench.action.replaceInFiles", - "title": "workbench.action.replaceInFiles", - "keywords": [ - "replace in files", - "replace in" - ] - }, - { - "id": "search.action.remove", - "title": "search.action.remove", - "keywords": [ - "search action remove", - "search action" - ] - }, - { - "id": "search.action.replace", - "title": "search.action.replace", - "keywords": [ - "search action replace", - "search action" - ] - }, - { - "id": "search.action.replaceAllInFile", - "title": "search.action.replaceAllInFile", - "keywords": [ - "search action replace all in file", - "search action" - ] - }, - { - "id": "search.action.replaceAllInFolder", - "title": "search.action.replaceAllInFolder", - "keywords": [ - "search action replace all in folder", - "search action" - ] - }, - { - "id": "workbench.action.quickTextSearch", - "title": "workbench.action.quickTextSearch", - "keywords": [ - "quick text search", - "quick text" - ] - }, - { - "id": "workbench.action.showAllSymbols", - "title": "workbench.action.showAllSymbols", - "keywords": [ - "show all symbols", - "show all" - ] - }, - { - "id": "search.action.replaceAll", - "title": "search.action.replaceAll", - "keywords": [ - "search action replace all", - "search action" - ] - }, - { - "id": "cleanSearchEditorState", - "title": "cleanSearchEditorState", - "keywords": [ - "clean search editor state", - "clean search" - ] - }, - { - "id": "search.searchEditor.action.deleteFileResults", - "title": "search.searchEditor.action.deleteFileResults", - "keywords": [ - "search search editor action delete file results", - "search search" - ] - }, - { - "id": "search.action.openNewEditor", - "title": "search.action.openNewEditor", - "keywords": [ - "search action open new editor", - "search action" - ] - }, - { - "id": "search.action.openEditor", - "title": "search.action.openEditor", - "keywords": [ - "search action open editor", - "search action" - ] - }, - { - "id": "search.action.openNewEditorToSide", - "title": "search.action.openNewEditorToSide", - "keywords": [ - "search action open new editor to side", - "search action" - ] - }, - { - "id": "search.action.openInEditor", - "title": "search.action.openInEditor", - "keywords": [ - "search action open in editor", - "search action" - ] - }, - { - "id": "rerunSearchEditorSearch", - "title": "rerunSearchEditorSearch", - "keywords": [ - "rerun search editor search", - "rerun search" - ] - }, - { - "id": "search.action.focusQueryEditorWidget", - "title": "search.action.focusQueryEditorWidget", - "keywords": [ - "search action focus query editor widget", - "search action" - ] - }, - { - "id": "search.action.focusFilesToInclude", - "title": "search.action.focusFilesToInclude", - "keywords": [ - "search action focus files to include", - "search action" - ] - }, - { - "id": "search.action.focusFilesToExclude", - "title": "search.action.focusFilesToExclude", - "keywords": [ - "search action focus files to exclude", - "search action" - ] - }, - { - "id": "toggleSearchEditorCaseSensitive", - "title": "toggleSearchEditorCaseSensitive", - "keywords": [ - "toggle search editor case sensitive", - "toggle search" - ] - }, - { - "id": "toggleSearchEditorWholeWord", - "title": "toggleSearchEditorWholeWord", - "keywords": [ - "toggle search editor whole word", - "toggle search" - ] - }, - { - "id": "toggleSearchEditorRegex", - "title": "toggleSearchEditorRegex", - "keywords": [ - "toggle search editor regex", - "toggle search" - ] - }, - { - "id": "toggleSearchEditorContextLines", - "title": "toggleSearchEditorContextLines", - "keywords": [ - "toggle search editor context lines", - "toggle search" - ] - }, - { - "id": "increaseSearchEditorContextLines", - "title": "increaseSearchEditorContextLines", - "keywords": [ - "increase search editor context lines", - "increase search" - ] - }, - { - "id": "decreaseSearchEditorContextLines", - "title": "decreaseSearchEditorContextLines", - "keywords": [ - "decrease search editor context lines", - "decrease search" - ] - }, - { - "id": "selectAllSearchEditorMatches", - "title": "selectAllSearchEditorMatches", - "keywords": [ - "select all search editor matches", - "select all" - ] - }, - { - "id": "search.action.openNewEditorFromView", - "title": "search.action.openNewEditorFromView", - "keywords": [ - "search action open new editor from view", - "search action" - ] - }, - { - "id": "scm.input.triggerSetup", - "title": "scm.input.triggerSetup", - "keywords": [ - "scm input trigger setup", - "scm input" - ] - }, - { - "id": "workbench.scm.action.setListViewMode", - "title": "workbench.scm.action.setListViewMode", - "keywords": [ - "workbench scm action set list view mode", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.setTreeViewMode", - "title": "workbench.scm.action.setTreeViewMode", - "keywords": [ - "workbench scm action set tree view mode", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.setListViewModeNavigation", - "title": "workbench.scm.action.setListViewModeNavigation", - "keywords": [ - "workbench scm action set list view mode navigation", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.setTreeViewModeNavigation", - "title": "workbench.scm.action.setTreeViewModeNavigation", - "keywords": [ - "workbench scm action set tree view mode navigation", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.repositories.setSortKey.discoveryTime", - "title": "workbench.scm.action.repositories.setSortKey.discoveryTime", - "keywords": [ - "workbench scm action repositories set sort key discovery time", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.repositories.setSortKey.name", - "title": "workbench.scm.action.repositories.setSortKey.name", - "keywords": [ - "workbench scm action repositories set sort key name", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.repositories.setSortKey.path", - "title": "workbench.scm.action.repositories.setSortKey.path", - "keywords": [ - "workbench scm action repositories set sort key path", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.repositories.setSelectionMode.single", - "title": "workbench.scm.action.repositories.setSelectionMode.single", - "keywords": [ - "workbench scm action repositories set selection mode single", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.repositories.setSelectionMode.multiple", - "title": "workbench.scm.action.repositories.setSelectionMode.multiple", - "keywords": [ - "workbench scm action repositories set selection mode multiple", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.setSortKey.name", - "title": "workbench.scm.action.setSortKey.name", - "keywords": [ - "workbench scm action set sort key name", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.setSortKey.path", - "title": "workbench.scm.action.setSortKey.path", - "keywords": [ - "workbench scm action set sort key path", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.setSortKey.status", - "title": "workbench.scm.action.setSortKey.status", - "keywords": [ - "workbench scm action set sort key status", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.collapseAllRepositories", - "title": "workbench.scm.action.collapseAllRepositories", - "keywords": [ - "workbench scm action collapse all repositories", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.expandAllRepositories", - "title": "workbench.scm.action.expandAllRepositories", - "keywords": [ - "workbench scm action expand all repositories", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.collapseAll", - "title": "workbench.scm.action.collapseAll", - "keywords": [ - "workbench scm action collapse all", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.pickRepository", - "title": "workbench.scm.action.graph.pickRepository", - "keywords": [ - "workbench scm action graph pick repository", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.pickHistoryItemRefs", - "title": "workbench.scm.action.graph.pickHistoryItemRefs", - "keywords": [ - "workbench scm action graph pick history item refs", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.revealCurrentHistoryItem", - "title": "workbench.scm.action.graph.revealCurrentHistoryItem", - "keywords": [ - "workbench scm action graph reveal current history item", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.refresh", - "title": "workbench.scm.action.graph.refresh", - "keywords": [ - "workbench scm action graph refresh", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.setListViewMode", - "title": "workbench.scm.action.graph.setListViewMode", - "keywords": [ - "workbench scm action graph set list view mode", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.setTreeViewMode", - "title": "workbench.scm.action.graph.setTreeViewMode", - "keywords": [ - "workbench scm action graph set tree view mode", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.viewChanges", - "title": "workbench.scm.action.graph.viewChanges", - "keywords": [ - "workbench scm action graph view changes", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.graph.openFile", - "title": "workbench.scm.action.graph.openFile", - "keywords": [ - "workbench scm action graph open file", - "workbench scm" - ] - }, - { - "id": "scm.acceptInput", - "title": "scm.acceptInput", - "keywords": [ - "scm accept input", - "scm accept" - ] - }, - { - "id": "scm.clearValidation", - "title": "scm.clearValidation", - "keywords": [ - "scm clear validation", - "scm clear" - ] - }, - { - "id": "scm.clearInput", - "title": "scm.clearInput", - "keywords": [ - "scm clear input", - "scm clear" - ] - }, - { - "id": "scm.viewNextCommit", - "title": "scm.viewNextCommit", - "keywords": [ - "scm view next commit", - "scm view" - ] - }, - { - "id": "scm.viewPreviousCommit", - "title": "scm.viewPreviousCommit", - "keywords": [ - "scm view previous commit", - "scm view" - ] - }, - { - "id": "scm.forceViewNextCommit", - "title": "scm.forceViewNextCommit", - "keywords": [ - "scm force view next commit", - "scm force" - ] - }, - { - "id": "scm.forceViewPreviousCommit", - "title": "scm.forceViewPreviousCommit", - "keywords": [ - "scm force view previous commit", - "scm force" - ] - }, - { - "id": "scm.openInIntegratedTerminal", - "title": "scm.openInIntegratedTerminal", - "keywords": [ - "scm open in integrated terminal", - "scm open" - ] - }, - { - "id": "scm.openInTerminal", - "title": "scm.openInTerminal", - "keywords": [ - "scm open in terminal", - "scm open" - ] - }, - { - "id": "scm.setActiveProvider", - "title": "scm.setActiveProvider", - "keywords": [ - "scm set active provider", - "scm set" - ] - }, - { - "id": "workbench.scm.action.focusPreviousInput", - "title": "workbench.scm.action.focusPreviousInput", - "keywords": [ - "workbench scm action focus previous input", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.focusNextInput", - "title": "workbench.scm.action.focusNextInput", - "keywords": [ - "workbench scm action focus next input", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.focusPreviousResourceGroup", - "title": "workbench.scm.action.focusPreviousResourceGroup", - "keywords": [ - "workbench scm action focus previous resource group", - "workbench scm" - ] - }, - { - "id": "workbench.scm.action.focusNextResourceGroup", - "title": "workbench.scm.action.focusNextResourceGroup", - "keywords": [ - "workbench scm action focus next resource group", - "workbench scm" - ] - }, - { - "id": "scm.editor.triggerSetup", - "title": "scm.editor.triggerSetup", - "keywords": [ - "scm editor trigger setup", - "scm editor" - ] - }, - { - "id": "editor.action.dirtydiff.previous", - "title": "editor.action.dirtydiff.previous", - "keywords": [ - "dirtydiff previous" - ] - }, - { - "id": "editor.action.dirtydiff.next", - "title": "editor.action.dirtydiff.next", - "keywords": [ - "dirtydiff next" - ] - }, - { - "id": "workbench.action.editor.previousChange", - "title": "workbench.action.editor.previousChange", - "keywords": [ - "editor previous change", - "editor previous" - ] - }, - { - "id": "workbench.action.editor.nextChange", - "title": "workbench.action.editor.nextChange", - "keywords": [ - "editor next change", - "editor next" - ] - }, - { - "id": "closeQuickDiff", - "title": "closeQuickDiff", - "keywords": [ - "close quick diff", - "close quick" - ] - }, - { - "id": "toggleQuickDiffWidgetFocus", - "title": "toggleQuickDiffWidgetFocus", - "keywords": [ - "toggle quick diff widget focus", - "toggle quick" - ] - }, - { - "id": "workbench.debug.viewlet.action.copyWorkspaceVariableValue", - "title": "workbench.debug.viewlet.action.copyWorkspaceVariableValue", - "keywords": [ - "workbench debug viewlet action copy workspace variable value", - "workbench debug" - ] - }, - { - "id": "callStack.collapse", - "title": "callStack.collapse", - "keywords": [ - "call stack collapse", - "call stack" - ] - }, - { - "id": "editor.debug.action.copyAddress", - "title": "editor.debug.action.copyAddress", - "keywords": [ - "editor debug action copy address", - "editor debug" - ] - }, - { - "id": "debug.action.openDisassemblyView", - "title": "debug.action.openDisassemblyView", - "keywords": [ - "debug action open disassembly view", - "debug action" - ] - }, - { - "id": "debug.action.toggleDisassemblyViewSourceCode", - "title": "debug.action.toggleDisassemblyViewSourceCode", - "keywords": [ - "debug action toggle disassembly view source code", - "debug action" - ] - }, - { - "id": "editor.debug.action.toggleBreakpoint", - "title": "editor.debug.action.toggleBreakpoint", - "keywords": [ - "editor debug action toggle breakpoint", - "editor debug" - ] - }, - { - "id": "editor.debug.action.conditionalBreakpoint", - "title": "editor.debug.action.conditionalBreakpoint", - "keywords": [ - "editor debug action conditional breakpoint", - "editor debug" - ] - }, - { - "id": "editor.debug.action.addLogPoint", - "title": "editor.debug.action.addLogPoint", - "keywords": [ - "editor debug action add log point", - "editor debug" - ] - }, - { - "id": "editor.debug.action.triggerByBreakpoint", - "title": "editor.debug.action.triggerByBreakpoint", - "keywords": [ - "editor debug action trigger by breakpoint", - "editor debug" - ] - }, - { - "id": "editor.debug.action.editBreakpoint", - "title": "editor.debug.action.editBreakpoint", - "keywords": [ - "editor debug action edit breakpoint", - "editor debug" - ] - }, - { - "id": "editor.debug.action.runToCursor", - "title": "editor.debug.action.runToCursor", - "keywords": [ - "editor debug action run to cursor", - "editor debug" - ] - }, - { - "id": "editor.debug.action.stepIntoTargets", - "title": "editor.debug.action.stepIntoTargets", - "keywords": [ - "editor debug action step into targets", - "editor debug" - ] - }, - { - "id": "editor.debug.action.selectionToRepl", - "title": "editor.debug.action.selectionToRepl", - "keywords": [ - "editor debug action selection to repl", - "editor debug" - ] - }, - { - "id": "editor.debug.action.selectionToWatch", - "title": "editor.debug.action.selectionToWatch", - "keywords": [ - "editor debug action selection to watch", - "editor debug" - ] - }, - { - "id": "editor.debug.action.showDebugHover", - "title": "editor.debug.action.showDebugHover", - "keywords": [ - "editor debug action show debug hover", - "editor debug" - ] - }, - { - "id": "editor.debug.action.goToNextBreakpoint", - "title": "editor.debug.action.goToNextBreakpoint", - "keywords": [ - "editor debug action go to next breakpoint", - "editor debug" - ] - }, - { - "id": "editor.debug.action.goToPreviousBreakpoint", - "title": "editor.debug.action.goToPreviousBreakpoint", - "keywords": [ - "editor debug action go to previous breakpoint", - "editor debug" - ] - }, - { - "id": "editor.debug.action.closeExceptionWidget", - "title": "editor.debug.action.closeExceptionWidget", - "keywords": [ - "editor debug action close exception widget", - "editor debug" - ] - }, - { - "id": "workbench.action.debug.configure", - "title": "workbench.action.debug.configure", - "keywords": [ - "debug configure" - ] - }, - { - "id": "debug.toggleReplIgnoreFocus", - "title": "debug.toggleReplIgnoreFocus", - "keywords": [ - "debug toggle repl ignore focus", - "debug toggle" - ] - }, - { - "id": "loadedScripts.collapse", - "title": "loadedScripts.collapse", - "keywords": [ - "loaded scripts collapse", - "loaded scripts" - ] - }, - { - "id": "watch.collapse", - "title": "watch.collapse", - "keywords": [ - "watch collapse" - ] - }, - { - "id": "workbench.debug.viewlet.action.addWatchExpression", - "title": "workbench.debug.viewlet.action.addWatchExpression", - "keywords": [ - "workbench debug viewlet action add watch expression", - "workbench debug" - ] - }, - { - "id": "workbench.debug.viewlet.action.removeAllWatchExpressions", - "title": "workbench.debug.viewlet.action.removeAllWatchExpressions", - "keywords": [ - "workbench debug viewlet action remove all watch expressions", - "workbench debug" - ] - }, - { - "id": "debug.copyWatchExpression", - "title": "debug.copyWatchExpression", - "keywords": [ - "debug copy watch expression", - "debug copy" - ] - }, - { - "id": "workbench.debug.viewlet.action.copyAllWatchExpressions", - "title": "workbench.debug.viewlet.action.copyAllWatchExpressions", - "keywords": [ - "workbench debug viewlet action copy all watch expressions", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.addVariableToChat", - "title": "workbench.debug.action.addVariableToChat", - "keywords": [ - "workbench debug action add variable to chat", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.addWatchExpressionToChat", - "title": "workbench.debug.action.addWatchExpressionToChat", - "keywords": [ - "workbench debug action add watch expression to chat", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.addScopeToChat", - "title": "workbench.debug.action.addScopeToChat", - "keywords": [ - "workbench debug action add scope to chat", - "workbench debug" - ] - }, - { - "id": "problems.action.open", - "title": "problems.action.open", - "keywords": [ - "problems action open", - "problems action" - ] - }, - { - "id": "problems.action.openToSide", - "title": "problems.action.openToSide", - "keywords": [ - "problems action open to side", - "problems action" - ] - }, - { - "id": "workbench.action.showErrorsWarnings", - "title": "workbench.action.showErrorsWarnings", - "keywords": [ - "show errors warnings", - "show errors" - ] - }, - { - "id": "problems.action.showQuickFixes", - "title": "problems.action.showQuickFixes", - "keywords": [ - "problems action show quick fixes", - "problems action" - ] - }, - { - "id": "workbench.actions.table.workbench.panel.markers.view.viewAsTree", - "title": "workbench.actions.table.workbench.panel.markers.view.viewAsTree", - "keywords": [ - "workbench actions table workbench panel markers view view as tree", - "workbench actions" - ] - }, - { - "id": "workbench.actions.table.workbench.panel.markers.view.viewAsTable", - "title": "workbench.actions.table.workbench.panel.markers.view.viewAsTable", - "keywords": [ - "workbench actions table workbench panel markers view view as table", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.markers.view.toggleErrors", - "title": "workbench.actions.workbench.panel.markers.view.toggleErrors", - "keywords": [ - "workbench actions workbench panel markers view toggle errors", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.markers.view.toggleWarnings", - "title": "workbench.actions.workbench.panel.markers.view.toggleWarnings", - "keywords": [ - "workbench actions workbench panel markers view toggle warnings", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.markers.view.toggleInfos", - "title": "workbench.actions.workbench.panel.markers.view.toggleInfos", - "keywords": [ - "workbench actions workbench panel markers view toggle infos", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.markers.view.toggleActiveFile", - "title": "workbench.actions.workbench.panel.markers.view.toggleActiveFile", - "keywords": [ - "workbench actions workbench panel markers view toggle active file", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.markers.view.toggleExcludedFiles", - "title": "workbench.actions.workbench.panel.markers.view.toggleExcludedFiles", - "keywords": [ - "workbench actions workbench panel markers view toggle excluded files", - "workbench actions" - ] - }, - { - "id": "workbench.action.problems.focus", - "title": "workbench.action.problems.focus", - "keywords": [ - "problems focus" - ] - }, - { - "id": "problems.action.copy", - "title": "problems.action.copy", - "keywords": [ - "problems action copy", - "problems action" - ] - }, - { - "id": "problems.action.copyMessage", - "title": "problems.action.copyMessage", - "keywords": [ - "problems action copy message", - "problems action" - ] - }, - { - "id": "problems.action.copyRelatedInformationMessage", - "title": "problems.action.copyRelatedInformationMessage", - "keywords": [ - "problems action copy related information message", - "problems action" - ] - }, - { - "id": "problems.action.focusProblemsFromFilter", - "title": "problems.action.focusProblemsFromFilter", - "keywords": [ - "problems action focus problems from filter", - "problems action" - ] - }, - { - "id": "problems.action.focusFilter", - "title": "problems.action.focusFilter", - "keywords": [ - "problems action focus filter", - "problems action" - ] - }, - { - "id": "problems.action.showMultilineMessage", - "title": "problems.action.showMultilineMessage", - "keywords": [ - "problems action show multiline message", - "problems action" - ] - }, - { - "id": "problems.action.showSinglelineMessage", - "title": "problems.action.showSinglelineMessage", - "keywords": [ - "problems action show singleline message", - "problems action" - ] - }, - { - "id": "problems.action.clearFilterText", - "title": "problems.action.clearFilterText", - "keywords": [ - "problems action clear filter text", - "problems action" - ] - }, - { - "id": "workbench.actions.treeView.workbench.panel.markers.view.collapseAll", - "title": "workbench.actions.treeView.workbench.panel.markers.view.collapseAll", - "keywords": [ - "workbench actions tree view workbench panel markers view collapse all", - "workbench actions" - ] - }, - { - "id": "workbench.actions.view.toggleProblems", - "title": "workbench.actions.view.toggleProblems", - "keywords": [ - "workbench actions view toggle problems", - "workbench actions" - ] - }, - { - "id": "workbench.action.openProcessExplorer", - "title": "workbench.action.openProcessExplorer", - "keywords": [ - "open process explorer", - "open process" - ] - }, - { - "id": "merge.openResult", - "title": "merge.openResult", - "keywords": [ - "merge open result", - "merge open" - ] - }, - { - "id": "merge.mixedLayout", - "title": "merge.mixedLayout", - "keywords": [ - "merge mixed layout", - "merge mixed" - ] - }, - { - "id": "merge.columnLayout", - "title": "merge.columnLayout", - "keywords": [ - "merge column layout", - "merge column" - ] - }, - { - "id": "merge.openBaseEditor", - "title": "merge.openBaseEditor", - "keywords": [ - "merge open base editor", - "merge open" - ] - }, - { - "id": "merge.showNonConflictingChanges", - "title": "merge.showNonConflictingChanges", - "keywords": [ - "merge show non conflicting changes", - "merge show" - ] - }, - { - "id": "merge.showBase", - "title": "merge.showBase", - "keywords": [ - "merge show base", - "merge show" - ] - }, - { - "id": "merge.showBaseTop", - "title": "merge.showBaseTop", - "keywords": [ - "merge show base top", - "merge show" - ] - }, - { - "id": "merge.showBaseCenter", - "title": "merge.showBaseCenter", - "keywords": [ - "merge show base center", - "merge show" - ] - }, - { - "id": "merge.goToNextUnhandledConflict", - "title": "merge.goToNextUnhandledConflict", - "keywords": [ - "merge go to next unhandled conflict", - "merge go" - ] - }, - { - "id": "merge.goToPreviousUnhandledConflict", - "title": "merge.goToPreviousUnhandledConflict", - "keywords": [ - "merge go to previous unhandled conflict", - "merge go" - ] - }, - { - "id": "merge.toggleActiveConflictInput1", - "title": "merge.toggleActiveConflictInput1", - "keywords": [ - "merge toggle active conflict input1", - "merge toggle" - ] - }, - { - "id": "merge.toggleActiveConflictInput2", - "title": "merge.toggleActiveConflictInput2", - "keywords": [ - "merge toggle active conflict input2", - "merge toggle" - ] - }, - { - "id": "mergeEditor.compareInput1WithBase", - "title": "mergeEditor.compareInput1WithBase", - "keywords": [ - "merge editor compare input1with base", - "merge editor" - ] - }, - { - "id": "mergeEditor.compareInput2WithBase", - "title": "mergeEditor.compareInput2WithBase", - "keywords": [ - "merge editor compare input2with base", - "merge editor" - ] - }, - { - "id": "merge.acceptAllInput1", - "title": "merge.acceptAllInput1", - "keywords": [ - "merge accept all input1", - "merge accept" - ] - }, - { - "id": "merge.acceptAllInput2", - "title": "merge.acceptAllInput2", - "keywords": [ - "merge accept all input2", - "merge accept" - ] - }, - { - "id": "mergeEditor.resetResultToBaseAndAutoMerge", - "title": "mergeEditor.resetResultToBaseAndAutoMerge", - "keywords": [ - "merge editor reset result to base and auto merge", - "merge editor" - ] - }, - { - "id": "mergeEditor.acceptMerge", - "title": "mergeEditor.acceptMerge", - "keywords": [ - "merge editor accept merge", - "merge editor" - ] - }, - { - "id": "mergeEditor.resetCloseWithConflictsChoice", - "title": "mergeEditor.resetCloseWithConflictsChoice", - "keywords": [ - "merge editor reset close with conflicts choice", - "merge editor" - ] - }, - { - "id": "mergeEditor.acceptAllCombination", - "title": "mergeEditor.acceptAllCombination", - "keywords": [ - "merge editor accept all combination", - "merge editor" - ] - }, - { - "id": "mergeEditor.toggleBetweenInputs", - "title": "mergeEditor.toggleBetweenInputs", - "keywords": [ - "merge editor toggle between inputs", - "merge editor" - ] - }, - { - "id": "merge.dev.copyContentsJson", - "title": "merge.dev.copyContentsJson", - "keywords": [ - "merge dev copy contents json", - "merge dev" - ] - }, - { - "id": "merge.dev.saveContentsToFolder", - "title": "merge.dev.saveContentsToFolder", - "keywords": [ - "merge dev save contents to folder", - "merge dev" - ] - }, - { - "id": "merge.dev.loadContentsFromFolder", - "title": "merge.dev.loadContentsFromFolder", - "keywords": [ - "merge dev load contents from folder", - "merge dev" - ] - }, - { - "id": "multiDiffEditor.goToFile", - "title": "multiDiffEditor.goToFile", - "keywords": [ - "multi diff editor go to file", - "multi diff" - ] - }, - { - "id": "multiDiffEditor.goToNextChange", - "title": "multiDiffEditor.goToNextChange", - "keywords": [ - "multi diff editor go to next change", - "multi diff" - ] - }, - { - "id": "multiDiffEditor.goToPreviousChange", - "title": "multiDiffEditor.goToPreviousChange", - "keywords": [ - "multi diff editor go to previous change", - "multi diff" - ] - }, - { - "id": "multiDiffEditor.collapseAll", - "title": "multiDiffEditor.collapseAll", - "keywords": [ - "multi diff editor collapse all", - "multi diff" - ] - }, - { - "id": "multiDiffEditor.expandAll", - "title": "multiDiffEditor.expandAll", - "keywords": [ - "multi diff editor expand all", - "multi diff" - ] - }, - { - "id": "runCommands", - "title": "runCommands", - "keywords": [ - "run commands" - ] - }, - { - "id": "editor.action.nextCommentThreadAction", - "title": "editor.action.nextCommentThreadAction", - "keywords": [ - "next comment thread action", - "next comment" - ] - }, - { - "id": "editor.action.previousCommentThreadAction", - "title": "editor.action.previousCommentThreadAction", - "keywords": [ - "previous comment thread action", - "previous comment" - ] - }, - { - "id": "editor.action.nextCommentedRangeAction", - "title": "editor.action.nextCommentedRangeAction", - "keywords": [ - "next commented range action", - "next commented" - ] - }, - { - "id": "editor.action.previousCommentedRangeAction", - "title": "editor.action.previousCommentedRangeAction", - "keywords": [ - "previous commented range action", - "previous commented" - ] - }, - { - "id": "editor.action.nextCommentingRange", - "title": "editor.action.nextCommentingRange", - "keywords": [ - "next commenting range", - "next commenting" - ] - }, - { - "id": "editor.action.previousCommentingRange", - "title": "editor.action.previousCommentingRange", - "keywords": [ - "previous commenting range", - "previous commenting" - ] - }, - { - "id": "workbench.action.toggleCommenting", - "title": "workbench.action.toggleCommenting", - "keywords": [ - "toggle commenting" - ] - }, - { - "id": "workbench.action.addComment", - "title": "workbench.action.addComment", - "keywords": [ - "add comment" - ] - }, - { - "id": "workbench.action.focusCommentOnCurrentLine", - "title": "workbench.action.focusCommentOnCurrentLine", - "keywords": [ - "focus comment on current line", - "focus comment" - ] - }, - { - "id": "workbench.action.collapseAllComments", - "title": "workbench.action.collapseAllComments", - "keywords": [ - "collapse all comments", - "collapse all" - ] - }, - { - "id": "workbench.action.expandAllComments", - "title": "workbench.action.expandAllComments", - "keywords": [ - "expand all comments", - "expand all" - ] - }, - { - "id": "workbench.action.expandUnresolvedComments", - "title": "workbench.action.expandUnresolvedComments", - "keywords": [ - "expand unresolved comments", - "expand unresolved" - ] - }, - { - "id": "editor.action.submitComment", - "title": "editor.action.submitComment", - "keywords": [ - "submit comment" - ] - }, - { - "id": "workbench.action.hideComment", - "title": "workbench.action.hideComment", - "keywords": [ - "hide comment" - ] - }, - { - "id": "comments.collapse", - "title": "comments.collapse", - "keywords": [ - "comments collapse" - ] - }, - { - "id": "comments.expand", - "title": "comments.expand", - "keywords": [ - "comments expand" - ] - }, - { - "id": "comments.reply", - "title": "comments.reply", - "keywords": [ - "comments reply" - ] - }, - { - "id": "workbench.action.url.openUrl", - "title": "workbench.action.url.openUrl", - "keywords": [ - "url open url", - "url open" - ] - }, - { - "id": "workbench.action.manageTrustedDomain", - "title": "workbench.action.manageTrustedDomain", - "keywords": [ - "manage trusted domain", - "manage trusted" - ] - }, - { - "id": "editor.action.webvieweditor.showFind", - "title": "editor.action.webvieweditor.showFind", - "keywords": [ - "webvieweditor show find", - "webvieweditor show" - ] - }, - { - "id": "editor.action.webvieweditor.hideFind", - "title": "editor.action.webvieweditor.hideFind", - "keywords": [ - "webvieweditor hide find", - "webvieweditor hide" - ] - }, - { - "id": "editor.action.webvieweditor.findNext", - "title": "editor.action.webvieweditor.findNext", - "keywords": [ - "webvieweditor find next", - "webvieweditor find" - ] - }, - { - "id": "editor.action.webvieweditor.findPrevious", - "title": "editor.action.webvieweditor.findPrevious", - "keywords": [ - "webvieweditor find previous", - "webvieweditor find" - ] - }, - { - "id": "workbench.action.webview.reloadWebviewAction", - "title": "workbench.action.webview.reloadWebviewAction", - "keywords": [ - "webview reload webview action", - "webview reload" - ] - }, - { - "id": "iconSelectBox.focusUp", - "title": "iconSelectBox.focusUp", - "keywords": [ - "icon select box focus up", - "icon select" - ] - }, - { - "id": "iconSelectBox.focusDown", - "title": "iconSelectBox.focusDown", - "keywords": [ - "icon select box focus down", - "icon select" - ] - }, - { - "id": "iconSelectBox.focusNext", - "title": "iconSelectBox.focusNext", - "keywords": [ - "icon select box focus next", - "icon select" - ] - }, - { - "id": "iconSelectBox.focusPrevious", - "title": "iconSelectBox.focusPrevious", - "keywords": [ - "icon select box focus previous", - "icon select" - ] - }, - { - "id": "iconSelectBox.selectFocused", - "title": "iconSelectBox.selectFocused", - "keywords": [ - "icon select box select focused", - "icon select" - ] - }, - { - "id": "workbench.action.terminal.newInActiveWorkspace", - "title": "workbench.action.terminal.newInActiveWorkspace", - "keywords": [ - "terminal new in active workspace", - "terminal new" - ] - }, - { - "id": "workbench.action.createTerminalEditor", - "title": "workbench.action.createTerminalEditor", - "keywords": [ - "create terminal editor", - "create terminal" - ] - }, - { - "id": "workbench.action.createTerminalEditorSameGroup", - "title": "workbench.action.createTerminalEditorSameGroup", - "keywords": [ - "create terminal editor same group", - "create terminal" - ] - }, - { - "id": "workbench.action.createTerminalEditorSide", - "title": "workbench.action.createTerminalEditorSide", - "keywords": [ - "create terminal editor side", - "create terminal" - ] - }, - { - "id": "workbench.action.terminal.newInNewWindow", - "title": "workbench.action.terminal.newInNewWindow", - "keywords": [ - "terminal new in new window", - "terminal new" - ] - }, - { - "id": "workbench.action.terminal.moveToEditor", - "title": "workbench.action.terminal.moveToEditor", - "keywords": [ - "terminal move to editor", - "terminal move" - ] - }, - { - "id": "workbench.action.terminal.moveIntoNewWindow", - "title": "workbench.action.terminal.moveIntoNewWindow", - "keywords": [ - "terminal move into new window", - "terminal move" - ] - }, - { - "id": "workbench.action.terminal.moveToTerminalPanel", - "title": "workbench.action.terminal.moveToTerminalPanel", - "keywords": [ - "terminal move to terminal panel", - "terminal move" - ] - }, - { - "id": "workbench.action.terminal.focusPreviousPane", - "title": "workbench.action.terminal.focusPreviousPane", - "keywords": [ - "terminal focus previous pane", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusNextPane", - "title": "workbench.action.terminal.focusNextPane", - "keywords": [ - "terminal focus next pane", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.resizePaneLeft", - "title": "workbench.action.terminal.resizePaneLeft", - "keywords": [ - "terminal resize pane left", - "terminal resize" - ] - }, - { - "id": "workbench.action.terminal.resizePaneRight", - "title": "workbench.action.terminal.resizePaneRight", - "keywords": [ - "terminal resize pane right", - "terminal resize" - ] - }, - { - "id": "workbench.action.terminal.resizePaneUp", - "title": "workbench.action.terminal.resizePaneUp", - "keywords": [ - "terminal resize pane up", - "terminal resize" - ] - }, - { - "id": "workbench.action.terminal.resizePaneDown", - "title": "workbench.action.terminal.resizePaneDown", - "keywords": [ - "terminal resize pane down", - "terminal resize" - ] - }, - { - "id": "workbench.action.terminal.focus", - "title": "workbench.action.terminal.focus", - "keywords": [ - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusTabs", - "title": "workbench.action.terminal.focusTabs", - "keywords": [ - "terminal focus tabs", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusNext", - "title": "workbench.action.terminal.focusNext", - "keywords": [ - "terminal focus next", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusPrevious", - "title": "workbench.action.terminal.focusPrevious", - "keywords": [ - "terminal focus previous", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.runSelectedText", - "title": "workbench.action.terminal.runSelectedText", - "keywords": [ - "terminal run selected text", - "terminal run" - ] - }, - { - "id": "workbench.action.terminal.runActiveFile", - "title": "workbench.action.terminal.runActiveFile", - "keywords": [ - "terminal run active file", - "terminal run" - ] - }, - { - "id": "workbench.action.terminal.scrollDown", - "title": "workbench.action.terminal.scrollDown", - "keywords": [ - "terminal scroll down", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollDownPage", - "title": "workbench.action.terminal.scrollDownPage", - "keywords": [ - "terminal scroll down page", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollToBottom", - "title": "workbench.action.terminal.scrollToBottom", - "keywords": [ - "terminal scroll to bottom", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollUp", - "title": "workbench.action.terminal.scrollUp", - "keywords": [ - "terminal scroll up", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollUpPage", - "title": "workbench.action.terminal.scrollUpPage", - "keywords": [ - "terminal scroll up page", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollToTop", - "title": "workbench.action.terminal.scrollToTop", - "keywords": [ - "terminal scroll to top", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.clearSelection", - "title": "workbench.action.terminal.clearSelection", - "keywords": [ - "terminal clear selection", - "terminal clear" - ] - }, - { - "id": "workbench.action.terminal.changeIcon", - "title": "workbench.action.terminal.changeIcon", - "keywords": [ - "terminal change icon", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.changeIconActiveTab", - "title": "workbench.action.terminal.changeIconActiveTab", - "keywords": [ - "terminal change icon active tab", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.changeColor", - "title": "workbench.action.terminal.changeColor", - "keywords": [ - "terminal change color", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.changeColorActiveTab", - "title": "workbench.action.terminal.changeColorActiveTab", - "keywords": [ - "terminal change color active tab", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.rename", - "title": "workbench.action.terminal.rename", - "keywords": [ - "terminal rename" - ] - }, - { - "id": "workbench.action.terminal.renameActiveTab", - "title": "workbench.action.terminal.renameActiveTab", - "keywords": [ - "terminal rename active tab", - "terminal rename" - ] - }, - { - "id": "workbench.action.terminal.detachSession", - "title": "workbench.action.terminal.detachSession", - "keywords": [ - "terminal detach session", - "terminal detach" - ] - }, - { - "id": "workbench.action.terminal.attachToSession", - "title": "workbench.action.terminal.attachToSession", - "keywords": [ - "terminal attach to session", - "terminal attach" - ] - }, - { - "id": "workbench.action.terminal.scrollToPreviousCommand", - "title": "workbench.action.terminal.scrollToPreviousCommand", - "keywords": [ - "terminal scroll to previous command", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollToNextCommand", - "title": "workbench.action.terminal.scrollToNextCommand", - "keywords": [ - "terminal scroll to next command", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.selectToPreviousCommand", - "title": "workbench.action.terminal.selectToPreviousCommand", - "keywords": [ - "terminal select to previous command", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.selectToNextCommand", - "title": "workbench.action.terminal.selectToNextCommand", - "keywords": [ - "terminal select to next command", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.selectToPreviousLine", - "title": "workbench.action.terminal.selectToPreviousLine", - "keywords": [ - "terminal select to previous line", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.selectToNextLine", - "title": "workbench.action.terminal.selectToNextLine", - "keywords": [ - "terminal select to next line", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.newWithCwd", - "title": "workbench.action.terminal.newWithCwd", - "keywords": [ - "terminal new with cwd", - "terminal new" - ] - }, - { - "id": "workbench.action.terminal.renameWithArg", - "title": "workbench.action.terminal.renameWithArg", - "keywords": [ - "terminal rename with arg", - "terminal rename" - ] - }, - { - "id": "workbench.action.terminal.relaunch", - "title": "workbench.action.terminal.relaunch", - "keywords": [ - "terminal relaunch" - ] - }, - { - "id": "workbench.action.terminal.split", - "title": "workbench.action.terminal.split", - "keywords": [ - "terminal split" - ] - }, - { - "id": "workbench.action.terminal.splitActiveTab", - "title": "workbench.action.terminal.splitActiveTab", - "keywords": [ - "terminal split active tab", - "terminal split" - ] - }, - { - "id": "workbench.action.terminal.unsplit", - "title": "workbench.action.terminal.unsplit", - "keywords": [ - "terminal unsplit" - ] - }, - { - "id": "workbench.action.terminal.joinActiveTab", - "title": "workbench.action.terminal.joinActiveTab", - "keywords": [ - "terminal join active tab", - "terminal join" - ] - }, - { - "id": "workbench.action.terminal.join", - "title": "workbench.action.terminal.join", - "keywords": [ - "terminal join" - ] - }, - { - "id": "workbench.action.terminal.splitInActiveWorkspace", - "title": "workbench.action.terminal.splitInActiveWorkspace", - "keywords": [ - "terminal split in active workspace", - "terminal split" - ] - }, - { - "id": "workbench.action.terminal.selectAll", - "title": "workbench.action.terminal.selectAll", - "keywords": [ - "terminal select all", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.new", - "title": "workbench.action.terminal.new", - "keywords": [ - "terminal new" - ] - }, - { - "id": "workbench.action.terminal.kill", - "title": "workbench.action.terminal.kill", - "keywords": [ - "terminal kill" - ] - }, - { - "id": "workbench.action.terminal.killViewOrEditor", - "title": "workbench.action.terminal.killViewOrEditor", - "keywords": [ - "terminal kill view or editor", - "terminal kill" - ] - }, - { - "id": "workbench.action.terminal.killAll", - "title": "workbench.action.terminal.killAll", - "keywords": [ - "terminal kill all", - "terminal kill" - ] - }, - { - "id": "workbench.action.terminal.killEditor", - "title": "workbench.action.terminal.killEditor", - "keywords": [ - "terminal kill editor", - "terminal kill" - ] - }, - { - "id": "workbench.action.terminal.killActiveTab", - "title": "workbench.action.terminal.killActiveTab", - "keywords": [ - "terminal kill active tab", - "terminal kill" - ] - }, - { - "id": "workbench.action.terminal.focusHover", - "title": "workbench.action.terminal.focusHover", - "keywords": [ - "terminal focus hover", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.clear", - "title": "workbench.action.terminal.clear", - "keywords": [ - "terminal clear" - ] - }, - { - "id": "workbench.action.terminal.selectDefaultShell", - "title": "workbench.action.terminal.selectDefaultShell", - "keywords": [ - "terminal select default shell", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.openSettings", - "title": "workbench.action.terminal.openSettings", - "keywords": [ - "terminal open settings", - "terminal open" - ] - }, - { - "id": "workbench.action.terminal.setDimensions", - "title": "workbench.action.terminal.setDimensions", - "keywords": [ - "terminal set dimensions", - "terminal set" - ] - }, - { - "id": "workbench.action.terminal.sizeToContentWidth", - "title": "workbench.action.terminal.sizeToContentWidth", - "keywords": [ - "terminal size to content width", - "terminal size" - ] - }, - { - "id": "workbench.action.terminal.switchTerminal", - "title": "workbench.action.terminal.switchTerminal", - "keywords": [ - "terminal switch terminal", - "terminal switch" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex1", - "title": "workbench.action.terminal.focusAtIndex1", - "keywords": [ - "terminal focus at index1", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex2", - "title": "workbench.action.terminal.focusAtIndex2", - "keywords": [ - "terminal focus at index2", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex3", - "title": "workbench.action.terminal.focusAtIndex3", - "keywords": [ - "terminal focus at index3", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex4", - "title": "workbench.action.terminal.focusAtIndex4", - "keywords": [ - "terminal focus at index4", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex5", - "title": "workbench.action.terminal.focusAtIndex5", - "keywords": [ - "terminal focus at index5", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex6", - "title": "workbench.action.terminal.focusAtIndex6", - "keywords": [ - "terminal focus at index6", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex7", - "title": "workbench.action.terminal.focusAtIndex7", - "keywords": [ - "terminal focus at index7", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex8", - "title": "workbench.action.terminal.focusAtIndex8", - "keywords": [ - "terminal focus at index8", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAtIndex9", - "title": "workbench.action.terminal.focusAtIndex9", - "keywords": [ - "terminal focus at index9", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.focusAccessibleBuffer", - "title": "workbench.action.terminal.focusAccessibleBuffer", - "keywords": [ - "terminal focus accessible buffer", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.accessibleBufferGoToNextCommand", - "title": "workbench.action.terminal.accessibleBufferGoToNextCommand", - "keywords": [ - "terminal accessible buffer go to next command", - "terminal accessible" - ] - }, - { - "id": "workbench.action.terminal.accessibleBufferGoToPreviousCommand", - "title": "workbench.action.terminal.accessibleBufferGoToPreviousCommand", - "keywords": [ - "terminal accessible buffer go to previous command", - "terminal accessible" - ] - }, - { - "id": "workbench.action.terminal.scrollToBottomAccessibleView", - "title": "workbench.action.terminal.scrollToBottomAccessibleView", - "keywords": [ - "terminal scroll to bottom accessible view", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.scrollToTopAccessibleView", - "title": "workbench.action.terminal.scrollToTopAccessibleView", - "keywords": [ - "terminal scroll to top accessible view", - "terminal scroll" - ] - }, - { - "id": "workbench.action.terminal.chat.focusTerminalById", - "title": "workbench.action.terminal.chat.focusTerminalById", - "keywords": [ - "terminal chat focus terminal by id", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.focusTerminalByExecutionId", - "title": "workbench.action.terminal.chat.focusTerminalByExecutionId", - "keywords": [ - "terminal chat focus terminal by execution id", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.addTerminalSelection", - "title": "workbench.action.terminal.chat.addTerminalSelection", - "keywords": [ - "terminal chat add terminal selection", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.showTextureAtlas", - "title": "workbench.action.terminal.showTextureAtlas", - "keywords": [ - "terminal show texture atlas", - "terminal show" - ] - }, - { - "id": "workbench.action.terminal.writeDataToTerminal", - "title": "workbench.action.terminal.writeDataToTerminal", - "keywords": [ - "terminal write data to terminal", - "terminal write" - ] - }, - { - "id": "workbench.action.terminal.recordSession", - "title": "workbench.action.terminal.recordSession", - "keywords": [ - "terminal record session", - "terminal record" - ] - }, - { - "id": "workbench.action.terminal.restartPtyHost", - "title": "workbench.action.terminal.restartPtyHost", - "keywords": [ - "terminal restart pty host", - "terminal restart" - ] - }, - { - "id": "workbench.action.terminal.showEnvironmentContributions", - "title": "workbench.action.terminal.showEnvironmentContributions", - "keywords": [ - "terminal show environment contributions", - "terminal show" - ] - }, - { - "id": "workbench.action.terminal.copyLastCommand", - "title": "workbench.action.terminal.copyLastCommand", - "keywords": [ - "terminal copy last command", - "terminal copy" - ] - }, - { - "id": "workbench.action.terminal.copyLastCommandOutput", - "title": "workbench.action.terminal.copyLastCommandOutput", - "keywords": [ - "terminal copy last command output", - "terminal copy" - ] - }, - { - "id": "workbench.action.terminal.copyLastCommandAndLastCommandOutput", - "title": "workbench.action.terminal.copyLastCommandAndLastCommandOutput", - "keywords": [ - "terminal copy last command and last command output", - "terminal copy" - ] - }, - { - "id": "workbench.action.terminal.copySelection", - "title": "workbench.action.terminal.copySelection", - "keywords": [ - "terminal copy selection", - "terminal copy" - ] - }, - { - "id": "workbench.action.terminal.copyAndClearSelection", - "title": "workbench.action.terminal.copyAndClearSelection", - "keywords": [ - "terminal copy and clear selection", - "terminal copy" - ] - }, - { - "id": "workbench.action.terminal.copySelectionAsHtml", - "title": "workbench.action.terminal.copySelectionAsHtml", - "keywords": [ - "terminal copy selection as html", - "terminal copy" - ] - }, - { - "id": "workbench.action.terminal.paste", - "title": "workbench.action.terminal.paste", - "keywords": [ - "terminal paste" - ] - }, - { - "id": "workbench.action.terminal.focusFind", - "title": "workbench.action.terminal.focusFind", - "keywords": [ - "terminal focus find", - "terminal focus" - ] - }, - { - "id": "workbench.action.terminal.hideFind", - "title": "workbench.action.terminal.hideFind", - "keywords": [ - "terminal hide find", - "terminal hide" - ] - }, - { - "id": "workbench.action.terminal.toggleFindRegex", - "title": "workbench.action.terminal.toggleFindRegex", - "keywords": [ - "terminal toggle find regex", - "terminal toggle" - ] - }, - { - "id": "workbench.action.terminal.toggleFindWholeWord", - "title": "workbench.action.terminal.toggleFindWholeWord", - "keywords": [ - "terminal toggle find whole word", - "terminal toggle" - ] - }, - { - "id": "workbench.action.terminal.toggleFindCaseSensitive", - "title": "workbench.action.terminal.toggleFindCaseSensitive", - "keywords": [ - "terminal toggle find case sensitive", - "terminal toggle" - ] - }, - { - "id": "workbench.action.terminal.findNext", - "title": "workbench.action.terminal.findNext", - "keywords": [ - "terminal find next", - "terminal find" - ] - }, - { - "id": "workbench.action.terminal.findPrevious", - "title": "workbench.action.terminal.findPrevious", - "keywords": [ - "terminal find previous", - "terminal find" - ] - }, - { - "id": "workbench.action.terminal.searchWorkspace", - "title": "workbench.action.terminal.searchWorkspace", - "keywords": [ - "terminal search workspace", - "terminal search" - ] - }, - { - "id": "workbench.action.terminal.chat.start", - "title": "workbench.action.terminal.chat.start", - "keywords": [ - "terminal chat start", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.close", - "title": "workbench.action.terminal.chat.close", - "keywords": [ - "terminal chat close", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.runCommand", - "title": "workbench.action.terminal.chat.runCommand", - "keywords": [ - "terminal chat run command", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.runFirstCommand", - "title": "workbench.action.terminal.chat.runFirstCommand", - "keywords": [ - "terminal chat run first command", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.insertCommand", - "title": "workbench.action.terminal.chat.insertCommand", - "keywords": [ - "terminal chat insert command", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.insertFirstCommand", - "title": "workbench.action.terminal.chat.insertFirstCommand", - "keywords": [ - "terminal chat insert first command", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.rerunRequest", - "title": "workbench.action.terminal.chat.rerunRequest", - "keywords": [ - "terminal chat rerun request", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.viewInChat", - "title": "workbench.action.terminal.chat.viewInChat", - "keywords": [ - "terminal chat view in chat", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.viewHiddenChatTerminals", - "title": "workbench.action.terminal.chat.viewHiddenChatTerminals", - "keywords": [ - "terminal chat view hidden chat terminals", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.focusMostRecentChatTerminal", - "title": "workbench.action.terminal.chat.focusMostRecentChatTerminal", - "keywords": [ - "terminal chat focus most recent chat terminal", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.focusMostRecentChatTerminalOutput", - "title": "workbench.action.terminal.chat.focusMostRecentChatTerminalOutput", - "keywords": [ - "terminal chat focus most recent chat terminal output", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.openTerminalSettingsLink", - "title": "workbench.action.terminal.chat.openTerminalSettingsLink", - "keywords": [ - "terminal chat open terminal settings link", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.chat.disableSessionAutoApproval", - "title": "workbench.action.terminal.chat.disableSessionAutoApproval", - "keywords": [ - "terminal chat disable session auto approval", - "terminal chat" - ] - }, - { - "id": "workbench.action.terminal.clearPreviousSessionHistory", - "title": "workbench.action.terminal.clearPreviousSessionHistory", - "keywords": [ - "terminal clear previous session history", - "terminal clear" - ] - }, - { - "id": "workbench.action.terminal.goToRecentDirectory", - "title": "workbench.action.terminal.goToRecentDirectory", - "keywords": [ - "terminal go to recent directory", - "terminal go" - ] - }, - { - "id": "workbench.action.terminal.runRecentCommand", - "title": "workbench.action.terminal.runRecentCommand", - "keywords": [ - "terminal run recent command", - "terminal run" - ] - }, - { - "id": "workbench.action.terminal.openDetectedLink", - "title": "workbench.action.terminal.openDetectedLink", - "keywords": [ - "terminal open detected link", - "terminal open" - ] - }, - { - "id": "workbench.action.terminal.openUrlLink", - "title": "workbench.action.terminal.openUrlLink", - "keywords": [ - "terminal open url link", - "terminal open" - ] - }, - { - "id": "workbench.action.terminal.openFileLink", - "title": "workbench.action.terminal.openFileLink", - "keywords": [ - "terminal open file link", - "terminal open" - ] - }, - { - "id": "workbench.action.terminal.fontZoomIn", - "title": "workbench.action.terminal.fontZoomIn", - "keywords": [ - "terminal font zoom in", - "terminal font" - ] - }, - { - "id": "workbench.action.terminal.fontZoomOut", - "title": "workbench.action.terminal.fontZoomOut", - "keywords": [ - "terminal font zoom out", - "terminal font" - ] - }, - { - "id": "workbench.action.terminal.fontZoomReset", - "title": "workbench.action.terminal.fontZoomReset", - "keywords": [ - "terminal font zoom reset", - "terminal font" - ] - }, - { - "id": "workbench.action.terminal.toggleStickyScroll", - "title": "workbench.action.terminal.toggleStickyScroll", - "keywords": [ - "terminal toggle sticky scroll", - "terminal toggle" - ] - }, - { - "id": "workbench.action.quickOpenNavigateNextInTerminalPicker", - "title": "workbench.action.quickOpenNavigateNextInTerminalPicker", - "keywords": [ - "quick open navigate next in terminal picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenNavigatePreviousInTerminalPicker", - "title": "workbench.action.quickOpenNavigatePreviousInTerminalPicker", - "keywords": [ - "quick open navigate previous in terminal picker", - "quick open" - ] - }, - { - "id": "workbench.action.quickOpenTerm", - "title": "workbench.action.quickOpenTerm", - "keywords": [ - "quick open term", - "quick open" - ] - }, - { - "id": "workbench.action.terminal.showQuickFixes", - "title": "workbench.action.terminal.showQuickFixes", - "keywords": [ - "terminal show quick fixes", - "terminal show" - ] - }, - { - "id": "workbench.action.terminal.sendSequence", - "title": "workbench.action.terminal.sendSequence", - "keywords": [ - "terminal send sequence", - "terminal send" - ] - }, - { - "id": "workbench.action.terminal.pastePwsh", - "title": "workbench.action.terminal.pastePwsh", - "keywords": [ - "terminal paste pwsh", - "terminal paste" - ] - }, - { - "id": "workbench.action.terminal.sendSignal", - "title": "workbench.action.terminal.sendSignal", - "keywords": [ - "terminal send signal", - "terminal send" - ] - }, - { - "id": "workbench.action.terminal.changeSelectionModeNever", - "title": "workbench.action.terminal.changeSelectionModeNever", - "keywords": [ - "terminal change selection mode never", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.changeSelectionModePartial", - "title": "workbench.action.terminal.changeSelectionModePartial", - "keywords": [ - "terminal change selection mode partial", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.changeSelectionModeAlways", - "title": "workbench.action.terminal.changeSelectionModeAlways", - "keywords": [ - "terminal change selection mode always", - "terminal change" - ] - }, - { - "id": "workbench.action.terminal.doNotShowSuggestOnType", - "title": "workbench.action.terminal.doNotShowSuggestOnType", - "keywords": [ - "terminal do not show suggest on type", - "terminal do" - ] - }, - { - "id": "workbench.action.terminal.showSuggestOnType", - "title": "workbench.action.terminal.showSuggestOnType", - "keywords": [ - "terminal show suggest on type", - "terminal show" - ] - }, - { - "id": "workbench.action.terminal.suggestLearnMore", - "title": "workbench.action.terminal.suggestLearnMore", - "keywords": [ - "terminal suggest learn more", - "terminal suggest" - ] - }, - { - "id": "workbench.action.terminal.configureSuggestSettings", - "title": "workbench.action.terminal.configureSuggestSettings", - "keywords": [ - "terminal configure suggest settings", - "terminal configure" - ] - }, - { - "id": "workbench.action.terminal.triggerSuggest", - "title": "workbench.action.terminal.triggerSuggest", - "keywords": [ - "terminal trigger suggest", - "terminal trigger" - ] - }, - { - "id": "workbench.action.terminal.resetSuggestWidgetSize", - "title": "workbench.action.terminal.resetSuggestWidgetSize", - "keywords": [ - "terminal reset suggest widget size", - "terminal reset" - ] - }, - { - "id": "workbench.action.terminal.selectPrevSuggestion", - "title": "workbench.action.terminal.selectPrevSuggestion", - "keywords": [ - "terminal select prev suggestion", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.selectPrevPageSuggestion", - "title": "workbench.action.terminal.selectPrevPageSuggestion", - "keywords": [ - "terminal select prev page suggestion", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.selectNextSuggestion", - "title": "workbench.action.terminal.selectNextSuggestion", - "keywords": [ - "terminal select next suggestion", - "terminal select" - ] - }, - { - "id": "terminalSuggestToggleExplainMode", - "title": "terminalSuggestToggleExplainMode", - "keywords": [ - "terminal suggest toggle explain mode", - "terminal suggest" - ] - }, - { - "id": "workbench.action.terminal.suggestToggleDetailsFocus", - "title": "workbench.action.terminal.suggestToggleDetailsFocus", - "keywords": [ - "terminal suggest toggle details focus", - "terminal suggest" - ] - }, - { - "id": "workbench.action.terminal.suggestToggleDetails", - "title": "workbench.action.terminal.suggestToggleDetails", - "keywords": [ - "terminal suggest toggle details", - "terminal suggest" - ] - }, - { - "id": "workbench.action.terminal.selectNextPageSuggestion", - "title": "workbench.action.terminal.selectNextPageSuggestion", - "keywords": [ - "terminal select next page suggestion", - "terminal select" - ] - }, - { - "id": "workbench.action.terminal.acceptSelectedSuggestion", - "title": "workbench.action.terminal.acceptSelectedSuggestion", - "keywords": [ - "terminal accept selected suggestion", - "terminal accept" - ] - }, - { - "id": "workbench.action.terminal.acceptSelectedSuggestionEnter", - "title": "workbench.action.terminal.acceptSelectedSuggestionEnter", - "keywords": [ - "terminal accept selected suggestion enter", - "terminal accept" - ] - }, - { - "id": "workbench.action.terminal.hideSuggestWidget", - "title": "workbench.action.terminal.hideSuggestWidget", - "keywords": [ - "terminal hide suggest widget", - "terminal hide" - ] - }, - { - "id": "workbench.action.terminal.hideSuggestWidgetAndNavigateHistory", - "title": "workbench.action.terminal.hideSuggestWidgetAndNavigateHistory", - "keywords": [ - "terminal hide suggest widget and navigate history", - "terminal hide" - ] - }, - { - "id": "workbench.action.terminal.startVoice", - "title": "workbench.action.terminal.startVoice", - "keywords": [ - "terminal start voice", - "terminal start" - ] - }, - { - "id": "workbench.action.terminal.stopVoice", - "title": "workbench.action.terminal.stopVoice", - "keywords": [ - "terminal stop voice", - "terminal stop" - ] - }, - { - "id": "openInTerminal", - "title": "openInTerminal", - "keywords": [ - "open in terminal", - "open in" - ] - }, - { - "id": "openInIntegratedTerminal", - "title": "openInIntegratedTerminal", - "keywords": [ - "open in integrated terminal", - "open in" - ] - }, - { - "id": "workbench.action.tasks.manageAutomaticRunning", - "title": "workbench.action.tasks.manageAutomaticRunning", - "keywords": [ - "tasks manage automatic running", - "tasks manage" - ] - }, - { - "id": "workbench.action.tasks.rerunForActiveTerminal", - "title": "workbench.action.tasks.rerunForActiveTerminal", - "keywords": [ - "tasks rerun for active terminal", - "tasks rerun" - ] - }, - { - "id": "workbench.action.triggerReconnect", - "title": "workbench.action.triggerReconnect", - "keywords": [ - "trigger reconnect" - ] - }, - { - "id": "workbench.action.pauseSocketWriting", - "title": "workbench.action.pauseSocketWriting", - "keywords": [ - "pause socket writing", - "pause socket" - ] - }, - { - "id": "resetGettingStartedProgress", - "title": "resetGettingStartedProgress", - "keywords": [ - "reset getting started progress", - "reset getting" - ] - }, - { - "id": "editor.emmet.action.expandAbbreviation", - "title": "editor.emmet.action.expandAbbreviation", - "keywords": [ - "editor emmet action expand abbreviation", - "editor emmet" - ] - }, - { - "id": "editor.action.toggleScreenReaderAccessibilityMode", - "title": "editor.action.toggleScreenReaderAccessibilityMode", - "keywords": [ - "toggle screen reader accessibility mode", - "toggle screen" - ] - }, - { - "id": "editor.action.announceCursorPosition", - "title": "editor.action.announceCursorPosition", - "keywords": [ - "announce cursor position", - "announce cursor" - ] - }, - { - "id": "workbench.action.inspectKeyMappings", - "title": "workbench.action.inspectKeyMappings", - "keywords": [ - "inspect key mappings", - "inspect key" - ] - }, - { - "id": "workbench.action.inspectKeyMappingsJSON", - "title": "workbench.action.inspectKeyMappingsJSON", - "keywords": [ - "inspect key mappings json", - "inspect key" - ] - }, - { - "id": "workbench.action.gotoLine", - "title": "workbench.action.gotoLine", - "keywords": [ - "goto line" - ] - }, - { - "id": "workbench.action.gotoOffset", - "title": "workbench.action.gotoOffset", - "keywords": [ - "goto offset" - ] - }, - { - "id": "editor.action.formatChanges", - "title": "editor.action.formatChanges", - "keywords": [ - "format changes" - ] - }, - { - "id": "editor.action.toggleColumnSelection", - "title": "editor.action.toggleColumnSelection", - "keywords": [ - "toggle column selection", - "toggle column" - ] - }, - { - "id": "editor.action.toggleMinimap", - "title": "editor.action.toggleMinimap", - "keywords": [ - "toggle minimap" - ] - }, - { - "id": "editor.action.toggleOvertypeInsertMode", - "title": "editor.action.toggleOvertypeInsertMode", - "keywords": [ - "toggle overtype insert mode", - "toggle overtype" - ] - }, - { - "id": "workbench.action.toggleMultiCursorModifier", - "title": "workbench.action.toggleMultiCursorModifier", - "keywords": [ - "toggle multi cursor modifier", - "toggle multi" - ] - }, - { - "id": "editor.action.toggleRenderControlCharacter", - "title": "editor.action.toggleRenderControlCharacter", - "keywords": [ - "toggle render control character", - "toggle render" - ] - }, - { - "id": "editor.action.renderWhitespace.none", - "title": "editor.action.renderWhitespace.none", - "keywords": [ - "render whitespace none", - "render whitespace" - ] - }, - { - "id": "editor.action.renderWhitespace.boundary", - "title": "editor.action.renderWhitespace.boundary", - "keywords": [ - "render whitespace boundary", - "render whitespace" - ] - }, - { - "id": "editor.action.renderWhitespace.selection", - "title": "editor.action.renderWhitespace.selection", - "keywords": [ - "render whitespace selection", - "render whitespace" - ] - }, - { - "id": "editor.action.renderWhitespace.trailing", - "title": "editor.action.renderWhitespace.trailing", - "keywords": [ - "render whitespace trailing", - "render whitespace" - ] - }, - { - "id": "editor.action.renderWhitespace.all", - "title": "editor.action.renderWhitespace.all", - "keywords": [ - "render whitespace all", - "render whitespace" - ] - }, - { - "id": "editor.action.toggleRenderWhitespace", - "title": "editor.action.toggleRenderWhitespace", - "keywords": [ - "toggle render whitespace", - "toggle render" - ] - }, - { - "id": "workbench.action.toggleKeybindingsLog", - "title": "workbench.action.toggleKeybindingsLog", - "keywords": [ - "toggle keybindings log", - "toggle keybindings" - ] - }, - { - "id": "editor.action.insertSnippet", - "title": "editor.action.insertSnippet", - "keywords": [ - "insert snippet" - ] - }, - { - "id": "editor.action.showSnippets", - "title": "editor.action.showSnippets", - "keywords": [ - "show snippets" - ] - }, - { - "id": "editor.action.surroundWithSnippet", - "title": "editor.action.surroundWithSnippet", - "keywords": [ - "surround with snippet", - "surround with" - ] - }, - { - "id": "workbench.action.populateFileFromSnippet", - "title": "workbench.action.populateFileFromSnippet", - "keywords": [ - "populate file from snippet", - "populate file" - ] - }, - { - "id": "workbench.action.openSnippets", - "title": "workbench.action.openSnippets", - "keywords": [ - "open snippets" - ] - }, - { - "id": "editor.action.formatDocument.none", - "title": "editor.action.formatDocument.none", - "keywords": [ - "format document none", - "format document" - ] - }, - { - "id": "inlayHints.startReadingLineWithHint", - "title": "inlayHints.startReadingLineWithHint", - "keywords": [ - "inlay hints start reading line with hint", - "inlay hints" - ] - }, - { - "id": "inlayHints.stopReadingLineWithHint", - "title": "inlayHints.stopReadingLineWithHint", - "keywords": [ - "inlay hints stop reading line with hint", - "inlay hints" - ] - }, - { - "id": "workbench.action.selectTheme", - "title": "workbench.action.selectTheme", - "keywords": [ - "select theme" - ] - }, - { - "id": "workbench.action.selectIconTheme", - "title": "workbench.action.selectIconTheme", - "keywords": [ - "select icon theme", - "select icon" - ] - }, - { - "id": "workbench.action.selectProductIconTheme", - "title": "workbench.action.selectProductIconTheme", - "keywords": [ - "select product icon theme", - "select product" - ] - }, - { - "id": "workbench.action.tryNewDefaultThemes", - "title": "workbench.action.tryNewDefaultThemes", - "keywords": [ - "try new default themes", - "try new" - ] - }, - { - "id": "workbench.action.previewColorTheme", - "title": "workbench.action.previewColorTheme", - "keywords": [ - "preview color theme", - "preview color" - ] - }, - { - "id": "workbench.action.generateColorTheme", - "title": "workbench.action.generateColorTheme", - "keywords": [ - "generate color theme", - "generate color" - ] - }, - { - "id": "workbench.action.toggleLightDarkThemes", - "title": "workbench.action.toggleLightDarkThemes", - "keywords": [ - "toggle light dark themes", - "toggle light" - ] - }, - { - "id": "workbench.action.browseColorThemesInMarketplace", - "title": "workbench.action.browseColorThemesInMarketplace", - "keywords": [ - "browse color themes in marketplace", - "browse color" - ] - }, - { - "id": "workbench.actions.updateIndicator", - "title": "workbench.actions.updateIndicator", - "keywords": [ - "workbench actions update indicator", - "workbench actions" - ] - }, - { - "id": "update.showCurrentReleaseNotes", - "title": "update.showCurrentReleaseNotes", - "keywords": [ - "update show current release notes", - "update show" - ] - }, - { - "id": "developer.showCurrentFileAsReleaseNotes", - "title": "developer.showCurrentFileAsReleaseNotes", - "keywords": [ - "developer show current file as release notes", - "developer show" - ] - }, - { - "id": "workbench.action.download", - "title": "workbench.action.download", - "keywords": [ - "download" - ] - }, - { - "id": "update.checkForUpdate", - "title": "update.checkForUpdate", - "keywords": [ - "update check for update", - "update check" - ] - }, - { - "id": "update.downloadUpdate", - "title": "update.downloadUpdate", - "keywords": [ - "update download update", - "update download" - ] - }, - { - "id": "update.installUpdate", - "title": "update.installUpdate", - "keywords": [ - "update install update", - "update install" - ] - }, - { - "id": "update.restartToUpdate", - "title": "update.restartToUpdate", - "keywords": [ - "update restart to update", - "update restart" - ] - }, - { - "id": "update.showUpdateInfo", - "title": "update.showUpdateInfo", - "keywords": [ - "update show update info", - "update show" - ] - }, - { - "id": "workbench.action.openWalkthrough", - "title": "workbench.action.openWalkthrough", - "keywords": [ - "open walkthrough" - ] - }, - { - "id": "welcome.goBack", - "title": "welcome.goBack", - "keywords": [ - "welcome go back", - "welcome go" - ] - }, - { - "id": "walkthroughs.selectStep", - "title": "walkthroughs.selectStep", - "keywords": [ - "walkthroughs select step", - "walkthroughs select" - ] - }, - { - "id": "welcome.markStepComplete", - "title": "welcome.markStepComplete", - "keywords": [ - "welcome mark step complete", - "welcome mark" - ] - }, - { - "id": "welcome.markStepIncomplete", - "title": "welcome.markStepIncomplete", - "keywords": [ - "welcome mark step incomplete", - "welcome mark" - ] - }, - { - "id": "welcome.showAllWalkthroughs", - "title": "welcome.showAllWalkthroughs", - "keywords": [ - "welcome show all walkthroughs", - "welcome show" - ] - }, - { - "id": "welcome.newWorkspaceChat", - "title": "welcome.newWorkspaceChat", - "keywords": [ - "welcome new workspace chat", - "welcome new" - ] - }, - { - "id": "workbench.action.openAgentSessionsWelcome", - "title": "workbench.action.openAgentSessionsWelcome", - "keywords": [ - "open agent sessions welcome", - "open agent" - ] - }, - { - "id": "workbench.action.showInteractivePlayground", - "title": "workbench.action.showInteractivePlayground", - "keywords": [ - "show interactive playground", - "show interactive" - ] - }, - { - "id": "workbench.action.interactivePlayground.arrowUp", - "title": "workbench.action.interactivePlayground.arrowUp", - "keywords": [ - "interactive playground arrow up", - "interactive playground" - ] - }, - { - "id": "workbench.action.interactivePlayground.arrowDown", - "title": "workbench.action.interactivePlayground.arrowDown", - "keywords": [ - "interactive playground arrow down", - "interactive playground" - ] - }, - { - "id": "workbench.action.interactivePlayground.pageUp", - "title": "workbench.action.interactivePlayground.pageUp", - "keywords": [ - "interactive playground page up", - "interactive playground" - ] - }, - { - "id": "workbench.action.interactivePlayground.pageDown", - "title": "workbench.action.interactivePlayground.pageDown", - "keywords": [ - "interactive playground page down", - "interactive playground" - ] - }, - { - "id": "welcome.showNewFileEntries", - "title": "welcome.showNewFileEntries", - "keywords": [ - "welcome show new file entries", - "welcome show" - ] - }, - { - "id": "workbench.action.welcomeOnboarding2026", - "title": "workbench.action.welcomeOnboarding2026", - "keywords": [ - "welcome onboarding2026" - ] - }, - { - "id": "editor.showCallHierarchy", - "title": "editor.showCallHierarchy", - "keywords": [ - "editor show call hierarchy", - "editor show" - ] - }, - { - "id": "editor.showIncomingCalls", - "title": "editor.showIncomingCalls", - "keywords": [ - "editor show incoming calls", - "editor show" - ] - }, - { - "id": "editor.showOutgoingCalls", - "title": "editor.showOutgoingCalls", - "keywords": [ - "editor show outgoing calls", - "editor show" - ] - }, - { - "id": "editor.refocusCallHierarchy", - "title": "editor.refocusCallHierarchy", - "keywords": [ - "editor refocus call hierarchy", - "editor refocus" - ] - }, - { - "id": "editor.closeCallHierarchy", - "title": "editor.closeCallHierarchy", - "keywords": [ - "editor close call hierarchy", - "editor close" - ] - }, - { - "id": "editor.showTypeHierarchy", - "title": "editor.showTypeHierarchy", - "keywords": [ - "editor show type hierarchy", - "editor show" - ] - }, - { - "id": "editor.showSupertypes", - "title": "editor.showSupertypes", - "keywords": [ - "editor show supertypes", - "editor show" - ] - }, - { - "id": "editor.showSubtypes", - "title": "editor.showSubtypes", - "keywords": [ - "editor show subtypes", - "editor show" - ] - }, - { - "id": "editor.refocusTypeHierarchy", - "title": "editor.refocusTypeHierarchy", - "keywords": [ - "editor refocus type hierarchy", - "editor refocus" - ] - }, - { - "id": "editor.closeTypeHierarchy", - "title": "editor.closeTypeHierarchy", - "keywords": [ - "editor close type hierarchy", - "editor close" - ] - }, - { - "id": "outline.collapse", - "title": "outline.collapse", - "keywords": [ - "outline collapse" - ] - }, - { - "id": "outline.expand", - "title": "outline.expand", - "keywords": [ - "outline expand" - ] - }, - { - "id": "outline.followCursor", - "title": "outline.followCursor", - "keywords": [ - "outline follow cursor", - "outline follow" - ] - }, - { - "id": "outline.filterOnType", - "title": "outline.filterOnType", - "keywords": [ - "outline filter on type", - "outline filter" - ] - }, - { - "id": "outline.sortByPosition", - "title": "outline.sortByPosition", - "keywords": [ - "outline sort by position", - "outline sort" - ] - }, - { - "id": "outline.sortByName", - "title": "outline.sortByName", - "keywords": [ - "outline sort by name", - "outline sort" - ] - }, - { - "id": "outline.sortByKind", - "title": "outline.sortByKind", - "keywords": [ - "outline sort by kind", - "outline sort" - ] - }, - { - "id": "editor.detectLanguage", - "title": "editor.detectLanguage", - "keywords": [ - "editor detect language", - "editor detect" - ] - }, - { - "id": "editor.inlayHints.Reset", - "title": "editor.inlayHints.Reset", - "keywords": [ - "editor inlay hints reset", - "editor inlay" - ] - }, - { - "id": "workbench.getCodeExchangeProxyEndpoints", - "title": "workbench.getCodeExchangeProxyEndpoints", - "keywords": [ - "workbench get code exchange proxy endpoints", - "workbench get" - ] - }, - { - "id": "workbench.profiles.actions.createTemporaryProfile", - "title": "workbench.profiles.actions.createTemporaryProfile", - "keywords": [ - "workbench profiles actions create temporary profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.cleanupProfiles", - "title": "workbench.profiles.actions.cleanupProfiles", - "keywords": [ - "workbench profiles actions cleanup profiles", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.resetWorkspaces", - "title": "workbench.profiles.actions.resetWorkspaces", - "keywords": [ - "workbench profiles actions reset workspaces", - "workbench profiles" - ] - }, - { - "id": "workbench.action.continueOn.extensions", - "title": "workbench.action.continueOn.extensions", - "keywords": [ - "continue on extensions", - "continue on" - ] - }, - { - "id": "files.openTimeline", - "title": "files.openTimeline", - "keywords": [ - "files open timeline", - "files open" - ] - }, - { - "id": "workbench.action.localHistory.compareWithFile", - "title": "workbench.action.localHistory.compareWithFile", - "keywords": [ - "local history compare with file", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.compareWithPrevious", - "title": "workbench.action.localHistory.compareWithPrevious", - "keywords": [ - "local history compare with previous", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.selectForCompare", - "title": "workbench.action.localHistory.selectForCompare", - "keywords": [ - "local history select for compare", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.compareWithSelected", - "title": "workbench.action.localHistory.compareWithSelected", - "keywords": [ - "local history compare with selected", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.open", - "title": "workbench.action.localHistory.open", - "keywords": [ - "local history open", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.restoreViaEditor", - "title": "workbench.action.localHistory.restoreViaEditor", - "keywords": [ - "local history restore via editor", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.restore", - "title": "workbench.action.localHistory.restore", - "keywords": [ - "local history restore", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.restoreViaPicker", - "title": "workbench.action.localHistory.restoreViaPicker", - "keywords": [ - "local history restore via picker", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.rename", - "title": "workbench.action.localHistory.rename", - "keywords": [ - "local history rename", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.delete", - "title": "workbench.action.localHistory.delete", - "keywords": [ - "local history delete", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.deleteAll", - "title": "workbench.action.localHistory.deleteAll", - "keywords": [ - "local history delete all", - "local history" - ] - }, - { - "id": "workbench.action.localHistory.create", - "title": "workbench.action.localHistory.create", - "keywords": [ - "local history create", - "local history" - ] - }, - { - "id": "workbench.trust.configure", - "title": "workbench.trust.configure", - "keywords": [ - "workbench trust configure", - "workbench trust" - ] - }, - { - "id": "workbench.trust.manage", - "title": "workbench.trust.manage", - "keywords": [ - "workbench trust manage", - "workbench trust" - ] - }, - { - "id": "workbench.action.openWorkspaceFromEditor", - "title": "workbench.action.openWorkspaceFromEditor", - "keywords": [ - "open workspace from editor", - "open workspace" - ] - }, - { - "id": "list.resizeColumn", - "title": "list.resizeColumn", - "keywords": [ - "list resize column", - "list resize" - ] - }, - { - "id": "signals.sounds.help", - "title": "signals.sounds.help", - "keywords": [ - "signals sounds help", - "signals sounds" - ] - }, - { - "id": "accessibility.announcement.help", - "title": "accessibility.announcement.help", - "keywords": [ - "accessibility announcement help", - "accessibility announcement" - ] - }, - { - "id": "workbench.action.configureMeteredConnection", - "title": "workbench.action.configureMeteredConnection", - "keywords": [ - "configure metered connection", - "configure metered" - ] - }, - { - "id": "toggle.workbench.experimental.share.enabled", - "title": "toggle.workbench.experimental.share.enabled", - "keywords": [ - "toggle workbench experimental share enabled", - "toggle workbench" - ] - }, - { - "id": "workbench.action.setNotificationsPosition.bottomRight", - "title": "workbench.action.setNotificationsPosition.bottomRight", - "keywords": [ - "set notifications position bottom right", - "set notifications" - ] - }, - { - "id": "workbench.action.setNotificationsPosition.bottomLeft", - "title": "workbench.action.setNotificationsPosition.bottomLeft", - "keywords": [ - "set notifications position bottom left", - "set notifications" - ] - }, - { - "id": "workbench.action.setNotificationsPosition.topRight", - "title": "workbench.action.setNotificationsPosition.topRight", - "keywords": [ - "set notifications position top right", - "set notifications" - ] - }, - { - "id": "workbench.action.zoomIn", - "title": "workbench.action.zoomIn", - "keywords": [ - "zoom in" - ] - }, - { - "id": "workbench.action.zoomOut", - "title": "workbench.action.zoomOut", - "keywords": [ - "zoom out" - ] - }, - { - "id": "workbench.action.zoomReset", - "title": "workbench.action.zoomReset", - "keywords": [ - "zoom reset" - ] - }, - { - "id": "workbench.action.switchWindow", - "title": "workbench.action.switchWindow", - "keywords": [ - "switch window" - ] - }, - { - "id": "workbench.action.quickSwitchWindow", - "title": "workbench.action.quickSwitchWindow", - "keywords": [ - "quick switch window", - "quick switch" - ] - }, - { - "id": "workbench.action.switchToMainWindow", - "title": "workbench.action.switchToMainWindow", - "keywords": [ - "switch to main window", - "switch to" - ] - }, - { - "id": "workbench.action.closeWindow", - "title": "workbench.action.closeWindow", - "keywords": [ - "close window" - ] - }, - { - "id": "workbench.action.closeOtherWindows", - "title": "workbench.action.closeOtherWindows", - "keywords": [ - "close other windows", - "close other" - ] - }, - { - "id": "workbench.action.toggleWindowAlwaysOnTop", - "title": "workbench.action.toggleWindowAlwaysOnTop", - "keywords": [ - "toggle window always on top", - "toggle window" - ] - }, - { - "id": "workbench.action.enableWindowAlwaysOnTop", - "title": "workbench.action.enableWindowAlwaysOnTop", - "keywords": [ - "enable window always on top", - "enable window" - ] - }, - { - "id": "workbench.action.disableWindowAlwaysOnTop", - "title": "workbench.action.disableWindowAlwaysOnTop", - "keywords": [ - "disable window always on top", - "disable window" - ] - }, - { - "id": "workbench.action.quit", - "title": "workbench.action.quit", - "keywords": [ - "quit" - ] - }, - { - "id": "workbench.action.reloadWindowWithExtensionsDisabled", - "title": "workbench.action.reloadWindowWithExtensionsDisabled", - "keywords": [ - "reload window with extensions disabled", - "reload window" - ] - }, - { - "id": "workbench.action.configureRuntimeArguments", - "title": "workbench.action.configureRuntimeArguments", - "keywords": [ - "configure runtime arguments", - "configure runtime" - ] - }, - { - "id": "workbench.action.toggleDevTools", - "title": "workbench.action.toggleDevTools", - "keywords": [ - "toggle dev tools", - "toggle dev" - ] - }, - { - "id": "workbench.action.revealUserDataFolder", - "title": "workbench.action.revealUserDataFolder", - "keywords": [ - "reveal user data folder", - "reveal user" - ] - }, - { - "id": "workbench.action.showGPUInfo", - "title": "workbench.action.showGPUInfo", - "keywords": [ - "show gpuinfo" - ] - }, - { - "id": "workbench.action.showContentTracing", - "title": "workbench.action.showContentTracing", - "keywords": [ - "show content tracing", - "show content" - ] - }, - { - "id": "workbench.action.stopTracing", - "title": "workbench.action.stopTracing", - "keywords": [ - "stop tracing" - ] - }, - { - "id": "workbench.action.startTracing", - "title": "workbench.action.startTracing", - "keywords": [ - "start tracing" - ] - }, - { - "id": "editor.action.measureExtHostLatency", - "title": "editor.action.measureExtHostLatency", - "keywords": [ - "measure ext host latency", - "measure ext" - ] - }, - { - "id": "workbench.action.restartExtensionHost", - "title": "workbench.action.restartExtensionHost", - "keywords": [ - "restart extension host", - "restart extension" - ] - }, - { - "id": "workbench.action.openLogsFolder", - "title": "workbench.action.openLogsFolder", - "keywords": [ - "open logs folder", - "open logs" - ] - }, - { - "id": "workbench.action.openExtensionLogsFolder", - "title": "workbench.action.openExtensionLogsFolder", - "keywords": [ - "open extension logs folder", - "open extension" - ] - }, - { - "id": "revealFileInOS", - "title": "revealFileInOS", - "keywords": [ - "reveal file in os", - "reveal file" - ] - }, - { - "id": "workbench.action.files.revealActiveFileInWindows", - "title": "workbench.action.files.revealActiveFileInWindows", - "keywords": [ - "files reveal active file in windows", - "files reveal" - ] - }, - { - "id": "editor.action.startDebugTextMate", - "title": "editor.action.startDebugTextMate", - "keywords": [ - "start debug text mate", - "start debug" - ] - }, - { - "id": "workbench.extensions.action.debugExtensionHost", - "title": "workbench.extensions.action.debugExtensionHost", - "keywords": [ - "workbench extensions action debug extension host", - "workbench extensions" - ] - }, - { - "id": "workbench.action.debugRenderer", - "title": "workbench.action.debugRenderer", - "keywords": [ - "debug renderer" - ] - }, - { - "id": "workbench.action.debugExtensionHostAndRenderer", - "title": "workbench.action.debugExtensionHostAndRenderer", - "keywords": [ - "debug extension host and renderer", - "debug extension" - ] - }, - { - "id": "workbench.extensions.action.extensionHostProfile", - "title": "workbench.extensions.action.extensionHostProfile", - "keywords": [ - "workbench extensions action extension host profile", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.stopExtensionHostProfile", - "title": "workbench.extensions.action.stopExtensionHostProfile", - "keywords": [ - "workbench extensions action stop extension host profile", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.saveExtensionHostProfile", - "title": "workbench.extensions.action.saveExtensionHostProfile", - "keywords": [ - "workbench extensions action save extension host profile", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.openExtensionHostProfile", - "title": "workbench.extensions.action.openExtensionHostProfile", - "keywords": [ - "workbench extensions action open extension host profile", - "workbench extensions" - ] - }, - { - "id": "workbench.action.showRuntimeExtensions", - "title": "workbench.action.showRuntimeExtensions", - "keywords": [ - "show runtime extensions", - "show runtime" - ] - }, - { - "id": "workbench.extensions.action.devtoolsExtensionHost", - "title": "workbench.extensions.action.devtoolsExtensionHost", - "keywords": [ - "workbench extensions action devtools extension host", - "workbench extensions" - ] - }, - { - "id": "workbench.action.troubleshootIssue.start", - "title": "workbench.action.troubleshootIssue.start", - "keywords": [ - "troubleshoot issue start", - "troubleshoot issue" - ] - }, - { - "id": "workbench.action.troubleshootIssue.stop", - "title": "workbench.action.troubleshootIssue.stop", - "keywords": [ - "troubleshoot issue stop", - "troubleshoot issue" - ] - }, - { - "id": "workbench.action.issueReporter.captureScreenshot", - "title": "workbench.action.issueReporter.captureScreenshot", - "keywords": [ - "issue reporter capture screenshot", - "issue reporter" - ] - }, - { - "id": "workbench.action.issueReporter.toggleRecording", - "title": "workbench.action.issueReporter.toggleRecording", - "keywords": [ - "issue reporter toggle recording", - "issue reporter" - ] - }, - { - "id": "workbench.action.files.openLocalFile", - "title": "workbench.action.files.openLocalFile", - "keywords": [ - "files open local file", - "files open" - ] - }, - { - "id": "workbench.action.files.openLocalFolder", - "title": "workbench.action.files.openLocalFolder", - "keywords": [ - "files open local folder", - "files open" - ] - }, - { - "id": "workbench.action.files.saveLocalFile", - "title": "workbench.action.files.saveLocalFile", - "keywords": [ - "files save local file", - "files save" - ] - }, - { - "id": "workbench.remote.action.closeUnusedPorts", - "title": "workbench.remote.action.closeUnusedPorts", - "keywords": [ - "workbench remote action close unused ports", - "workbench remote" - ] - }, - { - "id": "workbench.userData.actions.openSyncBackupsFolder", - "title": "workbench.userData.actions.openSyncBackupsFolder", - "keywords": [ - "workbench user data actions open sync backups folder", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.downloadSyncActivity", - "title": "workbench.userDataSync.actions.downloadSyncActivity", - "keywords": [ - "workbench user data sync actions download sync activity", - "workbench user" - ] - }, - { - "id": "workbench.action.terminal.openNativeConsole", - "title": "workbench.action.terminal.openNativeConsole", - "keywords": [ - "terminal open native console", - "terminal open" - ] - }, - { - "id": "workbench.action.webview.openDeveloperTools", - "title": "workbench.action.webview.openDeveloperTools", - "keywords": [ - "webview open developer tools", - "webview open" - ] - }, - { - "id": "workbench.action.browser.goBack", - "title": "workbench.action.browser.goBack", - "keywords": [ - "browser go back", - "browser go" - ] - }, - { - "id": "workbench.action.browser.goForward", - "title": "workbench.action.browser.goForward", - "keywords": [ - "browser go forward", - "browser go" - ] - }, - { - "id": "workbench.action.browser.reload", - "title": "workbench.action.browser.reload", - "keywords": [ - "browser reload" - ] - }, - { - "id": "workbench.action.browser.hardReload", - "title": "workbench.action.browser.hardReload", - "keywords": [ - "browser hard reload", - "browser hard" - ] - }, - { - "id": "workbench.action.browser.focusUrlInput", - "title": "workbench.action.browser.focusUrlInput", - "keywords": [ - "browser focus url input", - "browser focus" - ] - }, - { - "id": "workbench.action.browser.openExternal", - "title": "workbench.action.browser.openExternal", - "keywords": [ - "browser open external", - "browser open" - ] - }, - { - "id": "workbench.action.browser.openSettings", - "title": "workbench.action.browser.openSettings", - "keywords": [ - "browser open settings", - "browser open" - ] - }, - { - "id": "workbench.action.browser.clearGlobalStorage", - "title": "workbench.action.browser.clearGlobalStorage", - "keywords": [ - "browser clear global storage", - "browser clear" - ] - }, - { - "id": "workbench.action.browser.clearWorkspaceStorage", - "title": "workbench.action.browser.clearWorkspaceStorage", - "keywords": [ - "browser clear workspace storage", - "browser clear" - ] - }, - { - "id": "workbench.action.browser.clearEphemeralStorage", - "title": "workbench.action.browser.clearEphemeralStorage", - "keywords": [ - "browser clear ephemeral storage", - "browser clear" - ] - }, - { - "id": "workbench.action.browser.toggleDevTools", - "title": "workbench.action.browser.toggleDevTools", - "keywords": [ - "browser toggle dev tools", - "browser toggle" - ] - }, - { - "id": "workbench.action.browser.addElementToChat", - "title": "workbench.action.browser.addElementToChat", - "keywords": [ - "browser add element to chat", - "browser add" - ] - }, - { - "id": "workbench.action.browser.addConsoleLogsToChat", - "title": "workbench.action.browser.addConsoleLogsToChat", - "keywords": [ - "browser add console logs to chat", - "browser add" - ] - }, - { - "id": "workbench.action.browser.addScreenshotToChat", - "title": "workbench.action.browser.addScreenshotToChat", - "keywords": [ - "browser add screenshot to chat", - "browser add" - ] - }, - { - "id": "workbench.action.browser.zoomIn", - "title": "workbench.action.browser.zoomIn", - "keywords": [ - "browser zoom in", - "browser zoom" - ] - }, - { - "id": "workbench.action.browser.zoomOut", - "title": "workbench.action.browser.zoomOut", - "keywords": [ - "browser zoom out", - "browser zoom" - ] - }, - { - "id": "workbench.action.browser.resetZoom", - "title": "workbench.action.browser.resetZoom", - "keywords": [ - "browser reset zoom", - "browser reset" - ] - }, - { - "id": "workbench.action.browser.showEmulationToolbar", - "title": "workbench.action.browser.showEmulationToolbar", - "keywords": [ - "browser show emulation toolbar", - "browser show" - ] - }, - { - "id": "workbench.action.browser.hideEmulationToolbar", - "title": "workbench.action.browser.hideEmulationToolbar", - "keywords": [ - "browser hide emulation toolbar", - "browser hide" - ] - }, - { - "id": "workbench.action.browser.pickDevicePreset", - "title": "workbench.action.browser.pickDevicePreset", - "keywords": [ - "browser pick device preset", - "browser pick" - ] - }, - { - "id": "workbench.action.browser.setUserAgent", - "title": "workbench.action.browser.setUserAgent", - "keywords": [ - "browser set user agent", - "browser set" - ] - }, - { - "id": "workbench.action.browser.toggleMobileEmulation", - "title": "workbench.action.browser.toggleMobileEmulation", - "keywords": [ - "browser toggle mobile emulation", - "browser toggle" - ] - }, - { - "id": "workbench.action.browser.resetEmulation", - "title": "workbench.action.browser.resetEmulation", - "keywords": [ - "browser reset emulation", - "browser reset" - ] - }, - { - "id": "workbench.action.browser.showFind", - "title": "workbench.action.browser.showFind", - "keywords": [ - "browser show find", - "browser show" - ] - }, - { - "id": "workbench.action.browser.hideFind", - "title": "workbench.action.browser.hideFind", - "keywords": [ - "browser hide find", - "browser hide" - ] - }, - { - "id": "workbench.action.browser.findNext", - "title": "workbench.action.browser.findNext", - "keywords": [ - "browser find next", - "browser find" - ] - }, - { - "id": "workbench.action.browser.findPrevious", - "title": "workbench.action.browser.findPrevious", - "keywords": [ - "browser find previous", - "browser find" - ] - }, - { - "id": "workbench.action.browser.quickOpen", - "title": "workbench.action.browser.quickOpen", - "keywords": [ - "browser quick open", - "browser quick" - ] - }, - { - "id": "workbench.action.browser.open", - "title": "workbench.action.browser.open", - "keywords": [ - "browser open" - ] - }, - { - "id": "workbench.action.browser.openFile", - "title": "workbench.action.browser.openFile", - "keywords": [ - "browser open file", - "browser open" - ] - }, - { - "id": "workbench.action.browser.openOrList", - "title": "workbench.action.browser.openOrList", - "keywords": [ - "browser open or list", - "browser open" - ] - }, - { - "id": "workbench.action.browser.newTab", - "title": "workbench.action.browser.newTab", - "keywords": [ - "browser new tab", - "browser new" - ] - }, - { - "id": "workbench.action.browser.closeAll", - "title": "workbench.action.browser.closeAll", - "keywords": [ - "browser close all", - "browser close" - ] - }, - { - "id": "workbench.action.browser.closeAllInGroup", - "title": "workbench.action.browser.closeAllInGroup", - "keywords": [ - "browser close all in group", - "browser close" - ] - }, - { - "id": "toggle.workbench.browser.showInTitleBar", - "title": "toggle.workbench.browser.showInTitleBar", - "keywords": [ - "toggle workbench browser show in title bar", - "toggle workbench" - ] - }, - { - "id": "workbench.action.localHistory.revealInOS", - "title": "workbench.action.localHistory.revealInOS", - "keywords": [ - "local history reveal in os", - "local history" - ] - }, - { - "id": "merge.dev.openContentsJson", - "title": "merge.dev.openContentsJson", - "keywords": [ - "merge dev open contents json", - "merge dev" - ] - }, - { - "id": "merge.dev.openSelectionInTemporaryMergeEditor", - "title": "merge.dev.openSelectionInTemporaryMergeEditor", - "keywords": [ - "merge dev open selection in temporary merge editor", - "merge dev" - ] - }, - { - "id": "workbench.action.openWorkspaceInAgentsWindow", - "title": "workbench.action.openWorkspaceInAgentsWindow", - "keywords": [ - "open workspace in agents window", - "open workspace" - ] - }, - { - "id": "workbench.action.openAgentsWindow", - "title": "workbench.action.openAgentsWindow", - "keywords": [ - "open agents window", - "open agents" - ] - }, - { - "id": "workbench.action.chat.startVoiceChat", - "title": "workbench.action.chat.startVoiceChat", - "keywords": [ - "chat start voice chat", - "chat start" - ] - }, - { - "id": "workbench.action.chat.voiceChatInChatView", - "title": "workbench.action.chat.voiceChatInChatView", - "keywords": [ - "chat voice chat in chat view", - "chat voice" - ] - }, - { - "id": "workbench.action.chat.holdToVoiceChatInChatView", - "title": "workbench.action.chat.holdToVoiceChatInChatView", - "keywords": [ - "chat hold to voice chat in chat view", - "chat hold" - ] - }, - { - "id": "workbench.action.chat.quickVoiceChat", - "title": "workbench.action.chat.quickVoiceChat", - "keywords": [ - "chat quick voice chat", - "chat quick" - ] - }, - { - "id": "workbench.action.chat.inlineVoiceChat", - "title": "workbench.action.chat.inlineVoiceChat", - "keywords": [ - "chat inline voice chat", - "chat inline" - ] - }, - { - "id": "workbench.action.chat.stopListening", - "title": "workbench.action.chat.stopListening", - "keywords": [ - "chat stop listening", - "chat stop" - ] - }, - { - "id": "workbench.action.chat.stopListeningAndSubmit", - "title": "workbench.action.chat.stopListeningAndSubmit", - "keywords": [ - "chat stop listening and submit", - "chat stop" - ] - }, - { - "id": "workbench.action.chat.readChatResponseAloud", - "title": "workbench.action.chat.readChatResponseAloud", - "keywords": [ - "chat read chat response aloud", - "chat read" - ] - }, - { - "id": "workbench.action.chat.stopReadChatItemAloud", - "title": "workbench.action.chat.stopReadChatItemAloud", - "keywords": [ - "chat stop read chat item aloud", - "chat stop" - ] - }, - { - "id": "workbench.action.speech.stopReadAloud", - "title": "workbench.action.speech.stopReadAloud", - "keywords": [ - "speech stop read aloud", - "speech stop" - ] - }, - { - "id": "workbench.action.chat.openStorageFolder", - "title": "workbench.action.chat.openStorageFolder", - "keywords": [ - "chat open storage folder", - "chat open" - ] - }, - { - "id": "workbench.action.chat.debugAgentHostInDevTools", - "title": "workbench.action.chat.debugAgentHostInDevTools", - "keywords": [ - "chat debug agent host in dev tools", - "chat debug" - ] - }, - { - "id": "workbench.action.chat.exportAsZip", - "title": "workbench.action.chat.exportAsZip", - "keywords": [ - "chat export as zip", - "chat export" - ] - }, - { - "id": "workbench.action.chat.agentHost.otel.exportAgentTracesDB", - "title": "workbench.action.chat.agentHost.otel.exportAgentTracesDB", - "keywords": [ - "chat agent host otel export agent traces db", - "chat agent" - ] - }, - { - "id": "workbench.action.chat.openNewSessionSidebar.agent-host-copilotcli", - "title": "workbench.action.chat.openNewSessionSidebar.agent-host-copilotcli", - "keywords": [ - "chat open new session sidebar agent host copilotcli", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionEditor.agent-host-copilotcli", - "title": "workbench.action.chat.openNewSessionEditor.agent-host-copilotcli", - "keywords": [ - "chat open new session editor agent host copilotcli", - "chat open" - ] - }, - { - "id": "workbench.panel.chat.resetViewContainerLocation", - "title": "workbench.panel.chat.resetViewContainerLocation", - "keywords": [ - "workbench panel chat reset view container location", - "workbench panel" - ] - }, - { - "id": "workbench.view.extensions.resetViewContainerLocation", - "title": "workbench.view.extensions.resetViewContainerLocation", - "keywords": [ - "workbench view extensions reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.test.resetViewContainerLocation", - "title": "workbench.view.extension.test.resetViewContainerLocation", - "keywords": [ - "workbench view extension test reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.explorer.resetViewContainerLocation", - "title": "workbench.view.explorer.resetViewContainerLocation", - "keywords": [ - "workbench view explorer reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.search.resetViewContainerLocation", - "title": "workbench.view.search.resetViewContainerLocation", - "keywords": [ - "workbench view search reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.scm.resetViewContainerLocation", - "title": "workbench.view.scm.resetViewContainerLocation", - "keywords": [ - "workbench view scm reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.debug.resetViewContainerLocation", - "title": "workbench.view.debug.resetViewContainerLocation", - "keywords": [ - "workbench view debug reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.remote.resetViewContainerLocation", - "title": "workbench.view.remote.resetViewContainerLocation", - "keywords": [ - "workbench view remote reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.panel.testResults.resetViewContainerLocation", - "title": "workbench.panel.testResults.resetViewContainerLocation", - "keywords": [ - "workbench panel test results reset view container location", - "workbench panel" - ] - }, - { - "id": "refactorPreview.resetViewContainerLocation", - "title": "refactorPreview.resetViewContainerLocation", - "keywords": [ - "refactor preview reset view container location", - "refactor preview" - ] - }, - { - "id": "workbench.panel.repl.resetViewContainerLocation", - "title": "workbench.panel.repl.resetViewContainerLocation", - "keywords": [ - "workbench panel repl reset view container location", - "workbench panel" - ] - }, - { - "id": "workbench.panel.markers.resetViewContainerLocation", - "title": "workbench.panel.markers.resetViewContainerLocation", - "keywords": [ - "workbench panel markers reset view container location", - "workbench panel" - ] - }, - { - "id": "workbench.panel.output.resetViewContainerLocation", - "title": "workbench.panel.output.resetViewContainerLocation", - "keywords": [ - "workbench panel output reset view container location", - "workbench panel" - ] - }, - { - "id": "terminal.resetViewContainerLocation", - "title": "terminal.resetViewContainerLocation", - "keywords": [ - "terminal reset view container location", - "terminal reset" - ] - }, - { - "id": "workbench.action.toggleActivityBarVisibility", - "title": "workbench.action.toggleActivityBarVisibility", - "keywords": [ - "toggle activity bar visibility", - "toggle activity" - ] - }, - { - "id": "workbench.panel.chat", - "title": "workbench.panel.chat", - "keywords": [ - "workbench panel chat", - "workbench panel" - ] - }, - { - "id": "workbench.panel.chat.view.copilot.focus", - "title": "workbench.panel.chat.view.copilot.focus", - "keywords": [ - "workbench panel chat view copilot focus", - "workbench panel" - ] - }, - { - "id": "workbench.panel.chat.view.copilot.resetViewLocation", - "title": "workbench.panel.chat.view.copilot.resetViewLocation", - "keywords": [ - "workbench panel chat view copilot reset view location", - "workbench panel" - ] - }, - { - "id": "workbench.view.extensions", - "title": "workbench.view.extensions", - "keywords": [ - "workbench view extensions", - "workbench view" - ] - }, - { - "id": "workbench.view.testing.open", - "title": "workbench.view.testing.open", - "keywords": [ - "workbench view testing open", - "workbench view" - ] - }, - { - "id": "workbench.view.testing.focus", - "title": "workbench.view.testing.focus", - "keywords": [ - "workbench view testing focus", - "workbench view" - ] - }, - { - "id": "workbench.view.testing.resetViewLocation", - "title": "workbench.view.testing.resetViewLocation", - "keywords": [ - "workbench view testing reset view location", - "workbench view" - ] - }, - { - "id": "workbench.view.testCoverage.open", - "title": "workbench.view.testCoverage.open", - "keywords": [ - "workbench view test coverage open", - "workbench view" - ] - }, - { - "id": "workbench.view.testCoverage.focus", - "title": "workbench.view.testCoverage.focus", - "keywords": [ - "workbench view test coverage focus", - "workbench view" - ] - }, - { - "id": "workbench.view.testCoverage.resetViewLocation", - "title": "workbench.view.testCoverage.resetViewLocation", - "keywords": [ - "workbench view test coverage reset view location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.test", - "title": "workbench.view.extension.test", - "keywords": [ - "workbench view extension test", - "workbench view" - ] - }, - { - "id": "outline.open", - "title": "outline.open", - "keywords": [ - "outline open" - ] - }, - { - "id": "outline.focus", - "title": "outline.focus", - "keywords": [ - "outline focus" - ] - }, - { - "id": "outline.resetViewLocation", - "title": "outline.resetViewLocation", - "keywords": [ - "outline reset view location", - "outline reset" - ] - }, - { - "id": "timeline.open", - "title": "timeline.open", - "keywords": [ - "timeline open" - ] - }, - { - "id": "timeline.focus", - "title": "timeline.focus", - "keywords": [ - "timeline focus" - ] - }, - { - "id": "timeline.resetViewLocation", - "title": "timeline.resetViewLocation", - "keywords": [ - "timeline reset view location", - "timeline reset" - ] - }, - { - "id": "workbench.view.explorer", - "title": "workbench.view.explorer", - "keywords": [ - "workbench view explorer", - "workbench view" - ] - }, - { - "id": "workbench.view.search", - "title": "workbench.view.search", - "keywords": [ - "workbench view search", - "workbench view" - ] - }, - { - "id": "workbench.view.search.focus", - "title": "workbench.view.search.focus", - "keywords": [ - "workbench view search focus", - "workbench view" - ] - }, - { - "id": "workbench.view.search.resetViewLocation", - "title": "workbench.view.search.resetViewLocation", - "keywords": [ - "workbench view search reset view location", - "workbench view" - ] - }, - { - "id": "workbench.scm.repositories.open", - "title": "workbench.scm.repositories.open", - "keywords": [ - "workbench scm repositories open", - "workbench scm" - ] - }, - { - "id": "workbench.scm.repositories.focus", - "title": "workbench.scm.repositories.focus", - "keywords": [ - "workbench scm repositories focus", - "workbench scm" - ] - }, - { - "id": "workbench.scm.repositories.resetViewLocation", - "title": "workbench.scm.repositories.resetViewLocation", - "keywords": [ - "workbench scm repositories reset view location", - "workbench scm" - ] - }, - { - "id": "workbench.view.scm", - "title": "workbench.view.scm", - "keywords": [ - "workbench view scm", - "workbench view" - ] - }, - { - "id": "workbench.scm.focus", - "title": "workbench.scm.focus", - "keywords": [ - "workbench scm focus", - "workbench scm" - ] - }, - { - "id": "workbench.scm.resetViewLocation", - "title": "workbench.scm.resetViewLocation", - "keywords": [ - "workbench scm reset view location", - "workbench scm" - ] - }, - { - "id": "workbench.scm.history.open", - "title": "workbench.scm.history.open", - "keywords": [ - "workbench scm history open", - "workbench scm" - ] - }, - { - "id": "workbench.scm.history.focus", - "title": "workbench.scm.history.focus", - "keywords": [ - "workbench scm history focus", - "workbench scm" - ] - }, - { - "id": "workbench.scm.history.resetViewLocation", - "title": "workbench.scm.history.resetViewLocation", - "keywords": [ - "workbench scm history reset view location", - "workbench scm" - ] - }, - { - "id": "workbench.debug.welcome.open", - "title": "workbench.debug.welcome.open", - "keywords": [ - "workbench debug welcome open", - "workbench debug" - ] - }, - { - "id": "workbench.debug.welcome.focus", - "title": "workbench.debug.welcome.focus", - "keywords": [ - "workbench debug welcome focus", - "workbench debug" - ] - }, - { - "id": "workbench.debug.welcome.resetViewLocation", - "title": "workbench.debug.welcome.resetViewLocation", - "keywords": [ - "workbench debug welcome reset view location", - "workbench debug" - ] - }, - { - "id": "workbench.debug.variablesView.open", - "title": "workbench.debug.variablesView.open", - "keywords": [ - "workbench debug variables view open", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.focusVariablesView", - "title": "workbench.debug.action.focusVariablesView", - "keywords": [ - "workbench debug action focus variables view", - "workbench debug" - ] - }, - { - "id": "workbench.debug.variablesView.resetViewLocation", - "title": "workbench.debug.variablesView.resetViewLocation", - "keywords": [ - "workbench debug variables view reset view location", - "workbench debug" - ] - }, - { - "id": "workbench.debug.watchExpressionsView.open", - "title": "workbench.debug.watchExpressionsView.open", - "keywords": [ - "workbench debug watch expressions view open", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.focusWatchView", - "title": "workbench.debug.action.focusWatchView", - "keywords": [ - "workbench debug action focus watch view", - "workbench debug" - ] - }, - { - "id": "workbench.debug.watchExpressionsView.resetViewLocation", - "title": "workbench.debug.watchExpressionsView.resetViewLocation", - "keywords": [ - "workbench debug watch expressions view reset view location", - "workbench debug" - ] - }, - { - "id": "workbench.debug.callStackView.open", - "title": "workbench.debug.callStackView.open", - "keywords": [ - "workbench debug call stack view open", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.focusCallStackView", - "title": "workbench.debug.action.focusCallStackView", - "keywords": [ - "workbench debug action focus call stack view", - "workbench debug" - ] - }, - { - "id": "workbench.debug.callStackView.resetViewLocation", - "title": "workbench.debug.callStackView.resetViewLocation", - "keywords": [ - "workbench debug call stack view reset view location", - "workbench debug" - ] - }, - { - "id": "workbench.debug.loadedScriptsView.open", - "title": "workbench.debug.loadedScriptsView.open", - "keywords": [ - "workbench debug loaded scripts view open", - "workbench debug" - ] - }, - { - "id": "workbench.debug.loadedScriptsView.focus", - "title": "workbench.debug.loadedScriptsView.focus", - "keywords": [ - "workbench debug loaded scripts view focus", - "workbench debug" - ] - }, - { - "id": "workbench.debug.loadedScriptsView.resetViewLocation", - "title": "workbench.debug.loadedScriptsView.resetViewLocation", - "keywords": [ - "workbench debug loaded scripts view reset view location", - "workbench debug" - ] - }, - { - "id": "workbench.debug.breakPointsView.open", - "title": "workbench.debug.breakPointsView.open", - "keywords": [ - "workbench debug break points view open", - "workbench debug" - ] - }, - { - "id": "workbench.debug.action.focusBreakpointsView", - "title": "workbench.debug.action.focusBreakpointsView", - "keywords": [ - "workbench debug action focus breakpoints view", - "workbench debug" - ] - }, - { - "id": "workbench.debug.breakPointsView.resetViewLocation", - "title": "workbench.debug.breakPointsView.resetViewLocation", - "keywords": [ - "workbench debug break points view reset view location", - "workbench debug" - ] - }, - { - "id": "workbench.view.debug", - "title": "workbench.view.debug", - "keywords": [ - "workbench view debug", - "workbench view" - ] - }, - { - "id": "workbench.view.remote", - "title": "workbench.view.remote", - "keywords": [ - "workbench view remote", - "workbench view" - ] - }, - { - "id": "workbench.panel.testResults.view.open", - "title": "workbench.panel.testResults.view.open", - "keywords": [ - "workbench panel test results view open", - "workbench panel" - ] - }, - { - "id": "workbench.panel.testResults.view.focus", - "title": "workbench.panel.testResults.view.focus", - "keywords": [ - "workbench panel test results view focus", - "workbench panel" - ] - }, - { - "id": "workbench.panel.testResults.view.resetViewLocation", - "title": "workbench.panel.testResults.view.resetViewLocation", - "keywords": [ - "workbench panel test results view reset view location", - "workbench panel" - ] - }, - { - "id": "refactorPreview.open", - "title": "refactorPreview.open", - "keywords": [ - "refactor preview open", - "refactor preview" - ] - }, - { - "id": "refactorPreview.focus", - "title": "refactorPreview.focus", - "keywords": [ - "refactor preview focus", - "refactor preview" - ] - }, - { - "id": "refactorPreview.resetViewLocation", - "title": "refactorPreview.resetViewLocation", - "keywords": [ - "refactor preview reset view location", - "refactor preview" - ] - }, - { - "id": "refactorPreview", - "title": "refactorPreview", - "keywords": [ - "refactor preview" - ] - }, - { - "id": "workbench.debug.action.toggleRepl", - "title": "workbench.debug.action.toggleRepl", - "keywords": [ - "workbench debug action toggle repl", - "workbench debug" - ] - }, - { - "id": "workbench.panel.repl.view.focus", - "title": "workbench.panel.repl.view.focus", - "keywords": [ - "workbench panel repl view focus", - "workbench panel" - ] - }, - { - "id": "workbench.panel.repl.view.resetViewLocation", - "title": "workbench.panel.repl.view.resetViewLocation", - "keywords": [ - "workbench panel repl view reset view location", - "workbench panel" - ] - }, - { - "id": "workbench.actions.view.problems", - "title": "workbench.actions.view.problems", - "keywords": [ - "workbench actions view problems", - "workbench actions" - ] - }, - { - "id": "workbench.panel.markers.view.focus", - "title": "workbench.panel.markers.view.focus", - "keywords": [ - "workbench panel markers view focus", - "workbench panel" - ] - }, - { - "id": "workbench.panel.markers.view.resetViewLocation", - "title": "workbench.panel.markers.view.resetViewLocation", - "keywords": [ - "workbench panel markers view reset view location", - "workbench panel" - ] - }, - { - "id": "workbench.action.terminal.toggleTerminal", - "title": "workbench.action.terminal.toggleTerminal", - "keywords": [ - "terminal toggle terminal", - "terminal toggle" - ] - }, - { - "id": "terminal.focus", - "title": "terminal.focus", - "keywords": [ - "terminal focus" - ] - }, - { - "id": "terminal.resetViewLocation", - "title": "terminal.resetViewLocation", - "keywords": [ - "terminal reset view location", - "terminal reset" - ] - }, - { - "id": "workbench.action.output.toggleOutput", - "title": "workbench.action.output.toggleOutput", - "keywords": [ - "output toggle output", - "output toggle" - ] - }, - { - "id": "workbench.panel.output.focus", - "title": "workbench.panel.output.focus", - "keywords": [ - "workbench panel output focus", - "workbench panel" - ] - }, - { - "id": "workbench.panel.output.resetViewLocation", - "title": "workbench.panel.output.resetViewLocation", - "keywords": [ - "workbench panel output reset view location", - "workbench panel" - ] - }, - { - "id": "workbench.action.focusTitleBar", - "title": "workbench.action.focusTitleBar", - "keywords": [ - "focus title bar", - "focus title" - ] - }, - { - "id": "registerWindowTitleVariable", - "title": "registerWindowTitleVariable", - "keywords": [ - "register window title variable", - "register window" - ] - }, - { - "id": "workbench.action.openSettings", - "title": "workbench.action.openSettings", - "keywords": [ - "open settings" - ] - }, - { - "id": "workbench.action.openSettings2", - "title": "workbench.action.openSettings2", - "keywords": [ - "open settings2" - ] - }, - { - "id": "workbench.action.openSettingsJson", - "title": "workbench.action.openSettingsJson", - "keywords": [ - "open settings json", - "open settings" - ] - }, - { - "id": "workbench.action.openApplicationSettingsJson", - "title": "workbench.action.openApplicationSettingsJson", - "keywords": [ - "open application settings json", - "open application" - ] - }, - { - "id": "workbench.action.openGlobalSettings", - "title": "workbench.action.openGlobalSettings", - "keywords": [ - "open global settings", - "open global" - ] - }, - { - "id": "workbench.action.openRawDefaultSettings", - "title": "workbench.action.openRawDefaultSettings", - "keywords": [ - "open raw default settings", - "open raw" - ] - }, - { - "id": "workbench.action.configureLanguageBasedSettings", - "title": "workbench.action.configureLanguageBasedSettings", - "keywords": [ - "configure language based settings", - "configure language" - ] - }, - { - "id": "workbench.action.openWorkspaceSettings", - "title": "workbench.action.openWorkspaceSettings", - "keywords": [ - "open workspace settings", - "open workspace" - ] - }, - { - "id": "workbench.action.openAccessibilitySettings", - "title": "workbench.action.openAccessibilitySettings", - "keywords": [ - "open accessibility settings", - "open accessibility" - ] - }, - { - "id": "workbench.action.openWorkspaceSettingsFile", - "title": "workbench.action.openWorkspaceSettingsFile", - "keywords": [ - "open workspace settings file", - "open workspace" - ] - }, - { - "id": "workbench.action.openFolderSettings", - "title": "workbench.action.openFolderSettings", - "keywords": [ - "open folder settings", - "open folder" - ] - }, - { - "id": "workbench.action.openFolderSettingsFile", - "title": "workbench.action.openFolderSettingsFile", - "keywords": [ - "open folder settings file", - "open folder" - ] - }, - { - "id": "settings.filterByOnline", - "title": "settings.filterByOnline", - "keywords": [ - "settings filter by online", - "settings filter" - ] - }, - { - "id": "settings.action.toggleAiSearch", - "title": "settings.action.toggleAiSearch", - "keywords": [ - "settings action toggle ai search", - "settings action" - ] - }, - { - "id": "settings.filterUntrusted", - "title": "settings.filterUntrusted", - "keywords": [ - "settings filter untrusted", - "settings filter" - ] - }, - { - "id": "settings.filterByTelemetry", - "title": "settings.filterByTelemetry", - "keywords": [ - "settings filter by telemetry", - "settings filter" - ] - }, - { - "id": "settings.action.search", - "title": "settings.action.search", - "keywords": [ - "settings action search", - "settings action" - ] - }, - { - "id": "settings.action.clearSearchResults", - "title": "settings.action.clearSearchResults", - "keywords": [ - "settings action clear search results", - "settings action" - ] - }, - { - "id": "settings.action.focusSettingsFile", - "title": "settings.action.focusSettingsFile", - "keywords": [ - "settings action focus settings file", - "settings action" - ] - }, - { - "id": "settings.action.focusSettingsFromSearch", - "title": "settings.action.focusSettingsFromSearch", - "keywords": [ - "settings action focus settings from search", - "settings action" - ] - }, - { - "id": "settings.action.focusSettingsList", - "title": "settings.action.focusSettingsList", - "keywords": [ - "settings action focus settings list", - "settings action" - ] - }, - { - "id": "settings.action.focusTOC", - "title": "settings.action.focusTOC", - "keywords": [ - "settings action focus toc", - "settings action" - ] - }, - { - "id": "settings.action.focusSettingControl", - "title": "settings.action.focusSettingControl", - "keywords": [ - "settings action focus setting control", - "settings action" - ] - }, - { - "id": "settings.action.showContextMenu", - "title": "settings.action.showContextMenu", - "keywords": [ - "settings action show context menu", - "settings action" - ] - }, - { - "id": "settings.action.focusLevelUp", - "title": "settings.action.focusLevelUp", - "keywords": [ - "settings action focus level up", - "settings action" - ] - }, - { - "id": "workbench.action.openGlobalKeybindings", - "title": "workbench.action.openGlobalKeybindings", - "keywords": [ - "open global keybindings", - "open global" - ] - }, - { - "id": "workbench.action.openDefaultKeybindingsFile", - "title": "workbench.action.openDefaultKeybindingsFile", - "keywords": [ - "open default keybindings file", - "open default" - ] - }, - { - "id": "workbench.action.openGlobalKeybindingsFile", - "title": "workbench.action.openGlobalKeybindingsFile", - "keywords": [ - "open global keybindings file", - "open global" - ] - }, - { - "id": "keybindings.editor.showDefaultKeybindings", - "title": "keybindings.editor.showDefaultKeybindings", - "keywords": [ - "keybindings editor show default keybindings", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.showExtensionKeybindings", - "title": "keybindings.editor.showExtensionKeybindings", - "keywords": [ - "keybindings editor show extension keybindings", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.showUserKeybindings", - "title": "keybindings.editor.showUserKeybindings", - "keywords": [ - "keybindings editor show user keybindings", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.clearSearchResults", - "title": "keybindings.editor.clearSearchResults", - "keywords": [ - "keybindings editor clear search results", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.clearSearchHistory", - "title": "keybindings.editor.clearSearchHistory", - "keywords": [ - "keybindings editor clear search history", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.defineKeybinding", - "title": "keybindings.editor.defineKeybinding", - "keywords": [ - "keybindings editor define keybinding", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.addKeybinding", - "title": "keybindings.editor.addKeybinding", - "keywords": [ - "keybindings editor add keybinding", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.defineWhenExpression", - "title": "keybindings.editor.defineWhenExpression", - "keywords": [ - "keybindings editor define when expression", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.removeKeybinding", - "title": "keybindings.editor.removeKeybinding", - "keywords": [ - "keybindings editor remove keybinding", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.resetKeybinding", - "title": "keybindings.editor.resetKeybinding", - "keywords": [ - "keybindings editor reset keybinding", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.searchKeybindings", - "title": "keybindings.editor.searchKeybindings", - "keywords": [ - "keybindings editor search keybindings", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.recordSearchKeys", - "title": "keybindings.editor.recordSearchKeys", - "keywords": [ - "keybindings editor record search keys", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.toggleSortByPrecedence", - "title": "keybindings.editor.toggleSortByPrecedence", - "keywords": [ - "keybindings editor toggle sort by precedence", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.showConflicts", - "title": "keybindings.editor.showConflicts", - "keywords": [ - "keybindings editor show conflicts", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.copyKeybindingEntry", - "title": "keybindings.editor.copyKeybindingEntry", - "keywords": [ - "keybindings editor copy keybinding entry", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.copyCommandKeybindingEntry", - "title": "keybindings.editor.copyCommandKeybindingEntry", - "keywords": [ - "keybindings editor copy command keybinding entry", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.copyCommandTitle", - "title": "keybindings.editor.copyCommandTitle", - "keywords": [ - "keybindings editor copy command title", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.focusKeybindings", - "title": "keybindings.editor.focusKeybindings", - "keywords": [ - "keybindings editor focus keybindings", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.rejectWhenExpression", - "title": "keybindings.editor.rejectWhenExpression", - "keywords": [ - "keybindings editor reject when expression", - "keybindings editor" - ] - }, - { - "id": "keybindings.editor.acceptWhenExpression", - "title": "keybindings.editor.acceptWhenExpression", - "keywords": [ - "keybindings editor accept when expression", - "keybindings editor" - ] - }, - { - "id": "editor.action.defineKeybinding", - "title": "editor.action.defineKeybinding", - "keywords": [ - "define keybinding" - ] - }, - { - "id": "workbench.action.remote.showMenu", - "title": "workbench.action.remote.showMenu", - "keywords": [ - "remote show menu", - "remote show" - ] - }, - { - "id": "workbench.action.remote.close", - "title": "workbench.action.remote.close", - "keywords": [ - "remote close" - ] - }, - { - "id": "changeEditorIndentation2", - "title": "changeEditorIndentation2", - "keywords": [ - "change editor indentation2", - "change editor" - ] - }, - { - "id": "workbench.action.chat.retrySetup", - "title": "workbench.action.chat.retrySetup", - "keywords": [ - "chat retry setup", - "chat retry" - ] - }, - { - "id": "workbench.action.chat.showOutput", - "title": "workbench.action.chat.showOutput", - "keywords": [ - "chat show output", - "chat show" - ] - }, - { - "id": "workbench.action.chat.triggerSetup", - "title": "workbench.action.chat.triggerSetup", - "keywords": [ - "chat trigger setup", - "chat trigger" - ] - }, - { - "id": "workbench.action.chat.triggerSetupForceSignIn", - "title": "workbench.action.chat.triggerSetupForceSignIn", - "keywords": [ - "chat trigger setup force sign in", - "chat trigger" - ] - }, - { - "id": "workbench.action.chat.triggerSetupFromAccounts", - "title": "workbench.action.chat.triggerSetupFromAccounts", - "keywords": [ - "chat trigger setup from accounts", - "chat trigger" - ] - }, - { - "id": "workbench.action.chat.signInIndicator", - "title": "workbench.action.chat.signInIndicator", - "keywords": [ - "chat sign in indicator", - "chat sign" - ] - }, - { - "id": "toggle.chat.titleBar.signIn.enabled", - "title": "toggle.chat.titleBar.signIn.enabled", - "keywords": [ - "toggle chat title bar sign in enabled", - "toggle chat" - ] - }, - { - "id": "workbench.action.chat.triggerSetupAnonymousWithoutDialog", - "title": "workbench.action.chat.triggerSetupAnonymousWithoutDialog", - "keywords": [ - "chat trigger setup anonymous without dialog", - "chat trigger" - ] - }, - { - "id": "workbench.action.chat.triggerSetupSupportAnonymousAction", - "title": "workbench.action.chat.triggerSetupSupportAnonymousAction", - "keywords": [ - "chat trigger setup support anonymous action", - "chat trigger" - ] - }, - { - "id": "workbench.action.chat.upgradePlan", - "title": "workbench.action.chat.upgradePlan", - "keywords": [ - "chat upgrade plan", - "chat upgrade" - ] - }, - { - "id": "workbench.action.chat.manageAdditionalSpend", - "title": "workbench.action.chat.manageAdditionalSpend", - "keywords": [ - "chat manage additional spend", - "chat manage" - ] - }, - { - "id": "chat.internal.explain", - "title": "chat.internal.explain", - "keywords": [ - "chat internal explain", - "chat internal" - ] - }, - { - "id": "chat.internal.fix", - "title": "chat.internal.fix", - "keywords": [ - "chat internal fix", - "chat internal" - ] - }, - { - "id": "chat.internal.review", - "title": "chat.internal.review", - "keywords": [ - "chat internal review", - "chat internal" - ] - }, - { - "id": "chat.internal.codeReview.run", - "title": "chat.internal.codeReview.run", - "keywords": [ - "chat internal code review run", - "chat internal" - ] - }, - { - "id": "workbench.profiles.actions.manageProfiles", - "title": "workbench.profiles.actions.manageProfiles", - "keywords": [ - "workbench profiles actions manage profiles", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.switchProfile", - "title": "workbench.profiles.actions.switchProfile", - "keywords": [ - "workbench profiles actions switch profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.newWindowWithProfile", - "title": "workbench.profiles.actions.newWindowWithProfile", - "keywords": [ - "workbench profiles actions new window with profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.profileEntry.__default__profile__", - "title": "workbench.profiles.actions.profileEntry.__default__profile__", - "keywords": [ - "workbench profiles actions profile entry default profile", - "workbench profiles" - ] - }, - { - "id": "workbench.action.openProfile.Default", - "title": "workbench.action.openProfile.Default", - "keywords": [ - "open profile default", - "open profile" - ] - }, - { - "id": "workbench.profiles.actions.exportProfile", - "title": "workbench.profiles.actions.exportProfile", - "keywords": [ - "workbench profiles actions export profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.createFromCurrentProfile", - "title": "workbench.profiles.actions.createFromCurrentProfile", - "keywords": [ - "workbench profiles actions create from current profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.createProfile", - "title": "workbench.profiles.actions.createProfile", - "keywords": [ - "workbench profiles actions create profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.deleteProfile", - "title": "workbench.profiles.actions.deleteProfile", - "keywords": [ - "workbench profiles actions delete profile", - "workbench profiles" - ] - }, - { - "id": "workbench.profiles.actions.help", - "title": "workbench.profiles.actions.help", - "keywords": [ - "workbench profiles actions help", - "workbench profiles" - ] - }, - { - "id": "showEditorScreenReaderNotification", - "title": "showEditorScreenReaderNotification", - "keywords": [ - "show editor screen reader notification", - "show editor" - ] - }, - { - "id": "workbench.action.showHover", - "title": "workbench.action.showHover", - "keywords": [ - "show hover" - ] - }, - { - "id": "notifications.showList", - "title": "notifications.showList", - "keywords": [ - "notifications show list", - "notifications show" - ] - }, - { - "id": "notifications.hideList", - "title": "notifications.hideList", - "keywords": [ - "notifications hide list", - "notifications hide" - ] - }, - { - "id": "notifications.toggleList", - "title": "notifications.toggleList", - "keywords": [ - "notifications toggle list", - "notifications toggle" - ] - }, - { - "id": "notification.clear", - "title": "notification.clear", - "keywords": [ - "notification clear" - ] - }, - { - "id": "notification.expand", - "title": "notification.expand", - "keywords": [ - "notification expand" - ] - }, - { - "id": "notification.acceptPrimaryAction", - "title": "notification.acceptPrimaryAction", - "keywords": [ - "notification accept primary action", - "notification accept" - ] - }, - { - "id": "notification.collapse", - "title": "notification.collapse", - "keywords": [ - "notification collapse" - ] - }, - { - "id": "notification.toggle", - "title": "notification.toggle", - "keywords": [ - "notification toggle" - ] - }, - { - "id": "notifications.hideToasts", - "title": "notifications.hideToasts", - "keywords": [ - "notifications hide toasts", - "notifications hide" - ] - }, - { - "id": "notifications.focusToasts", - "title": "notifications.focusToasts", - "keywords": [ - "notifications focus toasts", - "notifications focus" - ] - }, - { - "id": "notifications.focusNextToast", - "title": "notifications.focusNextToast", - "keywords": [ - "notifications focus next toast", - "notifications focus" - ] - }, - { - "id": "notifications.focusPreviousToast", - "title": "notifications.focusPreviousToast", - "keywords": [ - "notifications focus previous toast", - "notifications focus" - ] - }, - { - "id": "notifications.focusFirstToast", - "title": "notifications.focusFirstToast", - "keywords": [ - "notifications focus first toast", - "notifications focus" - ] - }, - { - "id": "notifications.focusLastToast", - "title": "notifications.focusLastToast", - "keywords": [ - "notifications focus last toast", - "notifications focus" - ] - }, - { - "id": "notifications.clearAll", - "title": "notifications.clearAll", - "keywords": [ - "notifications clear all", - "notifications clear" - ] - }, - { - "id": "notifications.toggleDoNotDisturbMode", - "title": "notifications.toggleDoNotDisturbMode", - "keywords": [ - "notifications toggle do not disturb mode", - "notifications toggle" - ] - }, - { - "id": "notifications.toggleDoNotDisturbModeBySource", - "title": "notifications.toggleDoNotDisturbModeBySource", - "keywords": [ - "notifications toggle do not disturb mode by source", - "notifications toggle" - ] - }, - { - "id": "timeline.refresh", - "title": "timeline.refresh", - "keywords": [ - "timeline refresh" - ] - }, - { - "id": "timeline.toggleFollowActiveEditor", - "title": "timeline.toggleFollowActiveEditor", - "keywords": [ - "timeline toggle follow active editor", - "timeline toggle" - ] - }, - { - "id": "timeline.toggleExcludeSource:timeline.localHistory", - "title": "timeline.toggleExcludeSource:timeline.localHistory", - "keywords": [ - "timeline toggle exclude source:timeline local history", - "timeline toggle" - ] - }, - { - "id": "workbench.action.chat.pickAgentSession", - "title": "workbench.action.chat.pickAgentSession", - "keywords": [ - "chat pick agent session", - "chat pick" - ] - }, - { - "id": "workbench.explorer.openEditorsView.open", - "title": "workbench.explorer.openEditorsView.open", - "keywords": [ - "workbench explorer open editors view open", - "workbench explorer" - ] - }, - { - "id": "workbench.files.action.focusOpenEditorsView", - "title": "workbench.files.action.focusOpenEditorsView", - "keywords": [ - "workbench files action focus open editors view", - "workbench files" - ] - }, - { - "id": "workbench.explorer.openEditorsView.resetViewLocation", - "title": "workbench.explorer.openEditorsView.resetViewLocation", - "keywords": [ - "workbench explorer open editors view reset view location", - "workbench explorer" - ] - }, - { - "id": "workbench.explorer.fileView.open", - "title": "workbench.explorer.fileView.open", - "keywords": [ - "workbench explorer file view open", - "workbench explorer" - ] - }, - { - "id": "workbench.explorer.fileView.focus", - "title": "workbench.explorer.fileView.focus", - "keywords": [ - "workbench explorer file view focus", - "workbench explorer" - ] - }, - { - "id": "workbench.explorer.fileView.resetViewLocation", - "title": "workbench.explorer.fileView.resetViewLocation", - "keywords": [ - "workbench explorer file view reset view location", - "workbench explorer" - ] - }, - { - "id": "workbench.action.chat.openSessionWithPrompt.claude-code", - "title": "workbench.action.chat.openSessionWithPrompt.claude-code", - "keywords": [ - "chat open session with prompt claude code", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionEditor.claude-code", - "title": "workbench.action.chat.openNewSessionEditor.claude-code", - "keywords": [ - "chat open new session editor claude code", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionSidebar.claude-code", - "title": "workbench.action.chat.openNewSessionSidebar.claude-code", - "keywords": [ - "chat open new session sidebar claude code", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openSessionWithPrompt.copilotcli", - "title": "workbench.action.chat.openSessionWithPrompt.copilotcli", - "keywords": [ - "chat open session with prompt copilotcli", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionEditor.copilotcli", - "title": "workbench.action.chat.openNewSessionEditor.copilotcli", - "keywords": [ - "chat open new session editor copilotcli", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionSidebar.copilotcli", - "title": "workbench.action.chat.openNewSessionSidebar.copilotcli", - "keywords": [ - "chat open new session sidebar copilotcli", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openSessionWithPrompt.copilot-cloud-agent", - "title": "workbench.action.chat.openSessionWithPrompt.copilot-cloud-agent", - "keywords": [ - "chat open session with prompt copilot cloud agent", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionEditor.copilot-cloud-agent", - "title": "workbench.action.chat.openNewSessionEditor.copilot-cloud-agent", - "keywords": [ - "chat open new session editor copilot cloud agent", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewSessionSidebar.copilot-cloud-agent", - "title": "workbench.action.chat.openNewSessionSidebar.copilot-cloud-agent", - "keywords": [ - "chat open new session sidebar copilot cloud agent", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewChatSessionInPlace.local", - "title": "workbench.action.chat.openNewChatSessionInPlace.local", - "keywords": [ - "chat open new chat session in place local", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewChatSessionInPlace.claude-code", - "title": "workbench.action.chat.openNewChatSessionInPlace.claude-code", - "keywords": [ - "chat open new chat session in place claude code", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewChatSessionInPlace.copilotcli", - "title": "workbench.action.chat.openNewChatSessionInPlace.copilotcli", - "keywords": [ - "chat open new chat session in place copilotcli", - "chat open" - ] - }, - { - "id": "workbench.action.chat.openNewChatSessionInPlace.copilot-cloud-agent", - "title": "workbench.action.chat.openNewChatSessionInPlace.copilot-cloud-agent", - "keywords": [ - "chat open new chat session in place copilot cloud agent", - "chat open" - ] - }, - { - "id": "workbench.view.extension.copilot-chat.resetViewContainerLocation", - "title": "workbench.view.extension.copilot-chat.resetViewContainerLocation", - "keywords": [ - "workbench view extension copilot chat reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.copilot-chat", - "title": "workbench.view.extension.copilot-chat", - "keywords": [ - "workbench view extension copilot chat", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.context-inspector.resetViewContainerLocation", - "title": "workbench.view.extension.context-inspector.resetViewContainerLocation", - "keywords": [ - "workbench view extension context inspector reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.context-inspector", - "title": "workbench.view.extension.context-inspector", - "keywords": [ - "workbench view extension context inspector", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.references-view.resetViewContainerLocation", - "title": "workbench.view.extension.references-view.resetViewContainerLocation", - "keywords": [ - "workbench view extension references view reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.references-view", - "title": "workbench.view.extension.references-view", - "keywords": [ - "workbench view extension references view", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.package-explorer.resetViewContainerLocation", - "title": "workbench.view.extension.package-explorer.resetViewContainerLocation", - "keywords": [ - "workbench view extension package explorer reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.package-explorer", - "title": "workbench.view.extension.package-explorer", - "keywords": [ - "workbench view extension package explorer", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.github-actions.resetViewContainerLocation", - "title": "workbench.view.extension.github-actions.resetViewContainerLocation", - "keywords": [ - "workbench view extension github actions reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.github-actions", - "title": "workbench.view.extension.github-actions", - "keywords": [ - "workbench view extension github actions", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.github-pull-requests.resetViewContainerLocation", - "title": "workbench.view.extension.github-pull-requests.resetViewContainerLocation", - "keywords": [ - "workbench view extension github pull requests reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.github-pull-requests", - "title": "workbench.view.extension.github-pull-requests", - "keywords": [ - "workbench view extension github pull requests", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.github-pull-request.resetViewContainerLocation", - "title": "workbench.view.extension.github-pull-request.resetViewContainerLocation", - "keywords": [ - "workbench view extension github pull request reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.github-pull-request", - "title": "workbench.view.extension.github-pull-request", - "keywords": [ - "workbench view extension github pull request", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-duo.resetViewContainerLocation", - "title": "workbench.view.extension.gitlab-duo.resetViewContainerLocation", - "keywords": [ - "workbench view extension gitlab duo reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-duo", - "title": "workbench.view.extension.gitlab-duo", - "keywords": [ - "workbench view extension gitlab duo", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-workflow.resetViewContainerLocation", - "title": "workbench.view.extension.gitlab-workflow.resetViewContainerLocation", - "keywords": [ - "workbench view extension gitlab workflow reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-workflow", - "title": "workbench.view.extension.gitlab-workflow", - "keywords": [ - "workbench view extension gitlab workflow", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-agent-platform.resetViewContainerLocation", - "title": "workbench.view.extension.gitlab-agent-platform.resetViewContainerLocation", - "keywords": [ - "workbench view extension gitlab agent platform reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-agent-platform", - "title": "workbench.view.extension.gitlab-agent-platform", - "keywords": [ - "workbench view extension gitlab agent platform", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-agent-platform-next.resetViewContainerLocation", - "title": "workbench.view.extension.gitlab-agent-platform-next.resetViewContainerLocation", - "keywords": [ - "workbench view extension gitlab agent platform next reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.gitlab-agent-platform-next", - "title": "workbench.view.extension.gitlab-agent-platform-next", - "keywords": [ - "workbench view extension gitlab agent platform next", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.containersView.resetViewContainerLocation", - "title": "workbench.view.extension.containersView.resetViewContainerLocation", - "keywords": [ - "workbench view extension containers view reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.containersView", - "title": "workbench.view.extension.containersView", - "keywords": [ - "workbench view extension containers view", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.jupyter.resetViewContainerLocation", - "title": "workbench.view.extension.jupyter.resetViewContainerLocation", - "keywords": [ - "workbench view extension jupyter reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.jupyter", - "title": "workbench.view.extension.jupyter", - "keywords": [ - "workbench view extension jupyter", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.jupyter-variables.resetViewContainerLocation", - "title": "workbench.view.extension.jupyter-variables.resetViewContainerLocation", - "keywords": [ - "workbench view extension jupyter variables reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.jupyter-variables", - "title": "workbench.view.extension.jupyter-variables", - "keywords": [ - "workbench view extension jupyter variables", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.cmake-view.resetViewContainerLocation", - "title": "workbench.view.extension.cmake-view.resetViewContainerLocation", - "keywords": [ - "workbench view extension cmake view reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.cmake-view", - "title": "workbench.view.extension.cmake-view", - "keywords": [ - "workbench view extension cmake view", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.makefile__viewContainer.resetViewContainerLocation", - "title": "workbench.view.extension.makefile__viewContainer.resetViewContainerLocation", - "keywords": [ - "workbench view extension makefile view container reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.makefile__viewContainer", - "title": "workbench.view.extension.makefile__viewContainer", - "keywords": [ - "workbench view extension makefile view container", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.liveshare.resetViewContainerLocation", - "title": "workbench.view.extension.liveshare.resetViewContainerLocation", - "keywords": [ - "workbench view extension liveshare reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.liveshare", - "title": "workbench.view.extension.liveshare", - "keywords": [ - "workbench view extension liveshare", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.rustSyntaxTreeContainer.resetViewContainerLocation", - "title": "workbench.view.extension.rustSyntaxTreeContainer.resetViewContainerLocation", - "keywords": [ - "workbench view extension rust syntax tree container reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.rustSyntaxTreeContainer", - "title": "workbench.view.extension.rustSyntaxTreeContainer", - "keywords": [ - "workbench view extension rust syntax tree container", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.echocode.resetViewContainerLocation", - "title": "workbench.view.extension.echocode.resetViewContainerLocation", - "keywords": [ - "workbench view extension echocode reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.extension.echocode", - "title": "workbench.view.extension.echocode", - "keywords": [ - "workbench view extension echocode", - "workbench view" - ] - }, - { - "id": "copilot-chat.open", - "title": "copilot-chat.open", - "keywords": [ - "copilot chat open", - "copilot chat" - ] - }, - { - "id": "copilot-chat.focus", - "title": "copilot-chat.focus", - "keywords": [ - "copilot chat focus", - "copilot chat" - ] - }, - { - "id": "copilot-chat.resetViewLocation", - "title": "copilot-chat.resetViewLocation", - "keywords": [ - "copilot chat reset view location", - "copilot chat" - ] - }, - { - "id": "context-inspector.open", - "title": "context-inspector.open", - "keywords": [ - "context inspector open", - "context inspector" - ] - }, - { - "id": "context-inspector.focus", - "title": "context-inspector.focus", - "keywords": [ - "context inspector focus", - "context inspector" - ] - }, - { - "id": "context-inspector.resetViewLocation", - "title": "context-inspector.resetViewLocation", - "keywords": [ - "context inspector reset view location", - "context inspector" - ] - }, - { - "id": "jsBrowserBreakpoints.open", - "title": "jsBrowserBreakpoints.open", - "keywords": [ - "js browser breakpoints open", - "js browser" - ] - }, - { - "id": "jsBrowserBreakpoints.focus", - "title": "jsBrowserBreakpoints.focus", - "keywords": [ - "js browser breakpoints focus", - "js browser" - ] - }, - { - "id": "jsBrowserBreakpoints.resetViewLocation", - "title": "jsBrowserBreakpoints.resetViewLocation", - "keywords": [ - "js browser breakpoints reset view location", - "js browser" - ] - }, - { - "id": "jsExcludedCallers.open", - "title": "jsExcludedCallers.open", - "keywords": [ - "js excluded callers open", - "js excluded" - ] - }, - { - "id": "jsExcludedCallers.focus", - "title": "jsExcludedCallers.focus", - "keywords": [ - "js excluded callers focus", - "js excluded" - ] - }, - { - "id": "jsExcludedCallers.resetViewLocation", - "title": "jsExcludedCallers.resetViewLocation", - "keywords": [ - "js excluded callers reset view location", - "js excluded" - ] - }, - { - "id": "jsDebugNetworkTree.open", - "title": "jsDebugNetworkTree.open", - "keywords": [ - "js debug network tree open", - "js debug" - ] - }, - { - "id": "jsDebugNetworkTree.focus", - "title": "jsDebugNetworkTree.focus", - "keywords": [ - "js debug network tree focus", - "js debug" - ] - }, - { - "id": "jsDebugNetworkTree.resetViewLocation", - "title": "jsDebugNetworkTree.resetViewLocation", - "keywords": [ - "js debug network tree reset view location", - "js debug" - ] - }, - { - "id": "npm.open", - "title": "npm.open", - "keywords": [ - "npm open" - ] - }, - { - "id": "npm.focus", - "title": "npm.focus", - "keywords": [ - "npm focus" - ] - }, - { - "id": "npm.resetViewLocation", - "title": "npm.resetViewLocation", - "keywords": [ - "npm reset view location", - "npm reset" - ] - }, - { - "id": "references-view.tree.open", - "title": "references-view.tree.open", - "keywords": [ - "references view tree open", - "references view" - ] - }, - { - "id": "references-view.tree.focus", - "title": "references-view.tree.focus", - "keywords": [ - "references view tree focus", - "references view" - ] - }, - { - "id": "references-view.tree.resetViewLocation", - "title": "references-view.tree.resetViewLocation", - "keywords": [ - "references view tree reset view location", - "references view" - ] - }, - { - "id": "workspaceEnvironments.open", - "title": "workspaceEnvironments.open", - "keywords": [ - "workspace environments open", - "workspace environments" - ] - }, - { - "id": "workspaceEnvironments.focus", - "title": "workspaceEnvironments.focus", - "keywords": [ - "workspace environments focus", - "workspace environments" - ] - }, - { - "id": "workspaceEnvironments.resetViewLocation", - "title": "workspaceEnvironments.resetViewLocation", - "keywords": [ - "workspace environments reset view location", - "workspace environments" - ] - }, - { - "id": "pythonEnvironments.open", - "title": "pythonEnvironments.open", - "keywords": [ - "python environments open", - "python environments" - ] - }, - { - "id": "pythonEnvironments.focus", - "title": "pythonEnvironments.focus", - "keywords": [ - "python environments focus", - "python environments" - ] - }, - { - "id": "pythonEnvironments.resetViewLocation", - "title": "pythonEnvironments.resetViewLocation", - "keywords": [ - "python environments reset view location", - "python environments" - ] - }, - { - "id": "github-actions.current-branch.open", - "title": "github-actions.current-branch.open", - "keywords": [ - "github actions current branch open", - "github actions" - ] - }, - { - "id": "github-actions.current-branch.focus", - "title": "github-actions.current-branch.focus", - "keywords": [ - "github actions current branch focus", - "github actions" - ] - }, - { - "id": "github-actions.current-branch.resetViewLocation", - "title": "github-actions.current-branch.resetViewLocation", - "keywords": [ - "github actions current branch reset view location", - "github actions" - ] - }, - { - "id": "github-actions.workflows.open", - "title": "github-actions.workflows.open", - "keywords": [ - "github actions workflows open", - "github actions" - ] - }, - { - "id": "github-actions.workflows.focus", - "title": "github-actions.workflows.focus", - "keywords": [ - "github actions workflows focus", - "github actions" - ] - }, - { - "id": "github-actions.workflows.resetViewLocation", - "title": "github-actions.workflows.resetViewLocation", - "keywords": [ - "github actions workflows reset view location", - "github actions" - ] - }, - { - "id": "github-actions.settings.open", - "title": "github-actions.settings.open", - "keywords": [ - "github actions settings open", - "github actions" - ] - }, - { - "id": "github-actions.settings.focus", - "title": "github-actions.settings.focus", - "keywords": [ - "github actions settings focus", - "github actions" - ] - }, - { - "id": "github-actions.settings.resetViewLocation", - "title": "github-actions.settings.resetViewLocation", - "keywords": [ - "github actions settings reset view location", - "github actions" - ] - }, - { - "id": "github-actions.empty-view.open", - "title": "github-actions.empty-view.open", - "keywords": [ - "github actions empty view open", - "github actions" - ] - }, - { - "id": "github-actions.empty-view.focus", - "title": "github-actions.empty-view.focus", - "keywords": [ - "github actions empty view focus", - "github actions" - ] - }, - { - "id": "github-actions.empty-view.resetViewLocation", - "title": "github-actions.empty-view.resetViewLocation", - "keywords": [ - "github actions empty view reset view location", - "github actions" - ] - }, - { - "id": "github:login.open", - "title": "github:login.open", - "keywords": [ - "github:login open" - ] - }, - { - "id": "github:login.focus", - "title": "github:login.focus", - "keywords": [ - "github:login focus" - ] - }, - { - "id": "github:login.resetViewLocation", - "title": "github:login.resetViewLocation", - "keywords": [ - "github:login reset view location", - "github:login reset" - ] - }, - { - "id": "pr:github.open", - "title": "pr:github.open", - "keywords": [ - "pr:github open" - ] - }, - { - "id": "pr:github.focus", - "title": "pr:github.focus", - "keywords": [ - "pr:github focus" - ] - }, - { - "id": "pr:github.resetViewLocation", - "title": "pr:github.resetViewLocation", - "keywords": [ - "pr:github reset view location", - "pr:github reset" - ] - }, - { - "id": "issues:github.open", - "title": "issues:github.open", - "keywords": [ - "issues:github open" - ] - }, - { - "id": "issues:github.focus", - "title": "issues:github.focus", - "keywords": [ - "issues:github focus" - ] - }, - { - "id": "issues:github.resetViewLocation", - "title": "issues:github.resetViewLocation", - "keywords": [ - "issues:github reset view location", - "issues:github reset" - ] - }, - { - "id": "notifications:github.open", - "title": "notifications:github.open", - "keywords": [ - "notifications:github open" - ] - }, - { - "id": "notifications:github.focus", - "title": "notifications:github.focus", - "keywords": [ - "notifications:github focus" - ] - }, - { - "id": "notifications:github.resetViewLocation", - "title": "notifications:github.resetViewLocation", - "keywords": [ - "notifications:github reset view location", - "notifications:github reset" - ] - }, - { - "id": "github:conflictResolution.open", - "title": "github:conflictResolution.open", - "keywords": [ - "github:conflict resolution open", - "github:conflict resolution" - ] - }, - { - "id": "github:conflictResolution.focus", - "title": "github:conflictResolution.focus", - "keywords": [ - "github:conflict resolution focus", - "github:conflict resolution" - ] - }, - { - "id": "github:conflictResolution.resetViewLocation", - "title": "github:conflictResolution.resetViewLocation", - "keywords": [ - "github:conflict resolution reset view location", - "github:conflict resolution" - ] - }, - { - "id": "github:createPullRequestWebview.open", - "title": "github:createPullRequestWebview.open", - "keywords": [ - "github:create pull request webview open", - "github:create pull" - ] - }, - { - "id": "github:createPullRequestWebview.focus", - "title": "github:createPullRequestWebview.focus", - "keywords": [ - "github:create pull request webview focus", - "github:create pull" - ] - }, - { - "id": "github:createPullRequestWebview.resetViewLocation", - "title": "github:createPullRequestWebview.resetViewLocation", - "keywords": [ - "github:create pull request webview reset view location", - "github:create pull" - ] - }, - { - "id": "github:compareChangesFiles.open", - "title": "github:compareChangesFiles.open", - "keywords": [ - "github:compare changes files open", - "github:compare changes" - ] - }, - { - "id": "github:compareChangesFiles.focus", - "title": "github:compareChangesFiles.focus", - "keywords": [ - "github:compare changes files focus", - "github:compare changes" - ] - }, - { - "id": "github:compareChangesFiles.resetViewLocation", - "title": "github:compareChangesFiles.resetViewLocation", - "keywords": [ - "github:compare changes files reset view location", - "github:compare changes" - ] - }, - { - "id": "github:compareChangesCommits.open", - "title": "github:compareChangesCommits.open", - "keywords": [ - "github:compare changes commits open", - "github:compare changes" - ] - }, - { - "id": "github:compareChangesCommits.focus", - "title": "github:compareChangesCommits.focus", - "keywords": [ - "github:compare changes commits focus", - "github:compare changes" - ] - }, - { - "id": "github:compareChangesCommits.resetViewLocation", - "title": "github:compareChangesCommits.resetViewLocation", - "keywords": [ - "github:compare changes commits reset view location", - "github:compare changes" - ] - }, - { - "id": "prStatus:github.open", - "title": "prStatus:github.open", - "keywords": [ - "pr status:github open", - "pr status:github" - ] - }, - { - "id": "prStatus:github.focus", - "title": "prStatus:github.focus", - "keywords": [ - "pr status:github focus", - "pr status:github" - ] - }, - { - "id": "prStatus:github.resetViewLocation", - "title": "prStatus:github.resetViewLocation", - "keywords": [ - "pr status:github reset view location", - "pr status:github" - ] - }, - { - "id": "github:activePullRequest.open", - "title": "github:activePullRequest.open", - "keywords": [ - "github:active pull request open", - "github:active pull" - ] - }, - { - "id": "github:activePullRequest.focus", - "title": "github:activePullRequest.focus", - "keywords": [ - "github:active pull request focus", - "github:active pull" - ] - }, - { - "id": "github:activePullRequest.resetViewLocation", - "title": "github:activePullRequest.resetViewLocation", - "keywords": [ - "github:active pull request reset view location", - "github:active pull" - ] - }, - { - "id": "github:activePullRequest:welcome.open", - "title": "github:activePullRequest:welcome.open", - "keywords": [ - "github:active pull request:welcome open", - "github:active pull" - ] - }, - { - "id": "github:activePullRequest:welcome.focus", - "title": "github:activePullRequest:welcome.focus", - "keywords": [ - "github:active pull request:welcome focus", - "github:active pull" - ] - }, - { - "id": "github:activePullRequest:welcome.resetViewLocation", - "title": "github:activePullRequest:welcome.resetViewLocation", - "keywords": [ - "github:active pull request:welcome reset view location", - "github:active pull" - ] - }, - { - "id": "issuesAndMrs.open", - "title": "issuesAndMrs.open", - "keywords": [ - "issues and mrs open", - "issues and" - ] - }, - { - "id": "issuesAndMrs.focus", - "title": "issuesAndMrs.focus", - "keywords": [ - "issues and mrs focus", - "issues and" - ] - }, - { - "id": "issuesAndMrs.resetViewLocation", - "title": "issuesAndMrs.resetViewLocation", - "keywords": [ - "issues and mrs reset view location", - "issues and" - ] - }, - { - "id": "remoteSecurityScanning.open", - "title": "remoteSecurityScanning.open", - "keywords": [ - "remote security scanning open", - "remote security" - ] - }, - { - "id": "remoteSecurityScanning.focus", - "title": "remoteSecurityScanning.focus", - "keywords": [ - "remote security scanning focus", - "remote security" - ] - }, - { - "id": "remoteSecurityScanning.resetViewLocation", - "title": "remoteSecurityScanning.resetViewLocation", - "keywords": [ - "remote security scanning reset view location", - "remote security" - ] - }, - { - "id": "currentBranchInfo.open", - "title": "currentBranchInfo.open", - "keywords": [ - "current branch info open", - "current branch" - ] - }, - { - "id": "currentBranchInfo.focus", - "title": "currentBranchInfo.focus", - "keywords": [ - "current branch info focus", - "current branch" - ] - }, - { - "id": "currentBranchInfo.resetViewLocation", - "title": "currentBranchInfo.resetViewLocation", - "keywords": [ - "current branch info reset view location", - "current branch" - ] - }, - { - "id": "gl.chatView.open", - "title": "gl.chatView.open", - "keywords": [ - "gl chat view open", - "gl chat" - ] - }, - { - "id": "gl.chatView.focus", - "title": "gl.chatView.focus", - "keywords": [ - "gl chat view focus", - "gl chat" - ] - }, - { - "id": "gl.chatView.resetViewLocation", - "title": "gl.chatView.resetViewLocation", - "keywords": [ - "gl chat view reset view location", - "gl chat" - ] - }, - { - "id": "gl.webview.duo-chat-v2.open", - "title": "gl.webview.duo-chat-v2.open", - "keywords": [ - "gl webview duo chat v2 open", - "gl webview" - ] - }, - { - "id": "gl.webview.duo-chat-v2.focus", - "title": "gl.webview.duo-chat-v2.focus", - "keywords": [ - "gl webview duo chat v2 focus", - "gl webview" - ] - }, - { - "id": "gl.webview.duo-chat-v2.resetViewLocation", - "title": "gl.webview.duo-chat-v2.resetViewLocation", - "keywords": [ - "gl webview duo chat v2 reset view location", - "gl webview" - ] - }, - { - "id": "gl.webview.agentic-tabs.open", - "title": "gl.webview.agentic-tabs.open", - "keywords": [ - "gl webview agentic tabs open", - "gl webview" - ] - }, - { - "id": "gl.webview.agentic-tabs.focus", - "title": "gl.webview.agentic-tabs.focus", - "keywords": [ - "gl webview agentic tabs focus", - "gl webview" - ] - }, - { - "id": "gl.webview.agentic-tabs.resetViewLocation", - "title": "gl.webview.agentic-tabs.resetViewLocation", - "keywords": [ - "gl webview agentic tabs reset view location", - "gl webview" - ] - }, - { - "id": "gl.webview.root.duoAgentPlatform.open", - "title": "gl.webview.root.duoAgentPlatform.open", - "keywords": [ - "gl webview root duo agent platform open", - "gl webview" - ] - }, - { - "id": "gl.webview.root.duoAgentPlatform.focus", - "title": "gl.webview.root.duoAgentPlatform.focus", - "keywords": [ - "gl webview root duo agent platform focus", - "gl webview" - ] - }, - { - "id": "gl.webview.root.duoAgentPlatform.resetViewLocation", - "title": "gl.webview.root.duoAgentPlatform.resetViewLocation", - "keywords": [ - "gl webview root duo agent platform reset view location", - "gl webview" - ] - }, - { - "id": "vscode-containers.views.containers.open", - "title": "vscode-containers.views.containers.open", - "keywords": [ - "vscode containers views containers open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.containers.focus", - "title": "vscode-containers.views.containers.focus", - "keywords": [ - "vscode containers views containers focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.containers.resetViewLocation", - "title": "vscode-containers.views.containers.resetViewLocation", - "keywords": [ - "vscode containers views containers reset view location", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.images.open", - "title": "vscode-containers.views.images.open", - "keywords": [ - "vscode containers views images open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.images.focus", - "title": "vscode-containers.views.images.focus", - "keywords": [ - "vscode containers views images focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.images.resetViewLocation", - "title": "vscode-containers.views.images.resetViewLocation", - "keywords": [ - "vscode containers views images reset view location", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.registries.open", - "title": "vscode-containers.views.registries.open", - "keywords": [ - "vscode containers views registries open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.registries.focus", - "title": "vscode-containers.views.registries.focus", - "keywords": [ - "vscode containers views registries focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.registries.resetViewLocation", - "title": "vscode-containers.views.registries.resetViewLocation", - "keywords": [ - "vscode containers views registries reset view location", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.networks.open", - "title": "vscode-containers.views.networks.open", - "keywords": [ - "vscode containers views networks open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.networks.focus", - "title": "vscode-containers.views.networks.focus", - "keywords": [ - "vscode containers views networks focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.networks.resetViewLocation", - "title": "vscode-containers.views.networks.resetViewLocation", - "keywords": [ - "vscode containers views networks reset view location", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.volumes.open", - "title": "vscode-containers.views.volumes.open", - "keywords": [ - "vscode containers views volumes open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.volumes.focus", - "title": "vscode-containers.views.volumes.focus", - "keywords": [ - "vscode containers views volumes focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.volumes.resetViewLocation", - "title": "vscode-containers.views.volumes.resetViewLocation", - "keywords": [ - "vscode containers views volumes reset view location", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.dockerContexts.open", - "title": "vscode-containers.views.dockerContexts.open", - "keywords": [ - "vscode containers views docker contexts open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.dockerContexts.focus", - "title": "vscode-containers.views.dockerContexts.focus", - "keywords": [ - "vscode containers views docker contexts focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.dockerContexts.resetViewLocation", - "title": "vscode-containers.views.dockerContexts.resetViewLocation", - "keywords": [ - "vscode containers views docker contexts reset view location", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.help.open", - "title": "vscode-containers.views.help.open", - "keywords": [ - "vscode containers views help open", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.help.focus", - "title": "vscode-containers.views.help.focus", - "keywords": [ - "vscode containers views help focus", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.help.resetViewLocation", - "title": "vscode-containers.views.help.resetViewLocation", - "keywords": [ - "vscode containers views help reset view location", - "vscode containers" - ] - }, - { - "id": "solutionExplorer.open", - "title": "solutionExplorer.open", - "keywords": [ - "solution explorer open", - "solution explorer" - ] - }, - { - "id": "solutionExplorer.focus", - "title": "solutionExplorer.focus", - "keywords": [ - "solution explorer focus", - "solution explorer" - ] - }, - { - "id": "solutionExplorer.resetViewLocation", - "title": "solutionExplorer.resetViewLocation", - "keywords": [ - "solution explorer reset view location", - "solution explorer" - ] - }, - { - "id": "jupyterViewVariables.open", - "title": "jupyterViewVariables.open", - "keywords": [ - "jupyter view variables open", - "jupyter view" - ] - }, - { - "id": "jupyterViewVariables.focus", - "title": "jupyterViewVariables.focus", - "keywords": [ - "jupyter view variables focus", - "jupyter view" - ] - }, - { - "id": "jupyterViewVariables.resetViewLocation", - "title": "jupyterViewVariables.resetViewLocation", - "keywords": [ - "jupyter view variables reset view location", - "jupyter view" - ] - }, - { - "id": "cell-tag.open", - "title": "cell-tag.open", - "keywords": [ - "cell tag open", - "cell tag" - ] - }, - { - "id": "cell-tag.focus", - "title": "cell-tag.focus", - "keywords": [ - "cell tag focus", - "cell tag" - ] - }, - { - "id": "cell-tag.resetViewLocation", - "title": "cell-tag.resetViewLocation", - "keywords": [ - "cell tag reset view location", - "cell tag" - ] - }, - { - "id": "cmake.projectStatus.open", - "title": "cmake.projectStatus.open", - "keywords": [ - "cmake project status open", - "cmake project" - ] - }, - { - "id": "cmake.projectStatus.focus", - "title": "cmake.projectStatus.focus", - "keywords": [ - "cmake project status focus", - "cmake project" - ] - }, - { - "id": "cmake.projectStatus.resetViewLocation", - "title": "cmake.projectStatus.resetViewLocation", - "keywords": [ - "cmake project status reset view location", - "cmake project" - ] - }, - { - "id": "cmake.outline.open", - "title": "cmake.outline.open", - "keywords": [ - "cmake outline open", - "cmake outline" - ] - }, - { - "id": "cmake.outline.focus", - "title": "cmake.outline.focus", - "keywords": [ - "cmake outline focus", - "cmake outline" - ] - }, - { - "id": "cmake.outline.resetViewLocation", - "title": "cmake.outline.resetViewLocation", - "keywords": [ - "cmake outline reset view location", - "cmake outline" - ] - }, - { - "id": "cmake.pinnedCommands.open", - "title": "cmake.pinnedCommands.open", - "keywords": [ - "cmake pinned commands open", - "cmake pinned" - ] - }, - { - "id": "cmake.pinnedCommands.focus", - "title": "cmake.pinnedCommands.focus", - "keywords": [ - "cmake pinned commands focus", - "cmake pinned" - ] - }, - { - "id": "cmake.pinnedCommands.resetViewLocation", - "title": "cmake.pinnedCommands.resetViewLocation", - "keywords": [ - "cmake pinned commands reset view location", - "cmake pinned" - ] - }, - { - "id": "cmake.bookmarks.open", - "title": "cmake.bookmarks.open", - "keywords": [ - "cmake bookmarks open", - "cmake bookmarks" - ] - }, - { - "id": "cmake.bookmarks.focus", - "title": "cmake.bookmarks.focus", - "keywords": [ - "cmake bookmarks focus", - "cmake bookmarks" - ] - }, - { - "id": "cmake.bookmarks.resetViewLocation", - "title": "cmake.bookmarks.resetViewLocation", - "keywords": [ - "cmake bookmarks reset view location", - "cmake bookmarks" - ] - }, - { - "id": "CppReferencesView.open", - "title": "CppReferencesView.open", - "keywords": [ - "cpp references view open", - "cpp references" - ] - }, - { - "id": "CppReferencesView.focus", - "title": "CppReferencesView.focus", - "keywords": [ - "cpp references view focus", - "cpp references" - ] - }, - { - "id": "CppReferencesView.resetViewLocation", - "title": "CppReferencesView.resetViewLocation", - "keywords": [ - "cpp references view reset view location", - "cpp references" - ] - }, - { - "id": "CppSshTargetsView.open", - "title": "CppSshTargetsView.open", - "keywords": [ - "cpp ssh targets view open", - "cpp ssh" - ] - }, - { - "id": "CppSshTargetsView.focus", - "title": "CppSshTargetsView.focus", - "keywords": [ - "cpp ssh targets view focus", - "cpp ssh" - ] - }, - { - "id": "CppSshTargetsView.resetViewLocation", - "title": "CppSshTargetsView.resetViewLocation", - "keywords": [ - "cpp ssh targets view reset view location", - "cpp ssh" - ] - }, - { - "id": "makefile.outline.open", - "title": "makefile.outline.open", - "keywords": [ - "makefile outline open", - "makefile outline" - ] - }, - { - "id": "makefile.outline.focus", - "title": "makefile.outline.focus", - "keywords": [ - "makefile outline focus", - "makefile outline" - ] - }, - { - "id": "makefile.outline.resetViewLocation", - "title": "makefile.outline.resetViewLocation", - "keywords": [ - "makefile outline reset view location", - "makefile outline" - ] - }, - { - "id": "remoteTargets.open", - "title": "remoteTargets.open", - "keywords": [ - "remote targets open", - "remote targets" - ] - }, - { - "id": "remoteTargets.focus", - "title": "remoteTargets.focus", - "keywords": [ - "remote targets focus", - "remote targets" - ] - }, - { - "id": "remoteTargets.resetViewLocation", - "title": "remoteTargets.resetViewLocation", - "keywords": [ - "remote targets reset view location", - "remote targets" - ] - }, - { - "id": "remoteHub.views.workspaceRepositories.open", - "title": "remoteHub.views.workspaceRepositories.open", - "keywords": [ - "remote hub views workspace repositories open", - "remote hub" - ] - }, - { - "id": "remoteHub.views.workspaceRepositories.focus", - "title": "remoteHub.views.workspaceRepositories.focus", - "keywords": [ - "remote hub views workspace repositories focus", - "remote hub" - ] - }, - { - "id": "remoteHub.views.workspaceRepositories.resetViewLocation", - "title": "remoteHub.views.workspaceRepositories.resetViewLocation", - "keywords": [ - "remote hub views workspace repositories reset view location", - "remote hub" - ] - }, - { - "id": "liveshare.session.open", - "title": "liveshare.session.open", - "keywords": [ - "liveshare session open", - "liveshare session" - ] - }, - { - "id": "liveshare.session.focus", - "title": "liveshare.session.focus", - "keywords": [ - "liveshare session focus", - "liveshare session" - ] - }, - { - "id": "liveshare.session.resetViewLocation", - "title": "liveshare.session.resetViewLocation", - "keywords": [ - "liveshare session reset view location", - "liveshare session" - ] - }, - { - "id": "liveshare.help.open", - "title": "liveshare.help.open", - "keywords": [ - "liveshare help open", - "liveshare help" - ] - }, - { - "id": "liveshare.help.focus", - "title": "liveshare.help.focus", - "keywords": [ - "liveshare help focus", - "liveshare help" - ] - }, - { - "id": "liveshare.help.resetViewLocation", - "title": "liveshare.help.resetViewLocation", - "keywords": [ - "liveshare help reset view location", - "liveshare help" - ] - }, - { - "id": "liveshare.devtools.open", - "title": "liveshare.devtools.open", - "keywords": [ - "liveshare devtools open", - "liveshare devtools" - ] - }, - { - "id": "liveshare.devtools.focus", - "title": "liveshare.devtools.focus", - "keywords": [ - "liveshare devtools focus", - "liveshare devtools" - ] - }, - { - "id": "liveshare.devtools.resetViewLocation", - "title": "liveshare.devtools.resetViewLocation", - "keywords": [ - "liveshare devtools reset view location", - "liveshare devtools" - ] - }, - { - "id": "liveshare.session.explorer.open", - "title": "liveshare.session.explorer.open", - "keywords": [ - "liveshare session explorer open", - "liveshare session" - ] - }, - { - "id": "liveshare.session.explorer.focus", - "title": "liveshare.session.explorer.focus", - "keywords": [ - "liveshare session explorer focus", - "liveshare session" - ] - }, - { - "id": "liveshare.session.explorer.resetViewLocation", - "title": "liveshare.session.explorer.resetViewLocation", - "keywords": [ - "liveshare session explorer reset view location", - "liveshare session" - ] - }, - { - "id": "foundProjects.open", - "title": "foundProjects.open", - "keywords": [ - "found projects open", - "found projects" - ] - }, - { - "id": "foundProjects.focus", - "title": "foundProjects.focus", - "keywords": [ - "found projects focus", - "found projects" - ] - }, - { - "id": "foundProjects.resetViewLocation", - "title": "foundProjects.resetViewLocation", - "keywords": [ - "found projects reset view location", - "found projects" - ] - }, - { - "id": "run-config.open", - "title": "run-config.open", - "keywords": [ - "run config open", - "run config" - ] - }, - { - "id": "run-config.focus", - "title": "run-config.focus", - "keywords": [ - "run config focus", - "run config" - ] - }, - { - "id": "run-config.resetViewLocation", - "title": "run-config.resetViewLocation", - "keywords": [ - "run config reset view location", - "run config" - ] - }, - { - "id": "rustDependencies.open", - "title": "rustDependencies.open", - "keywords": [ - "rust dependencies open", - "rust dependencies" - ] - }, - { - "id": "rustDependencies.focus", - "title": "rustDependencies.focus", - "keywords": [ - "rust dependencies focus", - "rust dependencies" - ] - }, - { - "id": "rustDependencies.resetViewLocation", - "title": "rustDependencies.resetViewLocation", - "keywords": [ - "rust dependencies reset view location", - "rust dependencies" - ] - }, - { - "id": "rustSyntaxTree.open", - "title": "rustSyntaxTree.open", - "keywords": [ - "rust syntax tree open", - "rust syntax" - ] - }, - { - "id": "rustSyntaxTree.focus", - "title": "rustSyntaxTree.focus", - "keywords": [ - "rust syntax tree focus", - "rust syntax" - ] - }, - { - "id": "rustSyntaxTree.resetViewLocation", - "title": "rustSyntaxTree.resetViewLocation", - "keywords": [ - "rust syntax tree reset view location", - "rust syntax" - ] - }, - { - "id": "workbench.panel.chat.view.copilot.toggleVisibility", - "title": "workbench.panel.chat.view.copilot.toggleVisibility", - "keywords": [ - "workbench panel chat view copilot toggle visibility", - "workbench panel" - ] - }, - { - "id": "workbench.panel.chat.view.copilot.removeView", - "title": "workbench.panel.chat.view.copilot.removeView", - "keywords": [ - "workbench panel chat view copilot remove view", - "workbench panel" - ] - }, - { - "id": "workbench.explorer.openEditorsView.toggleVisibility", - "title": "workbench.explorer.openEditorsView.toggleVisibility", - "keywords": [ - "workbench explorer open editors view toggle visibility", - "workbench explorer" - ] - }, - { - "id": "workbench.explorer.openEditorsView.removeView", - "title": "workbench.explorer.openEditorsView.removeView", - "keywords": [ - "workbench explorer open editors view remove view", - "workbench explorer" - ] - }, - { - "id": "workbench.explorer.fileView.toggleVisibility", - "title": "workbench.explorer.fileView.toggleVisibility", - "keywords": [ - "workbench explorer file view toggle visibility", - "workbench explorer" - ] - }, - { - "id": "workbench.explorer.fileView.removeView", - "title": "workbench.explorer.fileView.removeView", - "keywords": [ - "workbench explorer file view remove view", - "workbench explorer" - ] - }, - { - "id": "outline.toggleVisibility", - "title": "outline.toggleVisibility", - "keywords": [ - "outline toggle visibility", - "outline toggle" - ] - }, - { - "id": "outline.removeView", - "title": "outline.removeView", - "keywords": [ - "outline remove view", - "outline remove" - ] - }, - { - "id": "timeline.toggleVisibility", - "title": "timeline.toggleVisibility", - "keywords": [ - "timeline toggle visibility", - "timeline toggle" - ] - }, - { - "id": "timeline.removeView", - "title": "timeline.removeView", - "keywords": [ - "timeline remove view", - "timeline remove" - ] - }, - { - "id": "workbench.view.search.toggleVisibility", - "title": "workbench.view.search.toggleVisibility", - "keywords": [ - "workbench view search toggle visibility", - "workbench view" - ] - }, - { - "id": "workbench.view.search.removeView", - "title": "workbench.view.search.removeView", - "keywords": [ - "workbench view search remove view", - "workbench view" - ] - }, - { - "id": "workbench.scm.toggleVisibility", - "title": "workbench.scm.toggleVisibility", - "keywords": [ - "workbench scm toggle visibility", - "workbench scm" - ] - }, - { - "id": "workbench.scm.removeView", - "title": "workbench.scm.removeView", - "keywords": [ - "workbench scm remove view", - "workbench scm" - ] - }, - { - "id": "workbench.debug.welcome.toggleVisibility", - "title": "workbench.debug.welcome.toggleVisibility", - "keywords": [ - "workbench debug welcome toggle visibility", - "workbench debug" - ] - }, - { - "id": "workbench.debug.welcome.removeView", - "title": "workbench.debug.welcome.removeView", - "keywords": [ - "workbench debug welcome remove view", - "workbench debug" - ] - }, - { - "id": "workbench.debug.breakPointsView.toggleVisibility", - "title": "workbench.debug.breakPointsView.toggleVisibility", - "keywords": [ - "workbench debug break points view toggle visibility", - "workbench debug" - ] - }, - { - "id": "workbench.debug.breakPointsView.removeView", - "title": "workbench.debug.breakPointsView.removeView", - "keywords": [ - "workbench debug break points view remove view", - "workbench debug" - ] - }, - { - "id": "workbench.panel.markers.view.toggleVisibility", - "title": "workbench.panel.markers.view.toggleVisibility", - "keywords": [ - "workbench panel markers view toggle visibility", - "workbench panel" - ] - }, - { - "id": "workbench.panel.markers.view.removeView", - "title": "workbench.panel.markers.view.removeView", - "keywords": [ - "workbench panel markers view remove view", - "workbench panel" - ] - }, - { - "id": "terminal.toggleVisibility", - "title": "terminal.toggleVisibility", - "keywords": [ - "terminal toggle visibility", - "terminal toggle" - ] - }, - { - "id": "terminal.removeView", - "title": "terminal.removeView", - "keywords": [ - "terminal remove view", - "terminal remove" - ] - }, - { - "id": "workbench.panel.output.toggleVisibility", - "title": "workbench.panel.output.toggleVisibility", - "keywords": [ - "workbench panel output toggle visibility", - "workbench panel" - ] - }, - { - "id": "workbench.panel.output.removeView", - "title": "workbench.panel.output.removeView", - "keywords": [ - "workbench panel output remove view", - "workbench panel" - ] - }, - { - "id": "workspaceEnvironments.toggleVisibility", - "title": "workspaceEnvironments.toggleVisibility", - "keywords": [ - "workspace environments toggle visibility", - "workspace environments" - ] - }, - { - "id": "workspaceEnvironments.removeView", - "title": "workspaceEnvironments.removeView", - "keywords": [ - "workspace environments remove view", - "workspace environments" - ] - }, - { - "id": "pythonEnvironments.toggleVisibility", - "title": "pythonEnvironments.toggleVisibility", - "keywords": [ - "python environments toggle visibility", - "python environments" - ] - }, - { - "id": "pythonEnvironments.removeView", - "title": "pythonEnvironments.removeView", - "keywords": [ - "python environments remove view", - "python environments" - ] - }, - { - "id": "github-actions.empty-view.toggleVisibility", - "title": "github-actions.empty-view.toggleVisibility", - "keywords": [ - "github actions empty view toggle visibility", - "github actions" - ] - }, - { - "id": "github-actions.empty-view.removeView", - "title": "github-actions.empty-view.removeView", - "keywords": [ - "github actions empty view remove view", - "github actions" - ] - }, - { - "id": "pr:github.toggleVisibility", - "title": "pr:github.toggleVisibility", - "keywords": [ - "pr:github toggle visibility", - "pr:github toggle" - ] - }, - { - "id": "pr:github.removeView", - "title": "pr:github.removeView", - "keywords": [ - "pr:github remove view", - "pr:github remove" - ] - }, - { - "id": "issues:github.toggleVisibility", - "title": "issues:github.toggleVisibility", - "keywords": [ - "issues:github toggle visibility", - "issues:github toggle" - ] - }, - { - "id": "issues:github.removeView", - "title": "issues:github.removeView", - "keywords": [ - "issues:github remove view", - "issues:github remove" - ] - }, - { - "id": "notifications:github.toggleVisibility", - "title": "notifications:github.toggleVisibility", - "keywords": [ - "notifications:github toggle visibility", - "notifications:github toggle" - ] - }, - { - "id": "notifications:github.removeView", - "title": "notifications:github.removeView", - "keywords": [ - "notifications:github remove view", - "notifications:github remove" - ] - }, - { - "id": "vscode-containers.views.containers.toggleVisibility", - "title": "vscode-containers.views.containers.toggleVisibility", - "keywords": [ - "vscode containers views containers toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.containers.removeView", - "title": "vscode-containers.views.containers.removeView", - "keywords": [ - "vscode containers views containers remove view", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.images.toggleVisibility", - "title": "vscode-containers.views.images.toggleVisibility", - "keywords": [ - "vscode containers views images toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.images.removeView", - "title": "vscode-containers.views.images.removeView", - "keywords": [ - "vscode containers views images remove view", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.registries.toggleVisibility", - "title": "vscode-containers.views.registries.toggleVisibility", - "keywords": [ - "vscode containers views registries toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.registries.removeView", - "title": "vscode-containers.views.registries.removeView", - "keywords": [ - "vscode containers views registries remove view", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.networks.toggleVisibility", - "title": "vscode-containers.views.networks.toggleVisibility", - "keywords": [ - "vscode containers views networks toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.networks.removeView", - "title": "vscode-containers.views.networks.removeView", - "keywords": [ - "vscode containers views networks remove view", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.volumes.toggleVisibility", - "title": "vscode-containers.views.volumes.toggleVisibility", - "keywords": [ - "vscode containers views volumes toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.volumes.removeView", - "title": "vscode-containers.views.volumes.removeView", - "keywords": [ - "vscode containers views volumes remove view", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.dockerContexts.toggleVisibility", - "title": "vscode-containers.views.dockerContexts.toggleVisibility", - "keywords": [ - "vscode containers views docker contexts toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.dockerContexts.removeView", - "title": "vscode-containers.views.dockerContexts.removeView", - "keywords": [ - "vscode containers views docker contexts remove view", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.help.toggleVisibility", - "title": "vscode-containers.views.help.toggleVisibility", - "keywords": [ - "vscode containers views help toggle visibility", - "vscode containers" - ] - }, - { - "id": "vscode-containers.views.help.removeView", - "title": "vscode-containers.views.help.removeView", - "keywords": [ - "vscode containers views help remove view", - "vscode containers" - ] - }, - { - "id": "liveshare.session.toggleVisibility", - "title": "liveshare.session.toggleVisibility", - "keywords": [ - "liveshare session toggle visibility", - "liveshare session" - ] - }, - { - "id": "liveshare.session.removeView", - "title": "liveshare.session.removeView", - "keywords": [ - "liveshare session remove view", - "liveshare session" - ] - }, - { - "id": "workbench.action.openRemoteSettings", - "title": "workbench.action.openRemoteSettings", - "keywords": [ - "open remote settings", - "open remote" - ] - }, - { - "id": "workbench.action.openRemoteSettingsFile", - "title": "workbench.action.openRemoteSettingsFile", - "keywords": [ - "open remote settings file", - "open remote" - ] - }, - { - "id": "workbench.action.terminal.newWithProfile", - "title": "workbench.action.terminal.newWithProfile", - "keywords": [ - "terminal new with profile", - "terminal new" - ] - }, - { - "id": "workbench.action.tasks.runTask", - "title": "workbench.action.tasks.runTask", - "keywords": [ - "tasks run task", - "tasks run" - ] - }, - { - "id": "workbench.action.tasks.reRunTask", - "title": "workbench.action.tasks.reRunTask", - "keywords": [ - "tasks re run task", - "tasks re" - ] - }, - { - "id": "workbench.action.tasks.restartTask", - "title": "workbench.action.tasks.restartTask", - "keywords": [ - "tasks restart task", - "tasks restart" - ] - }, - { - "id": "workbench.action.tasks.rerunAllRunningTasks", - "title": "workbench.action.tasks.rerunAllRunningTasks", - "keywords": [ - "tasks rerun all running tasks", - "tasks rerun" - ] - }, - { - "id": "workbench.action.tasks.terminate", - "title": "workbench.action.tasks.terminate", - "keywords": [ - "tasks terminate" - ] - }, - { - "id": "workbench.action.tasks.showLog", - "title": "workbench.action.tasks.showLog", - "keywords": [ - "tasks show log", - "tasks show" - ] - }, - { - "id": "workbench.action.tasks.build", - "title": "workbench.action.tasks.build", - "keywords": [ - "tasks build" - ] - }, - { - "id": "workbench.action.tasks.test", - "title": "workbench.action.tasks.test", - "keywords": [ - "tasks test" - ] - }, - { - "id": "workbench.action.tasks.configureTaskRunner", - "title": "workbench.action.tasks.configureTaskRunner", - "keywords": [ - "tasks configure task runner", - "tasks configure" - ] - }, - { - "id": "workbench.action.tasks.configureDefaultBuildTask", - "title": "workbench.action.tasks.configureDefaultBuildTask", - "keywords": [ - "tasks configure default build task", - "tasks configure" - ] - }, - { - "id": "workbench.action.tasks.configureDefaultTestTask", - "title": "workbench.action.tasks.configureDefaultTestTask", - "keywords": [ - "tasks configure default test task", - "tasks configure" - ] - }, - { - "id": "workbench.action.tasks.showTasks", - "title": "workbench.action.tasks.showTasks", - "keywords": [ - "tasks show tasks", - "tasks show" - ] - }, - { - "id": "workbench.action.tasks.toggleProblems", - "title": "workbench.action.tasks.toggleProblems", - "keywords": [ - "tasks toggle problems", - "tasks toggle" - ] - }, - { - "id": "workbench.action.tasks.openUserTasks", - "title": "workbench.action.tasks.openUserTasks", - "keywords": [ - "tasks open user tasks", - "tasks open" - ] - }, - { - "id": "workbench.action.tasks.openWorkspaceFileTasks", - "title": "workbench.action.tasks.openWorkspaceFileTasks", - "keywords": [ - "tasks open workspace file tasks", - "tasks open" - ] - }, - { - "id": "workbench.panel.repl.view.toggleVisibility", - "title": "workbench.panel.repl.view.toggleVisibility", - "keywords": [ - "workbench panel repl view toggle visibility", - "workbench panel" - ] - }, - { - "id": "workbench.panel.repl.view.removeView", - "title": "workbench.panel.repl.view.removeView", - "keywords": [ - "workbench panel repl view remove view", - "workbench panel" - ] - }, - { - "id": "workbench.action.showTelemetry", - "title": "workbench.action.showTelemetry", - "keywords": [ - "show telemetry" - ] - }, - { - "id": "settings.switchToJSON", - "title": "settings.switchToJSON", - "keywords": [ - "settings switch to json", - "settings switch" - ] - }, - { - "id": "workbench.action.chat.manage", - "title": "workbench.action.chat.manage", - "keywords": [ - "chat manage" - ] - }, - { - "id": "chat.models.action.clearSearchResults", - "title": "chat.models.action.clearSearchResults", - "keywords": [ - "chat models action clear search results", - "chat models" - ] - }, - { - "id": "workbench.action.openLanguageModelsJson", - "title": "workbench.action.openLanguageModelsJson", - "keywords": [ - "open language models json", - "open language" - ] - }, - { - "id": "aiCustomization.openManagementEditor", - "title": "aiCustomization.openManagementEditor", - "keywords": [ - "ai customization open management editor", - "ai customization" - ] - }, - { - "id": "aiCustomization.openMarketplace", - "title": "aiCustomization.openMarketplace", - "keywords": [ - "ai customization open marketplace", - "ai customization" - ] - }, - { - "id": "aiCustomization.generateDebugReport", - "title": "aiCustomization.generateDebugReport", - "keywords": [ - "ai customization generate debug report", - "ai customization" - ] - }, - { - "id": "workbench.action.chat.hideSetup", - "title": "workbench.action.chat.hideSetup", - "keywords": [ - "chat hide setup", - "chat hide" - ] - }, - { - "id": "workbench.extensions.action.focusExtensionsView", - "title": "workbench.extensions.action.focusExtensionsView", - "keywords": [ - "workbench extensions action focus extensions view", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installExtensions", - "title": "workbench.extensions.action.installExtensions", - "keywords": [ - "workbench extensions action install extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showRecommendedKeymapExtensions", - "title": "workbench.extensions.action.showRecommendedKeymapExtensions", - "keywords": [ - "workbench extensions action show recommended keymap extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showLanguageExtensions", - "title": "workbench.extensions.action.showLanguageExtensions", - "keywords": [ - "workbench extensions action show language extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.checkForUpdates", - "title": "workbench.extensions.action.checkForUpdates", - "keywords": [ - "workbench extensions action check for updates", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.enableAutoUpdate", - "title": "workbench.extensions.action.enableAutoUpdate", - "keywords": [ - "workbench extensions action enable auto update", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.disableAutoUpdate", - "title": "workbench.extensions.action.disableAutoUpdate", - "keywords": [ - "workbench extensions action disable auto update", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.updateAllExtensions", - "title": "workbench.extensions.action.updateAllExtensions", - "keywords": [ - "workbench extensions action update all extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.enableAll", - "title": "workbench.extensions.action.enableAll", - "keywords": [ - "workbench extensions action enable all", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.enableAllWorkspace", - "title": "workbench.extensions.action.enableAllWorkspace", - "keywords": [ - "workbench extensions action enable all workspace", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.disableAll", - "title": "workbench.extensions.action.disableAll", - "keywords": [ - "workbench extensions action disable all", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.disableAllWorkspace", - "title": "workbench.extensions.action.disableAllWorkspace", - "keywords": [ - "workbench extensions action disable all workspace", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installVSIX", - "title": "workbench.extensions.action.installVSIX", - "keywords": [ - "workbench extensions action install vsix", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.command.installFromVSIX", - "title": "workbench.extensions.command.installFromVSIX", - "keywords": [ - "workbench extensions command install from vsix", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installExtensionFromLocation", - "title": "workbench.extensions.action.installExtensionFromLocation", - "keywords": [ - "workbench extensions action install extension from location", - "workbench extensions" - ] - }, - { - "id": "extensions.filter.featured", - "title": "extensions.filter.featured", - "keywords": [ - "extensions filter featured", - "extensions filter" - ] - }, - { - "id": "workbench.extensions.action.showPopularExtensions", - "title": "workbench.extensions.action.showPopularExtensions", - "keywords": [ - "workbench extensions action show popular extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showRecommendedExtensions", - "title": "workbench.extensions.action.showRecommendedExtensions", - "keywords": [ - "workbench extensions action show recommended extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.recentlyPublishedExtensions", - "title": "workbench.extensions.action.recentlyPublishedExtensions", - "keywords": [ - "workbench extensions action recently published extensions", - "workbench extensions" - ] - }, - { - "id": "extensions.actions.searchByCategory.AI", - "title": "extensions.actions.searchByCategory.AI", - "keywords": [ - "extensions actions search by category ai", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Azure", - "title": "extensions.actions.searchByCategory.Azure", - "keywords": [ - "extensions actions search by category azure", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Chat", - "title": "extensions.actions.searchByCategory.Chat", - "keywords": [ - "extensions actions search by category chat", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Data Science", - "title": "extensions.actions.searchByCategory.Data Science", - "keywords": [ - "extensions actions search by category data science", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Debuggers", - "title": "extensions.actions.searchByCategory.Debuggers", - "keywords": [ - "extensions actions search by category debuggers", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Extension Packs", - "title": "extensions.actions.searchByCategory.Extension Packs", - "keywords": [ - "extensions actions search by category extension packs", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Education", - "title": "extensions.actions.searchByCategory.Education", - "keywords": [ - "extensions actions search by category education", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Formatters", - "title": "extensions.actions.searchByCategory.Formatters", - "keywords": [ - "extensions actions search by category formatters", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Keymaps", - "title": "extensions.actions.searchByCategory.Keymaps", - "keywords": [ - "extensions actions search by category keymaps", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Language Packs", - "title": "extensions.actions.searchByCategory.Language Packs", - "keywords": [ - "extensions actions search by category language packs", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Linters", - "title": "extensions.actions.searchByCategory.Linters", - "keywords": [ - "extensions actions search by category linters", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Machine Learning", - "title": "extensions.actions.searchByCategory.Machine Learning", - "keywords": [ - "extensions actions search by category machine learning", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Notebooks", - "title": "extensions.actions.searchByCategory.Notebooks", - "keywords": [ - "extensions actions search by category notebooks", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Programming Languages", - "title": "extensions.actions.searchByCategory.Programming Languages", - "keywords": [ - "extensions actions search by category programming languages", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.SCM Providers", - "title": "extensions.actions.searchByCategory.SCM Providers", - "keywords": [ - "extensions actions search by category scm providers", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Snippets", - "title": "extensions.actions.searchByCategory.Snippets", - "keywords": [ - "extensions actions search by category snippets", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Testing", - "title": "extensions.actions.searchByCategory.Testing", - "keywords": [ - "extensions actions search by category testing", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Themes", - "title": "extensions.actions.searchByCategory.Themes", - "keywords": [ - "extensions actions search by category themes", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Visualization", - "title": "extensions.actions.searchByCategory.Visualization", - "keywords": [ - "extensions actions search by category visualization", - "extensions actions" - ] - }, - { - "id": "extensions.actions.searchByCategory.Other", - "title": "extensions.actions.searchByCategory.Other", - "keywords": [ - "extensions actions search by category other", - "extensions actions" - ] - }, - { - "id": "workbench.extensions.action.installedExtensions", - "title": "workbench.extensions.action.installedExtensions", - "keywords": [ - "workbench extensions action installed extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.listBuiltInExtensions", - "title": "workbench.extensions.action.listBuiltInExtensions", - "keywords": [ - "workbench extensions action list built in extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.extensionUpdates", - "title": "workbench.extensions.action.extensionUpdates", - "keywords": [ - "workbench extensions action extension updates", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.listWorkspaceUnsupportedExtensions", - "title": "workbench.extensions.action.listWorkspaceUnsupportedExtensions", - "keywords": [ - "workbench extensions action list workspace unsupported extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showEnabledExtensions", - "title": "workbench.extensions.action.showEnabledExtensions", - "keywords": [ - "workbench extensions action show enabled extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showDisabledExtensions", - "title": "workbench.extensions.action.showDisabledExtensions", - "keywords": [ - "workbench extensions action show disabled extensions", - "workbench extensions" - ] - }, - { - "id": "extensions.sort.installs", - "title": "extensions.sort.installs", - "keywords": [ - "extensions sort installs", - "extensions sort" - ] - }, - { - "id": "extensions.sort.rating", - "title": "extensions.sort.rating", - "keywords": [ - "extensions sort rating", - "extensions sort" - ] - }, - { - "id": "extensions.sort.name", - "title": "extensions.sort.name", - "keywords": [ - "extensions sort name", - "extensions sort" - ] - }, - { - "id": "extensions.sort.publishedDate", - "title": "extensions.sort.publishedDate", - "keywords": [ - "extensions sort published date", - "extensions sort" - ] - }, - { - "id": "extensions.sort.updateDate", - "title": "extensions.sort.updateDate", - "keywords": [ - "extensions sort update date", - "extensions sort" - ] - }, - { - "id": "workbench.extensions.action.clearExtensionsSearchResults", - "title": "workbench.extensions.action.clearExtensionsSearchResults", - "keywords": [ - "workbench extensions action clear extensions search results", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.refreshExtension", - "title": "workbench.extensions.action.refreshExtension", - "keywords": [ - "workbench extensions action refresh extension", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installWorkspaceRecommendedExtensions", - "title": "workbench.extensions.action.installWorkspaceRecommendedExtensions", - "keywords": [ - "workbench extensions action install workspace recommended extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions", - "title": "workbench.extensions.action.configureWorkspaceFolderRecommendedExtensions", - "keywords": [ - "workbench extensions action configure workspace folder recommended extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.install.specificVersion", - "title": "workbench.extensions.action.install.specificVersion", - "keywords": [ - "workbench extensions action install specific version", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.setColorTheme", - "title": "workbench.extensions.action.setColorTheme", - "keywords": [ - "workbench extensions action set color theme", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.setFileIconTheme", - "title": "workbench.extensions.action.setFileIconTheme", - "keywords": [ - "workbench extensions action set file icon theme", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.setProductIconTheme", - "title": "workbench.extensions.action.setProductIconTheme", - "keywords": [ - "workbench extensions action set product icon theme", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showPreReleaseVersion", - "title": "workbench.extensions.action.showPreReleaseVersion", - "keywords": [ - "workbench extensions action show pre release version", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.showReleasedVersion", - "title": "workbench.extensions.action.showReleasedVersion", - "keywords": [ - "workbench extensions action show released version", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.toggleAutoUpdateForExtension", - "title": "workbench.extensions.action.toggleAutoUpdateForExtension", - "keywords": [ - "workbench extensions action toggle auto update for extension", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.toggleAutoUpdatesForPublisher", - "title": "workbench.extensions.action.toggleAutoUpdatesForPublisher", - "keywords": [ - "workbench extensions action toggle auto updates for publisher", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.switchToPreRlease", - "title": "workbench.extensions.action.switchToPreRlease", - "keywords": [ - "workbench extensions action switch to pre rlease", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.switchToRelease", - "title": "workbench.extensions.action.switchToRelease", - "keywords": [ - "workbench extensions action switch to release", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.clearLanguage", - "title": "workbench.extensions.action.clearLanguage", - "keywords": [ - "workbench extensions action clear language", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installUnsigned", - "title": "workbench.extensions.action.installUnsigned", - "keywords": [ - "workbench extensions action install unsigned", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installAndDonotSync", - "title": "workbench.extensions.action.installAndDonotSync", - "keywords": [ - "workbench extensions action install and donot sync", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.installPrereleaseAndDonotSync", - "title": "workbench.extensions.action.installPrereleaseAndDonotSync", - "keywords": [ - "workbench extensions action install prerelease and donot sync", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.install.anotherVersion", - "title": "workbench.extensions.action.install.anotherVersion", - "keywords": [ - "workbench extensions action install another version", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.copyExtension", - "title": "workbench.extensions.action.copyExtension", - "keywords": [ - "workbench extensions action copy extension", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.copyExtensionId", - "title": "workbench.extensions.action.copyExtensionId", - "keywords": [ - "workbench extensions action copy extension id", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.copyLink", - "title": "workbench.extensions.action.copyLink", - "keywords": [ - "workbench extensions action copy link", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.configure", - "title": "workbench.extensions.action.configure", - "keywords": [ - "workbench extensions action configure", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.download", - "title": "workbench.extensions.action.download", - "keywords": [ - "workbench extensions action download", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.downloadPreRelease", - "title": "workbench.extensions.action.downloadPreRelease", - "keywords": [ - "workbench extensions action download pre release", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.downloadSpecificVersion", - "title": "workbench.extensions.action.downloadSpecificVersion", - "keywords": [ - "workbench extensions action download specific version", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.manageAccountPreferences", - "title": "workbench.extensions.action.manageAccountPreferences", - "keywords": [ - "workbench extensions action manage account preferences", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.configureKeybindings", - "title": "workbench.extensions.action.configureKeybindings", - "keywords": [ - "workbench extensions action configure keybindings", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.toggleApplyToAllProfiles", - "title": "workbench.extensions.action.toggleApplyToAllProfiles", - "keywords": [ - "workbench extensions action toggle apply to all profiles", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.toggleIgnoreExtension", - "title": "workbench.extensions.action.toggleIgnoreExtension", - "keywords": [ - "workbench extensions action toggle ignore extension", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.ignoreRecommendation", - "title": "workbench.extensions.action.ignoreRecommendation", - "keywords": [ - "workbench extensions action ignore recommendation", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.undoIgnoredRecommendation", - "title": "workbench.extensions.action.undoIgnoredRecommendation", - "keywords": [ - "workbench extensions action undo ignored recommendation", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.addExtensionToWorkspaceRecommendations", - "title": "workbench.extensions.action.addExtensionToWorkspaceRecommendations", - "keywords": [ - "workbench extensions action add extension to workspace recommendations", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.removeExtensionFromWorkspaceRecommendations", - "title": "workbench.extensions.action.removeExtensionFromWorkspaceRecommendations", - "keywords": [ - "workbench extensions action remove extension from workspace recommendations", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.addToWorkspaceRecommendations", - "title": "workbench.extensions.action.addToWorkspaceRecommendations", - "keywords": [ - "workbench extensions action add to workspace recommendations", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.addToWorkspaceFolderRecommendations", - "title": "workbench.extensions.action.addToWorkspaceFolderRecommendations", - "keywords": [ - "workbench extensions action add to workspace folder recommendations", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.addToWorkspaceIgnoredRecommendations", - "title": "workbench.extensions.action.addToWorkspaceIgnoredRecommendations", - "keywords": [ - "workbench extensions action add to workspace ignored recommendations", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations", - "title": "workbench.extensions.action.addToWorkspaceFolderIgnoredRecommendations", - "keywords": [ - "workbench extensions action add to workspace folder ignored recommendations", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.configureWorkspaceRecommendedExtensions", - "title": "workbench.extensions.action.configureWorkspaceRecommendedExtensions", - "keywords": [ - "workbench extensions action configure workspace recommended extensions", - "workbench extensions" - ] - }, - { - "id": "workbench.extensions.action.manageTrustedPublishers", - "title": "workbench.extensions.action.manageTrustedPublishers", - "keywords": [ - "workbench extensions action manage trusted publishers", - "workbench extensions" - ] - }, - { - "id": "workbench.views.extensions.installed.open", - "title": "workbench.views.extensions.installed.open", - "keywords": [ - "workbench views extensions installed open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.installed.focus", - "title": "workbench.views.extensions.installed.focus", - "keywords": [ - "workbench views extensions installed focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.installed.resetViewLocation", - "title": "workbench.views.extensions.installed.resetViewLocation", - "keywords": [ - "workbench views extensions installed reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.popular.open", - "title": "workbench.views.extensions.popular.open", - "keywords": [ - "workbench views extensions popular open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.popular.focus", - "title": "workbench.views.extensions.popular.focus", - "keywords": [ - "workbench views extensions popular focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.popular.resetViewLocation", - "title": "workbench.views.extensions.popular.resetViewLocation", - "keywords": [ - "workbench views extensions popular reset view location", - "workbench views" - ] - }, - { - "id": "extensions.recommendedList.open", - "title": "extensions.recommendedList.open", - "keywords": [ - "extensions recommended list open", - "extensions recommended" - ] - }, - { - "id": "extensions.recommendedList.focus", - "title": "extensions.recommendedList.focus", - "keywords": [ - "extensions recommended list focus", - "extensions recommended" - ] - }, - { - "id": "extensions.recommendedList.resetViewLocation", - "title": "extensions.recommendedList.resetViewLocation", - "keywords": [ - "extensions recommended list reset view location", - "extensions recommended" - ] - }, - { - "id": "workbench.views.extensions.enabled.open", - "title": "workbench.views.extensions.enabled.open", - "keywords": [ - "workbench views extensions enabled open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.enabled.focus", - "title": "workbench.views.extensions.enabled.focus", - "keywords": [ - "workbench views extensions enabled focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.enabled.resetViewLocation", - "title": "workbench.views.extensions.enabled.resetViewLocation", - "keywords": [ - "workbench views extensions enabled reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.disabled.open", - "title": "workbench.views.extensions.disabled.open", - "keywords": [ - "workbench views extensions disabled open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.disabled.focus", - "title": "workbench.views.extensions.disabled.focus", - "keywords": [ - "workbench views extensions disabled focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.disabled.resetViewLocation", - "title": "workbench.views.extensions.disabled.resetViewLocation", - "keywords": [ - "workbench views extensions disabled reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.marketplace.open", - "title": "workbench.views.extensions.marketplace.open", - "keywords": [ - "workbench views extensions marketplace open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.marketplace.focus", - "title": "workbench.views.extensions.marketplace.focus", - "keywords": [ - "workbench views extensions marketplace focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.marketplace.resetViewLocation", - "title": "workbench.views.extensions.marketplace.resetViewLocation", - "keywords": [ - "workbench views extensions marketplace reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchInstalled.open", - "title": "workbench.views.extensions.searchInstalled.open", - "keywords": [ - "workbench views extensions search installed open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchInstalled.focus", - "title": "workbench.views.extensions.searchInstalled.focus", - "keywords": [ - "workbench views extensions search installed focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchInstalled.resetViewLocation", - "title": "workbench.views.extensions.searchInstalled.resetViewLocation", - "keywords": [ - "workbench views extensions search installed reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchRecentlyUpdated.open", - "title": "workbench.views.extensions.searchRecentlyUpdated.open", - "keywords": [ - "workbench views extensions search recently updated open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchRecentlyUpdated.focus", - "title": "workbench.views.extensions.searchRecentlyUpdated.focus", - "keywords": [ - "workbench views extensions search recently updated focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchRecentlyUpdated.resetViewLocation", - "title": "workbench.views.extensions.searchRecentlyUpdated.resetViewLocation", - "keywords": [ - "workbench views extensions search recently updated reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchEnabled.open", - "title": "workbench.views.extensions.searchEnabled.open", - "keywords": [ - "workbench views extensions search enabled open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchEnabled.focus", - "title": "workbench.views.extensions.searchEnabled.focus", - "keywords": [ - "workbench views extensions search enabled focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchEnabled.resetViewLocation", - "title": "workbench.views.extensions.searchEnabled.resetViewLocation", - "keywords": [ - "workbench views extensions search enabled reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchDisabled.open", - "title": "workbench.views.extensions.searchDisabled.open", - "keywords": [ - "workbench views extensions search disabled open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchDisabled.focus", - "title": "workbench.views.extensions.searchDisabled.focus", - "keywords": [ - "workbench views extensions search disabled focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchDisabled.resetViewLocation", - "title": "workbench.views.extensions.searchDisabled.resetViewLocation", - "keywords": [ - "workbench views extensions search disabled reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchOutdated.open", - "title": "workbench.views.extensions.searchOutdated.open", - "keywords": [ - "workbench views extensions search outdated open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchOutdated.focus", - "title": "workbench.views.extensions.searchOutdated.focus", - "keywords": [ - "workbench views extensions search outdated focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchOutdated.resetViewLocation", - "title": "workbench.views.extensions.searchOutdated.resetViewLocation", - "keywords": [ - "workbench views extensions search outdated reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchBuiltin.open", - "title": "workbench.views.extensions.searchBuiltin.open", - "keywords": [ - "workbench views extensions search builtin open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchBuiltin.focus", - "title": "workbench.views.extensions.searchBuiltin.focus", - "keywords": [ - "workbench views extensions search builtin focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchBuiltin.resetViewLocation", - "title": "workbench.views.extensions.searchBuiltin.resetViewLocation", - "keywords": [ - "workbench views extensions search builtin reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchWorkspaceUnsupported.open", - "title": "workbench.views.extensions.searchWorkspaceUnsupported.open", - "keywords": [ - "workbench views extensions search workspace unsupported open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchWorkspaceUnsupported.focus", - "title": "workbench.views.extensions.searchWorkspaceUnsupported.focus", - "keywords": [ - "workbench views extensions search workspace unsupported focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.searchWorkspaceUnsupported.resetViewLocation", - "title": "workbench.views.extensions.searchWorkspaceUnsupported.resetViewLocation", - "keywords": [ - "workbench views extensions search workspace unsupported reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.workspaceRecommendations.open", - "title": "workbench.views.extensions.workspaceRecommendations.open", - "keywords": [ - "workbench views extensions workspace recommendations open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.workspaceRecommendations.focus", - "title": "workbench.views.extensions.workspaceRecommendations.focus", - "keywords": [ - "workbench views extensions workspace recommendations focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.workspaceRecommendations.resetViewLocation", - "title": "workbench.views.extensions.workspaceRecommendations.resetViewLocation", - "keywords": [ - "workbench views extensions workspace recommendations reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.otherRecommendations.open", - "title": "workbench.views.extensions.otherRecommendations.open", - "keywords": [ - "workbench views extensions other recommendations open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.otherRecommendations.focus", - "title": "workbench.views.extensions.otherRecommendations.focus", - "keywords": [ - "workbench views extensions other recommendations focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.otherRecommendations.resetViewLocation", - "title": "workbench.views.extensions.otherRecommendations.resetViewLocation", - "keywords": [ - "workbench views extensions other recommendations reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinFeatureExtensions.open", - "title": "workbench.views.extensions.builtinFeatureExtensions.open", - "keywords": [ - "workbench views extensions builtin feature extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinFeatureExtensions.focus", - "title": "workbench.views.extensions.builtinFeatureExtensions.focus", - "keywords": [ - "workbench views extensions builtin feature extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinFeatureExtensions.resetViewLocation", - "title": "workbench.views.extensions.builtinFeatureExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions builtin feature extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinThemeExtensions.open", - "title": "workbench.views.extensions.builtinThemeExtensions.open", - "keywords": [ - "workbench views extensions builtin theme extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinThemeExtensions.focus", - "title": "workbench.views.extensions.builtinThemeExtensions.focus", - "keywords": [ - "workbench views extensions builtin theme extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinThemeExtensions.resetViewLocation", - "title": "workbench.views.extensions.builtinThemeExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions builtin theme extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinProgrammingLanguageExtensions.open", - "title": "workbench.views.extensions.builtinProgrammingLanguageExtensions.open", - "keywords": [ - "workbench views extensions builtin programming language extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinProgrammingLanguageExtensions.focus", - "title": "workbench.views.extensions.builtinProgrammingLanguageExtensions.focus", - "keywords": [ - "workbench views extensions builtin programming language extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.builtinProgrammingLanguageExtensions.resetViewLocation", - "title": "workbench.views.extensions.builtinProgrammingLanguageExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions builtin programming language extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.untrustedUnsupportedExtensions.open", - "title": "workbench.views.extensions.untrustedUnsupportedExtensions.open", - "keywords": [ - "workbench views extensions untrusted unsupported extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.untrustedUnsupportedExtensions.focus", - "title": "workbench.views.extensions.untrustedUnsupportedExtensions.focus", - "keywords": [ - "workbench views extensions untrusted unsupported extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.untrustedUnsupportedExtensions.resetViewLocation", - "title": "workbench.views.extensions.untrustedUnsupportedExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions untrusted unsupported extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.untrustedPartiallySupportedExtensions.open", - "title": "workbench.views.extensions.untrustedPartiallySupportedExtensions.open", - "keywords": [ - "workbench views extensions untrusted partially supported extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.untrustedPartiallySupportedExtensions.focus", - "title": "workbench.views.extensions.untrustedPartiallySupportedExtensions.focus", - "keywords": [ - "workbench views extensions untrusted partially supported extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.untrustedPartiallySupportedExtensions.resetViewLocation", - "title": "workbench.views.extensions.untrustedPartiallySupportedExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions untrusted partially supported extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.virtualUnsupportedExtensions.open", - "title": "workbench.views.extensions.virtualUnsupportedExtensions.open", - "keywords": [ - "workbench views extensions virtual unsupported extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.virtualUnsupportedExtensions.focus", - "title": "workbench.views.extensions.virtualUnsupportedExtensions.focus", - "keywords": [ - "workbench views extensions virtual unsupported extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.virtualUnsupportedExtensions.resetViewLocation", - "title": "workbench.views.extensions.virtualUnsupportedExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions virtual unsupported extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.virtualPartiallySupportedExtensions.open", - "title": "workbench.views.extensions.virtualPartiallySupportedExtensions.open", - "keywords": [ - "workbench views extensions virtual partially supported extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.virtualPartiallySupportedExtensions.focus", - "title": "workbench.views.extensions.virtualPartiallySupportedExtensions.focus", - "keywords": [ - "workbench views extensions virtual partially supported extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.virtualPartiallySupportedExtensions.resetViewLocation", - "title": "workbench.views.extensions.virtualPartiallySupportedExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions virtual partially supported extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.deprecatedExtensions.open", - "title": "workbench.views.extensions.deprecatedExtensions.open", - "keywords": [ - "workbench views extensions deprecated extensions open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.deprecatedExtensions.focus", - "title": "workbench.views.extensions.deprecatedExtensions.focus", - "keywords": [ - "workbench views extensions deprecated extensions focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.deprecatedExtensions.resetViewLocation", - "title": "workbench.views.extensions.deprecatedExtensions.resetViewLocation", - "keywords": [ - "workbench views extensions deprecated extensions reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.restartRequired.open", - "title": "workbench.views.extensions.restartRequired.open", - "keywords": [ - "workbench views extensions restart required open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.restartRequired.focus", - "title": "workbench.views.extensions.restartRequired.focus", - "keywords": [ - "workbench views extensions restart required focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.restartRequired.resetViewLocation", - "title": "workbench.views.extensions.restartRequired.resetViewLocation", - "keywords": [ - "workbench views extensions restart required reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.marketplaceAccess.open", - "title": "workbench.views.extensions.marketplaceAccess.open", - "keywords": [ - "workbench views extensions marketplace access open", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.marketplaceAccess.focus", - "title": "workbench.views.extensions.marketplaceAccess.focus", - "keywords": [ - "workbench views extensions marketplace access focus", - "workbench views" - ] - }, - { - "id": "workbench.views.extensions.marketplaceAccess.resetViewLocation", - "title": "workbench.views.extensions.marketplaceAccess.resetViewLocation", - "keywords": [ - "workbench views extensions marketplace access reset view location", - "workbench views" - ] - }, - { - "id": "workbench.agentPlugins.browse", - "title": "workbench.agentPlugins.browse", - "keywords": [ - "workbench agent plugins browse", - "workbench agent" - ] - }, - { - "id": "workbench.agentPlugins.checkForUpdates", - "title": "workbench.agentPlugins.checkForUpdates", - "keywords": [ - "workbench agent plugins check for updates", - "workbench agent" - ] - }, - { - "id": "workbench.agentPlugins.forceUpdate", - "title": "workbench.agentPlugins.forceUpdate", - "keywords": [ - "workbench agent plugins force update", - "workbench agent" - ] - }, - { - "id": "workbench.views.agentPlugins.installed.open", - "title": "workbench.views.agentPlugins.installed.open", - "keywords": [ - "workbench views agent plugins installed open", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.installed.focus", - "title": "workbench.views.agentPlugins.installed.focus", - "keywords": [ - "workbench views agent plugins installed focus", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.installed.resetViewLocation", - "title": "workbench.views.agentPlugins.installed.resetViewLocation", - "keywords": [ - "workbench views agent plugins installed reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.default.marketplace.open", - "title": "workbench.views.agentPlugins.default.marketplace.open", - "keywords": [ - "workbench views agent plugins default marketplace open", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.default.marketplace.focus", - "title": "workbench.views.agentPlugins.default.marketplace.focus", - "keywords": [ - "workbench views agent plugins default marketplace focus", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.default.marketplace.resetViewLocation", - "title": "workbench.views.agentPlugins.default.marketplace.resetViewLocation", - "keywords": [ - "workbench views agent plugins default marketplace reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.marketplace.open", - "title": "workbench.views.agentPlugins.marketplace.open", - "keywords": [ - "workbench views agent plugins marketplace open", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.marketplace.focus", - "title": "workbench.views.agentPlugins.marketplace.focus", - "keywords": [ - "workbench views agent plugins marketplace focus", - "workbench views" - ] - }, - { - "id": "workbench.views.agentPlugins.marketplace.resetViewLocation", - "title": "workbench.views.agentPlugins.marketplace.resetViewLocation", - "keywords": [ - "workbench views agent plugins marketplace reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.installed.open", - "title": "workbench.views.mcp.installed.open", - "keywords": [ - "workbench views mcp installed open", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.installed.focus", - "title": "workbench.views.mcp.installed.focus", - "keywords": [ - "workbench views mcp installed focus", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.installed.resetViewLocation", - "title": "workbench.views.mcp.installed.resetViewLocation", - "keywords": [ - "workbench views mcp installed reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.default.marketplace.open", - "title": "workbench.views.mcp.default.marketplace.open", - "keywords": [ - "workbench views mcp default marketplace open", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.default.marketplace.focus", - "title": "workbench.views.mcp.default.marketplace.focus", - "keywords": [ - "workbench views mcp default marketplace focus", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.default.marketplace.resetViewLocation", - "title": "workbench.views.mcp.default.marketplace.resetViewLocation", - "keywords": [ - "workbench views mcp default marketplace reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.marketplace.open", - "title": "workbench.views.mcp.marketplace.open", - "keywords": [ - "workbench views mcp marketplace open", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.marketplace.focus", - "title": "workbench.views.mcp.marketplace.focus", - "keywords": [ - "workbench views mcp marketplace focus", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.marketplace.resetViewLocation", - "title": "workbench.views.mcp.marketplace.resetViewLocation", - "keywords": [ - "workbench views mcp marketplace reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.default.welcomeView.open", - "title": "workbench.views.mcp.default.welcomeView.open", - "keywords": [ - "workbench views mcp default welcome view open", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.default.welcomeView.focus", - "title": "workbench.views.mcp.default.welcomeView.focus", - "keywords": [ - "workbench views mcp default welcome view focus", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.default.welcomeView.resetViewLocation", - "title": "workbench.views.mcp.default.welcomeView.resetViewLocation", - "keywords": [ - "workbench views mcp default welcome view reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.welcomeView.open", - "title": "workbench.views.mcp.welcomeView.open", - "keywords": [ - "workbench views mcp welcome view open", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.welcomeView.focus", - "title": "workbench.views.mcp.welcomeView.focus", - "keywords": [ - "workbench views mcp welcome view focus", - "workbench views" - ] - }, - { - "id": "workbench.views.mcp.welcomeView.resetViewLocation", - "title": "workbench.views.mcp.welcomeView.resetViewLocation", - "keywords": [ - "workbench views mcp welcome view reset view location", - "workbench views" - ] - }, - { - "id": "workbench.action.showWindowLog", - "title": "workbench.action.showWindowLog", - "keywords": [ - "show window log", - "show window" - ] - }, - { - "id": "workbench.output.action.switchBetweenOutputs", - "title": "workbench.output.action.switchBetweenOutputs", - "keywords": [ - "workbench output action switch between outputs", - "workbench output" - ] - }, - { - "id": "workbench.action.output.show.tasks", - "title": "workbench.action.output.show.tasks", - "keywords": [ - "output show tasks", - "output show" - ] - }, - { - "id": "workbench.action.output.show.agentSessionsOutput", - "title": "workbench.action.output.show.agentSessionsOutput", - "keywords": [ - "output show agent sessions output", - "output show" - ] - }, - { - "id": "workbench.action.output.show.main", - "title": "workbench.action.output.show.main", - "keywords": [ - "output show main", - "output show" - ] - }, - { - "id": "workbench.action.output.show.mcpGateway", - "title": "workbench.action.output.show.mcpGateway", - "keywords": [ - "output show mcp gateway", - "output show" - ] - }, - { - "id": "workbench.action.output.show.shared", - "title": "workbench.action.output.show.shared", - "keywords": [ - "output show shared", - "output show" - ] - }, - { - "id": "workbench.action.output.show.userDataSync", - "title": "workbench.action.output.show.userDataSync", - "keywords": [ - "output show user data sync", - "output show" - ] - }, - { - "id": "workbench.action.output.show.tunnelHostService", - "title": "workbench.action.output.show.tunnelHostService", - "keywords": [ - "output show tunnel host service", - "output show" - ] - }, - { - "id": "workbench.action.output.show.ptyhost", - "title": "workbench.action.output.show.ptyhost", - "keywords": [ - "output show ptyhost", - "output show" - ] - }, - { - "id": "workbench.action.output.show.rendererLog", - "title": "workbench.action.output.show.rendererLog", - "keywords": [ - "output show renderer log", - "output show" - ] - }, - { - "id": "workbench.action.output.show.terminal", - "title": "workbench.action.output.show.terminal", - "keywords": [ - "output show terminal", - "output show" - ] - }, - { - "id": "workbench.action.output.show.textModelChanges", - "title": "workbench.action.output.show.textModelChanges", - "keywords": [ - "output show text model changes", - "output show" - ] - }, - { - "id": "workbench.action.output.show.exthost", - "title": "workbench.action.output.show.exthost", - "keywords": [ - "output show exthost", - "output show" - ] - }, - { - "id": "workbench.action.output.addCompoundLog", - "title": "workbench.action.output.addCompoundLog", - "keywords": [ - "output add compound log", - "output add" - ] - }, - { - "id": "workbench.action.output.remove", - "title": "workbench.action.output.remove", - "keywords": [ - "output remove" - ] - }, - { - "id": "workbench.action.showOutputChannels", - "title": "workbench.action.showOutputChannels", - "keywords": [ - "show output channels", - "show output" - ] - }, - { - "id": "workbench.output.action.clearOutput", - "title": "workbench.output.action.clearOutput", - "keywords": [ - "workbench output action clear output", - "workbench output" - ] - }, - { - "id": "workbench.output.action.toggleAutoScroll", - "title": "workbench.output.action.toggleAutoScroll", - "keywords": [ - "workbench output action toggle auto scroll", - "workbench output" - ] - }, - { - "id": "workbench.action.openActiveLogOutputFile", - "title": "workbench.action.openActiveLogOutputFile", - "keywords": [ - "open active log output file", - "open active" - ] - }, - { - "id": "workbench.action.openActiveLogOutputFileInNewWindow", - "title": "workbench.action.openActiveLogOutputFileInNewWindow", - "keywords": [ - "open active log output file in new window", - "open active" - ] - }, - { - "id": "workbench.action.saveActiveLogOutputAs", - "title": "workbench.action.saveActiveLogOutputAs", - "keywords": [ - "save active log output as", - "save active" - ] - }, - { - "id": "workbench.action.showLogs", - "title": "workbench.action.showLogs", - "keywords": [ - "show logs" - ] - }, - { - "id": "workbench.action.openLogFile", - "title": "workbench.action.openLogFile", - "keywords": [ - "open log file", - "open log" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevel.1", - "title": "workbench.action.output.activeOutputLogLevel.1", - "keywords": [ - "output active output log level 1", - "output active" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevel.2", - "title": "workbench.action.output.activeOutputLogLevel.2", - "keywords": [ - "output active output log level 2", - "output active" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevel.3", - "title": "workbench.action.output.activeOutputLogLevel.3", - "keywords": [ - "output active output log level 3", - "output active" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevel.4", - "title": "workbench.action.output.activeOutputLogLevel.4", - "keywords": [ - "output active output log level 4", - "output active" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevel.5", - "title": "workbench.action.output.activeOutputLogLevel.5", - "keywords": [ - "output active output log level 5", - "output active" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevel.0", - "title": "workbench.action.output.activeOutputLogLevel.0", - "keywords": [ - "output active output log level 0", - "output active" - ] - }, - { - "id": "workbench.action.output.activeOutputLogLevelDefault", - "title": "workbench.action.output.activeOutputLogLevelDefault", - "keywords": [ - "output active output log level default", - "output active" - ] - }, - { - "id": "workbench.actions.workbench.panel.output.toggle.trace", - "title": "workbench.actions.workbench.panel.output.toggle.trace", - "keywords": [ - "workbench actions workbench panel output toggle trace", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.output.toggle.debug", - "title": "workbench.actions.workbench.panel.output.toggle.debug", - "keywords": [ - "workbench actions workbench panel output toggle debug", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.output.toggle.info", - "title": "workbench.actions.workbench.panel.output.toggle.info", - "keywords": [ - "workbench actions workbench panel output toggle info", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.output.toggle.warn", - "title": "workbench.actions.workbench.panel.output.toggle.warn", - "keywords": [ - "workbench actions workbench panel output toggle warn", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.output.toggle.error", - "title": "workbench.actions.workbench.panel.output.toggle.error", - "keywords": [ - "workbench actions workbench panel output toggle error", - "workbench actions" - ] - }, - { - "id": "workbench.actions.workbench.panel.output.clearFilterText", - "title": "workbench.actions.workbench.panel.output.clearFilterText", - "keywords": [ - "workbench actions workbench panel output clear filter text", - "workbench actions" - ] - }, - { - "id": "workbench.action.exportLogs", - "title": "workbench.action.exportLogs", - "keywords": [ - "export logs" - ] - }, - { - "id": "workbench.action.importLog", - "title": "workbench.action.importLog", - "keywords": [ - "import log" - ] - }, - { - "id": "~remote.forwardedPortsContainer.resetViewContainerLocation", - "title": "~remote.forwardedPortsContainer.resetViewContainerLocation", - "keywords": [ - "~remote forwarded ports container reset view container location", - "~remote forwarded" - ] - }, - { - "id": "~remote.forwardedPortsContainer", - "title": "~remote.forwardedPortsContainer", - "keywords": [ - "~remote forwarded ports container", - "~remote forwarded" - ] - }, - { - "id": "update.check", - "title": "update.check", - "keywords": [ - "update check" - ] - }, - { - "id": "update.checking", - "title": "update.checking", - "keywords": [ - "update checking" - ] - }, - { - "id": "update.downloadNow", - "title": "update.downloadNow", - "keywords": [ - "update download now", - "update download" - ] - }, - { - "id": "update.downloading", - "title": "update.downloading", - "keywords": [ - "update downloading" - ] - }, - { - "id": "update.install", - "title": "update.install", - "keywords": [ - "update install" - ] - }, - { - "id": "update.updating", - "title": "update.updating", - "keywords": [ - "update updating" - ] - }, - { - "id": "update.restart", - "title": "update.restart", - "keywords": [ - "update restart" - ] - }, - { - "id": "update.showUpdateReleaseNotes", - "title": "update.showUpdateReleaseNotes", - "keywords": [ - "update show update release notes", - "update show" - ] - }, - { - "id": "~remote.forwardedPorts.open", - "title": "~remote.forwardedPorts.open", - "keywords": [ - "~remote forwarded ports open", - "~remote forwarded" - ] - }, - { - "id": "~remote.forwardedPorts.focus", - "title": "~remote.forwardedPorts.focus", - "keywords": [ - "~remote forwarded ports focus", - "~remote forwarded" - ] - }, - { - "id": "~remote.forwardedPorts.resetViewLocation", - "title": "~remote.forwardedPorts.resetViewLocation", - "keywords": [ - "~remote forwarded ports reset view location", - "~remote forwarded" - ] - }, - { - "id": "~remote.forwardedPorts.toggleVisibility", - "title": "~remote.forwardedPorts.toggleVisibility", - "keywords": [ - "~remote forwarded ports toggle visibility", - "~remote forwarded" - ] - }, - { - "id": "~remote.forwardedPorts.removeView", - "title": "~remote.forwardedPorts.removeView", - "keywords": [ - "~remote forwarded ports remove view", - "~remote forwarded" - ] - }, - { - "id": "workbench.action.manageAccounts", - "title": "workbench.action.manageAccounts", - "keywords": [ - "manage accounts" - ] - }, - { - "id": "workbench.action.removeDynamicAuthenticationProviders", - "title": "workbench.action.removeDynamicAuthenticationProviders", - "keywords": [ - "remove dynamic authentication providers", - "remove dynamic" - ] - }, - { - "id": "workbench.userDataSync.actions.turnOn", - "title": "workbench.userDataSync.actions.turnOn", - "keywords": [ - "workbench user data sync actions turn on", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.turnOff", - "title": "workbench.userDataSync.actions.turnOff", - "keywords": [ - "workbench user data sync actions turn off", - "workbench user" - ] - }, - { - "id": "workbench.userData.actions.turningOn", - "title": "workbench.userData.actions.turningOn", - "keywords": [ - "workbench user data actions turning on", - "workbench user" - ] - }, - { - "id": "workbench.userData.actions.cancelTurnOn", - "title": "workbench.userData.actions.cancelTurnOn", - "keywords": [ - "workbench user data actions cancel turn on", - "workbench user" - ] - }, - { - "id": "workbench.userData.actions.signin", - "title": "workbench.userData.actions.signin", - "keywords": [ - "workbench user data actions signin", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.showConflicts", - "title": "workbench.userDataSync.actions.showConflicts", - "keywords": [ - "workbench user data sync actions show conflicts", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.showSyncedData", - "title": "workbench.userDataSync.actions.showSyncedData", - "keywords": [ - "workbench user data sync actions show synced data", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.manage", - "title": "workbench.userDataSync.actions.manage", - "keywords": [ - "workbench user data sync actions manage", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.syncNow", - "title": "workbench.userDataSync.actions.syncNow", - "keywords": [ - "workbench user data sync actions sync now", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.configure", - "title": "workbench.userDataSync.actions.configure", - "keywords": [ - "workbench user data sync actions configure", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.settings", - "title": "workbench.userDataSync.actions.settings", - "keywords": [ - "workbench user data sync actions settings", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.help", - "title": "workbench.userDataSync.actions.help", - "keywords": [ - "workbench user data sync actions help", - "workbench user" - ] - }, - { - "id": "workbench.userDataSync.actions.showLog", - "title": "workbench.userDataSync.actions.showLog", - "keywords": [ - "workbench user data sync actions show log", - "workbench user" - ] - }, - { - "id": "workbench.actions.syncData.reset", - "title": "workbench.actions.syncData.reset", - "keywords": [ - "workbench actions sync data reset", - "workbench actions" - ] - }, - { - "id": "workbench.userDataSync.actions.acceptMerges", - "title": "workbench.userDataSync.actions.acceptMerges", - "keywords": [ - "workbench user data sync actions accept merges", - "workbench user" - ] - }, - { - "id": "workbench.view.sync.resetViewContainerLocation", - "title": "workbench.view.sync.resetViewContainerLocation", - "keywords": [ - "workbench view sync reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.view.sync", - "title": "workbench.view.sync", - "keywords": [ - "workbench view sync", - "workbench view" - ] - }, - { - "id": "workbench.views.sync.conflicts.open", - "title": "workbench.views.sync.conflicts.open", - "keywords": [ - "workbench views sync conflicts open", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.conflicts.focus", - "title": "workbench.views.sync.conflicts.focus", - "keywords": [ - "workbench views sync conflicts focus", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.conflicts.resetViewLocation", - "title": "workbench.views.sync.conflicts.resetViewLocation", - "keywords": [ - "workbench views sync conflicts reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.remoteActivity.open", - "title": "workbench.views.sync.remoteActivity.open", - "keywords": [ - "workbench views sync remote activity open", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.remoteActivity.focus", - "title": "workbench.views.sync.remoteActivity.focus", - "keywords": [ - "workbench views sync remote activity focus", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.remoteActivity.resetViewLocation", - "title": "workbench.views.sync.remoteActivity.resetViewLocation", - "keywords": [ - "workbench views sync remote activity reset view location", - "workbench views" - ] - }, - { - "id": "workbench.actions.sync.workbench.views.sync.remoteActivity.resolveResource", - "title": "workbench.actions.sync.workbench.views.sync.remoteActivity.resolveResource", - "keywords": [ - "workbench actions sync workbench views sync remote activity resolve resource", - "workbench actions" - ] - }, - { - "id": "workbench.actions.sync.workbench.views.sync.remoteActivity.compareWithLocal", - "title": "workbench.actions.sync.workbench.views.sync.remoteActivity.compareWithLocal", - "keywords": [ - "workbench actions sync workbench views sync remote activity compare with local", - "workbench actions" - ] - }, - { - "id": "workbench.actions.sync.workbench.views.sync.remoteActivity.replaceCurrent", - "title": "workbench.actions.sync.workbench.views.sync.remoteActivity.replaceCurrent", - "keywords": [ - "workbench actions sync workbench views sync remote activity replace current", - "workbench actions" - ] - }, - { - "id": "workbench.views.sync.machines.open", - "title": "workbench.views.sync.machines.open", - "keywords": [ - "workbench views sync machines open", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.machines.focus", - "title": "workbench.views.sync.machines.focus", - "keywords": [ - "workbench views sync machines focus", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.machines.resetViewLocation", - "title": "workbench.views.sync.machines.resetViewLocation", - "keywords": [ - "workbench views sync machines reset view location", - "workbench views" - ] - }, - { - "id": "workbench.actions.sync.editMachineName", - "title": "workbench.actions.sync.editMachineName", - "keywords": [ - "workbench actions sync edit machine name", - "workbench actions" - ] - }, - { - "id": "workbench.actions.sync.turnOffSyncOnMachine", - "title": "workbench.actions.sync.turnOffSyncOnMachine", - "keywords": [ - "workbench actions sync turn off sync on machine", - "workbench actions" - ] - }, - { - "id": "workbench.views.sync.localActivity.open", - "title": "workbench.views.sync.localActivity.open", - "keywords": [ - "workbench views sync local activity open", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.localActivity.focus", - "title": "workbench.views.sync.localActivity.focus", - "keywords": [ - "workbench views sync local activity focus", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.localActivity.resetViewLocation", - "title": "workbench.views.sync.localActivity.resetViewLocation", - "keywords": [ - "workbench views sync local activity reset view location", - "workbench views" - ] - }, - { - "id": "workbench.actions.sync.workbench.views.sync.localActivity.resolveResource", - "title": "workbench.actions.sync.workbench.views.sync.localActivity.resolveResource", - "keywords": [ - "workbench actions sync workbench views sync local activity resolve resource", - "workbench actions" - ] - }, - { - "id": "workbench.actions.sync.workbench.views.sync.localActivity.compareWithLocal", - "title": "workbench.actions.sync.workbench.views.sync.localActivity.compareWithLocal", - "keywords": [ - "workbench actions sync workbench views sync local activity compare with local", - "workbench actions" - ] - }, - { - "id": "workbench.actions.sync.workbench.views.sync.localActivity.replaceCurrent", - "title": "workbench.actions.sync.workbench.views.sync.localActivity.replaceCurrent", - "keywords": [ - "workbench actions sync workbench views sync local activity replace current", - "workbench actions" - ] - }, - { - "id": "workbench.views.sync.troubleshoot.open", - "title": "workbench.views.sync.troubleshoot.open", - "keywords": [ - "workbench views sync troubleshoot open", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.troubleshoot.focus", - "title": "workbench.views.sync.troubleshoot.focus", - "keywords": [ - "workbench views sync troubleshoot focus", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.troubleshoot.resetViewLocation", - "title": "workbench.views.sync.troubleshoot.resetViewLocation", - "keywords": [ - "workbench views sync troubleshoot reset view location", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.externalActivity.open", - "title": "workbench.views.sync.externalActivity.open", - "keywords": [ - "workbench views sync external activity open", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.externalActivity.focus", - "title": "workbench.views.sync.externalActivity.focus", - "keywords": [ - "workbench views sync external activity focus", - "workbench views" - ] - }, - { - "id": "workbench.views.sync.externalActivity.resetViewLocation", - "title": "workbench.views.sync.externalActivity.resetViewLocation", - "keywords": [ - "workbench views sync external activity reset view location", - "workbench views" - ] - }, - { - "id": "workbench.actions.sync.loadActivity", - "title": "workbench.actions.sync.loadActivity", - "keywords": [ - "workbench actions sync load activity", - "workbench actions" - ] - }, - { - "id": "workbench.editSessions.actions.signIn", - "title": "workbench.editSessions.actions.signIn", - "keywords": [ - "workbench edit sessions actions sign in", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.resetAuth", - "title": "workbench.editSessions.actions.resetAuth", - "keywords": [ - "workbench edit sessions actions reset auth", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.resumeLatest", - "title": "workbench.editSessions.actions.resumeLatest", - "keywords": [ - "workbench edit sessions actions resume latest", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.resumeFromSerializedPayload", - "title": "workbench.editSessions.actions.resumeFromSerializedPayload", - "keywords": [ - "workbench edit sessions actions resume from serialized payload", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.storeCurrent", - "title": "workbench.editSessions.actions.storeCurrent", - "keywords": [ - "workbench edit sessions actions store current", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.showEditSessions", - "title": "workbench.editSessions.actions.showEditSessions", - "keywords": [ - "workbench edit sessions actions show edit sessions", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.showOutputChannel", - "title": "workbench.editSessions.actions.showOutputChannel", - "keywords": [ - "workbench edit sessions actions show output channel", - "workbench edit" - ] - }, - { - "id": "workbench.view.editSessions.resetViewContainerLocation", - "title": "workbench.view.editSessions.resetViewContainerLocation", - "keywords": [ - "workbench view edit sessions reset view container location", - "workbench view" - ] - }, - { - "id": "workbench.views.editSessions.data.open", - "title": "workbench.views.editSessions.data.open", - "keywords": [ - "workbench views edit sessions data open", - "workbench views" - ] - }, - { - "id": "workbench.views.editSessions.data.focus", - "title": "workbench.views.editSessions.data.focus", - "keywords": [ - "workbench views edit sessions data focus", - "workbench views" - ] - }, - { - "id": "workbench.views.editSessions.data.resetViewLocation", - "title": "workbench.views.editSessions.data.resetViewLocation", - "keywords": [ - "workbench views edit sessions data reset view location", - "workbench views" - ] - }, - { - "id": "workbench.editSessions.actions.resume", - "title": "workbench.editSessions.actions.resume", - "keywords": [ - "workbench edit sessions actions resume", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.store", - "title": "workbench.editSessions.actions.store", - "keywords": [ - "workbench edit sessions actions store", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.delete", - "title": "workbench.editSessions.actions.delete", - "keywords": [ - "workbench edit sessions actions delete", - "workbench edit" - ] - }, - { - "id": "workbench.editSessions.actions.deleteAll", - "title": "workbench.editSessions.actions.deleteAll", - "keywords": [ - "workbench edit sessions actions delete all", - "workbench edit" - ] - }, - { - "id": "makefile.outline.toggleVisibility", - "title": "makefile.outline.toggleVisibility", - "keywords": [ - "makefile outline toggle visibility", - "makefile outline" - ] - }, - { - "id": "makefile.outline.removeView", - "title": "makefile.outline.removeView", - "keywords": [ - "makefile outline remove view", - "makefile outline" - ] - }, - { - "id": "workbench.extensions.action.openExtensionsFolder", - "title": "workbench.extensions.action.openExtensionsFolder", - "keywords": [ - "workbench extensions action open extensions folder", - "workbench extensions" - ] - }, - { - "id": "workbench.action.openIssueReporter", - "title": "workbench.action.openIssueReporter", - "keywords": [ - "open issue reporter", - "open issue" - ] - }, - { - "id": "vscode.openIssueReporter", - "title": "vscode.openIssueReporter", - "keywords": [ - "vscode open issue reporter", - "vscode open" - ] - }, - { - "id": "workbench.action.reportPerformanceIssueUsingReporter", - "title": "workbench.action.reportPerformanceIssueUsingReporter", - "keywords": [ - "report performance issue using reporter", - "report performance" - ] - }, - { - "id": "workbench.action.output.show.remoteTunnelService", - "title": "workbench.action.output.show.remoteTunnelService", - "keywords": [ - "output show remote tunnel service", - "output show" - ] - }, - { - "id": "workbench.remoteTunnel.actions.turnOn", - "title": "workbench.remoteTunnel.actions.turnOn", - "keywords": [ - "workbench remote tunnel actions turn on", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.manage", - "title": "workbench.remoteTunnel.actions.manage", - "keywords": [ - "workbench remote tunnel actions manage", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.connecting", - "title": "workbench.remoteTunnel.actions.connecting", - "keywords": [ - "workbench remote tunnel actions connecting", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.turnOff", - "title": "workbench.remoteTunnel.actions.turnOff", - "keywords": [ - "workbench remote tunnel actions turn off", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.showLog", - "title": "workbench.remoteTunnel.actions.showLog", - "keywords": [ - "workbench remote tunnel actions show log", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.configure", - "title": "workbench.remoteTunnel.actions.configure", - "keywords": [ - "workbench remote tunnel actions configure", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.copyToClipboard", - "title": "workbench.remoteTunnel.actions.copyToClipboard", - "keywords": [ - "workbench remote tunnel actions copy to clipboard", - "workbench remote" - ] - }, - { - "id": "workbench.remoteTunnel.actions.learnMore", - "title": "workbench.remoteTunnel.actions.learnMore", - "keywords": [ - "workbench remote tunnel actions learn more", - "workbench remote" - ] - }, - { - "id": "keywordActivation.status.command", - "title": "keywordActivation.status.command", - "keywords": [ - "keyword activation status command", - "keyword activation" - ] - }, - { - "id": "chat.installRecommendation.openai.chatgpt.codex", - "title": "chat.installRecommendation.openai.chatgpt.codex", - "keywords": [ - "chat install recommendation openai chatgpt codex", - "chat install" - ] - }, - { - "id": "chat.installRecommendation.github.copilot-chat.cli", - "title": "chat.installRecommendation.github.copilot-chat.cli", - "keywords": [ - "chat install recommendation github copilot chat cli", - "chat install" - ] - }, - { - "id": "chat.installRecommendation.github.copilot-chat.cloud", - "title": "chat.installRecommendation.github.copilot-chat.cloud", - "keywords": [ - "chat install recommendation github copilot chat cloud", - "chat install" - ] - }, - { - "id": "workbench.extensions.installMissingDependencies", - "title": "workbench.extensions.installMissingDependencies", - "keywords": [ - "workbench extensions install missing dependencies", - "workbench extensions" - ] - }, - { - "id": "workbench.action.toggleLockedScrolling", - "title": "workbench.action.toggleLockedScrolling", - "keywords": [ - "toggle locked scrolling", - "toggle locked" - ] - }, - { - "id": "workbench.action.holdLockedScrolling", - "title": "workbench.action.holdLockedScrolling", - "keywords": [ - "hold locked scrolling", - "hold locked" - ] - }, - { - "id": "workbench.action.configureLocale", - "title": "workbench.action.configureLocale", - "keywords": [ - "configure locale" - ] - }, - { - "id": "workbench.action.clearLocalePreference", - "title": "workbench.action.clearLocalePreference", - "keywords": [ - "clear locale preference", - "clear locale" - ] - }, - { - "id": "workbench.action.extensionHostProfiler.stop", - "title": "workbench.action.extensionHostProfiler.stop", - "keywords": [ - "extension host profiler stop", - "extension host" - ] - }, - { - "id": "rainbow-csv.CSVLint", - "title": "rainbow-csv.CSVLint", - "keywords": [ - "rainbow csv csvlint", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.RBQL", - "title": "rainbow-csv.RBQL", - "keywords": [ - "rainbow csv rbql", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.SetHeaderLine", - "title": "rainbow-csv.SetHeaderLine", - "keywords": [ - "rainbow csv set header line", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.SetCommentPrefix", - "title": "rainbow-csv.SetCommentPrefix", - "keywords": [ - "rainbow csv set comment prefix", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.SetVirtualHeader", - "title": "rainbow-csv.SetVirtualHeader", - "keywords": [ - "rainbow csv set virtual header", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.SetJoinTableName", - "title": "rainbow-csv.SetJoinTableName", - "keywords": [ - "rainbow csv set join table name", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.ColumnEditBefore", - "title": "rainbow-csv.ColumnEditBefore", - "keywords": [ - "rainbow csv column edit before", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.ColumnEditAfter", - "title": "rainbow-csv.ColumnEditAfter", - "keywords": [ - "rainbow csv column edit after", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.ColumnEditSelect", - "title": "rainbow-csv.ColumnEditSelect", - "keywords": [ - "rainbow csv column edit select", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.RainbowSeparator", - "title": "rainbow-csv.RainbowSeparator", - "keywords": [ - "rainbow csv rainbow separator", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.RainbowSeparatorOff", - "title": "rainbow-csv.RainbowSeparatorOff", - "keywords": [ - "rainbow csv rainbow separator off", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.RainbowSeparatorOn", - "title": "rainbow-csv.RainbowSeparatorOn", - "keywords": [ - "rainbow csv rainbow separator on", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.SampleHead", - "title": "rainbow-csv.SampleHead", - "keywords": [ - "rainbow csv sample head", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.SampleTail", - "title": "rainbow-csv.SampleTail", - "keywords": [ - "rainbow csv sample tail", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.Align", - "title": "rainbow-csv.Align", - "keywords": [ - "rainbow csv align", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.VirtualAlign", - "title": "rainbow-csv.VirtualAlign", - "keywords": [ - "rainbow csv virtual align", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.Shrink", - "title": "rainbow-csv.Shrink", - "keywords": [ - "rainbow csv shrink", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.VirtualShrink", - "title": "rainbow-csv.VirtualShrink", - "keywords": [ - "rainbow csv virtual shrink", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.CopyBack", - "title": "rainbow-csv.CopyBack", - "keywords": [ - "rainbow csv copy back", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.ToggleRowBackground", - "title": "rainbow-csv.ToggleRowBackground", - "keywords": [ - "rainbow csv toggle row background", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.ToggleColumnTracking", - "title": "rainbow-csv.ToggleColumnTracking", - "keywords": [ - "rainbow csv toggle column tracking", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.ExcelCopy", - "title": "rainbow-csv.ExcelCopy", - "keywords": [ - "rainbow csv excel copy", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.MarkdownCopy", - "title": "rainbow-csv.MarkdownCopy", - "keywords": [ - "rainbow csv markdown copy", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.InternalTest", - "title": "rainbow-csv.InternalTest", - "keywords": [ - "rainbow csv internal test", - "rainbow csv" - ] - }, - { - "id": "rainbow-csv.GoToColumn", - "title": "rainbow-csv.GoToColumn", - "keywords": [ - "rainbow csv go to column", - "rainbow csv" - ] - }, - { - "id": "editor.emmet.action.updateImageSize", - "title": "editor.emmet.action.updateImageSize", - "keywords": [ - "editor emmet action update image size", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.wrapWithAbbreviation", - "title": "editor.emmet.action.wrapWithAbbreviation", - "keywords": [ - "editor emmet action wrap with abbreviation", - "editor emmet" - ] - }, - { - "id": "emmet.expandAbbreviation", - "title": "emmet.expandAbbreviation", - "keywords": [ - "emmet expand abbreviation", - "emmet expand" - ] - }, - { - "id": "editor.emmet.action.removeTag", - "title": "editor.emmet.action.removeTag", - "keywords": [ - "editor emmet action remove tag", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.updateTag", - "title": "editor.emmet.action.updateTag", - "keywords": [ - "editor emmet action update tag", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.matchTag", - "title": "editor.emmet.action.matchTag", - "keywords": [ - "editor emmet action match tag", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.balanceOut", - "title": "editor.emmet.action.balanceOut", - "keywords": [ - "editor emmet action balance out", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.balanceIn", - "title": "editor.emmet.action.balanceIn", - "keywords": [ - "editor emmet action balance in", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.splitJoinTag", - "title": "editor.emmet.action.splitJoinTag", - "keywords": [ - "editor emmet action split join tag", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.mergeLines", - "title": "editor.emmet.action.mergeLines", - "keywords": [ - "editor emmet action merge lines", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.toggleComment", - "title": "editor.emmet.action.toggleComment", - "keywords": [ - "editor emmet action toggle comment", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.nextEditPoint", - "title": "editor.emmet.action.nextEditPoint", - "keywords": [ - "editor emmet action next edit point", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.prevEditPoint", - "title": "editor.emmet.action.prevEditPoint", - "keywords": [ - "editor emmet action prev edit point", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.selectNextItem", - "title": "editor.emmet.action.selectNextItem", - "keywords": [ - "editor emmet action select next item", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.selectPrevItem", - "title": "editor.emmet.action.selectPrevItem", - "keywords": [ - "editor emmet action select prev item", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.evaluateMathExpression", - "title": "editor.emmet.action.evaluateMathExpression", - "keywords": [ - "editor emmet action evaluate math expression", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.incrementNumberByOneTenth", - "title": "editor.emmet.action.incrementNumberByOneTenth", - "keywords": [ - "editor emmet action increment number by one tenth", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.incrementNumberByOne", - "title": "editor.emmet.action.incrementNumberByOne", - "keywords": [ - "editor emmet action increment number by one", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.incrementNumberByTen", - "title": "editor.emmet.action.incrementNumberByTen", - "keywords": [ - "editor emmet action increment number by ten", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.decrementNumberByOneTenth", - "title": "editor.emmet.action.decrementNumberByOneTenth", - "keywords": [ - "editor emmet action decrement number by one tenth", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.decrementNumberByOne", - "title": "editor.emmet.action.decrementNumberByOne", - "keywords": [ - "editor emmet action decrement number by one", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.decrementNumberByTen", - "title": "editor.emmet.action.decrementNumberByTen", - "keywords": [ - "editor emmet action decrement number by ten", - "editor emmet" - ] - }, - { - "id": "editor.emmet.action.reflectCSSValue", - "title": "editor.emmet.action.reflectCSSValue", - "keywords": [ - "editor emmet action reflect cssvalue", - "editor emmet" - ] - }, - { - "id": "workbench.action.showEmmetCommands", - "title": "workbench.action.showEmmetCommands", - "keywords": [ - "show emmet commands", - "show emmet" - ] - }, - { - "id": "extension.pickNativeProcess", - "title": "extension.pickNativeProcess", - "keywords": [ - "pick native process", - "pick native" - ] - }, - { - "id": "extension.pickRemoteNativeProcess", - "title": "extension.pickRemoteNativeProcess", - "keywords": [ - "pick remote native process", - "pick remote" - ] - }, - { - "id": "C_Cpp.BuildAndDebugFile", - "title": "C_Cpp.BuildAndDebugFile", - "keywords": [ - "c cpp build and debug file", - "c cpp" - ] - }, - { - "id": "C_Cpp.BuildAndRunFile", - "title": "C_Cpp.BuildAndRunFile", - "keywords": [ - "c cpp build and run file", - "c cpp" - ] - }, - { - "id": "C_Cpp.AddDebugConfiguration", - "title": "C_Cpp.AddDebugConfiguration", - "keywords": [ - "c cpp add debug configuration", - "c cpp" - ] - }, - { - "id": "workbench.action.output.show.vscode.github-authentication.GitHub Authentication.log", - "title": "workbench.action.output.show.vscode.github-authentication.GitHub Authentication.log", - "keywords": [ - "output show vscode github authentication git hub authentication log", - "output show" - ] - }, - { - "id": "workbench.action.output.show.mechatroner.rainbow-csv.rainbow_csv_debug_channel.log", - "title": "workbench.action.output.show.mechatroner.rainbow-csv.rainbow_csv_debug_channel.log", - "keywords": [ - "output show mechatroner rainbow csv rainbow csv debug channel log", - "output show" - ] - }, - { - "id": "workbench.action.output.show.GitHub.copilot-chat.GitHub Copilot Chat.log", - "title": "workbench.action.output.show.GitHub.copilot-chat.GitHub Copilot Chat.log", - "keywords": [ - "output show git hub copilot chat git hub copilot chat log", - "output show" - ] - }, - { - "id": "git-base.api.getRemoteSources", - "title": "git-base.api.getRemoteSources", - "keywords": [ - "git base api get remote sources", - "git base" - ] - }, - { - "id": "workbench.action.output.show.vscode.microsoft-authentication.Microsoft Authentication.log", - "title": "workbench.action.output.show.vscode.microsoft-authentication.Microsoft Authentication.log", - "keywords": [ - "output show vscode microsoft authentication microsoft authentication log", - "output show" - ] - }, - { - "id": "streamdeck.reconnectToServer", - "title": "streamdeck.reconnectToServer", - "keywords": [ - "streamdeck reconnect to server", - "streamdeck reconnect" - ] - }, - { - "id": "streamdeck.activateSession", - "title": "streamdeck.activateSession", - "keywords": [ - "streamdeck activate session", - "streamdeck activate" - ] - }, - { - "id": "todohighlight.toggleHighlight", - "title": "todohighlight.toggleHighlight", - "keywords": [ - "todohighlight toggle highlight", - "todohighlight toggle" - ] - }, - { - "id": "todohighlight.listAnnotations", - "title": "todohighlight.listAnnotations", - "keywords": [ - "todohighlight list annotations", - "todohighlight list" - ] - }, - { - "id": "todohighlight.showOutputChannel", - "title": "todohighlight.showOutputChannel", - "keywords": [ - "todohighlight show output channel", - "todohighlight show" - ] - }, - { - "id": "C_Cpp.addSshTarget", - "title": "C_Cpp.addSshTarget", - "keywords": [ - "c cpp add ssh target", - "c cpp" - ] - }, - { - "id": "C_Cpp.removeSshTarget", - "title": "C_Cpp.removeSshTarget", - "keywords": [ - "c cpp remove ssh target", - "c cpp" - ] - }, - { - "id": "C_Cpp.refreshCppSshTargetsView", - "title": "C_Cpp.refreshCppSshTargetsView", - "keywords": [ - "c cpp refresh cpp ssh targets view", - "c cpp" - ] - }, - { - "id": "C_Cpp.setActiveSshTarget", - "title": "C_Cpp.setActiveSshTarget", - "keywords": [ - "c cpp set active ssh target", - "c cpp" - ] - }, - { - "id": "C_Cpp.selectSshTarget", - "title": "C_Cpp.selectSshTarget", - "keywords": [ - "c cpp select ssh target", - "c cpp" - ] - }, - { - "id": "C_Cpp.selectActiveSshTarget", - "title": "C_Cpp.selectActiveSshTarget", - "keywords": [ - "c cpp select active ssh target", - "c cpp" - ] - }, - { - "id": "C_Cpp.activeSshTarget", - "title": "C_Cpp.activeSshTarget", - "keywords": [ - "c cpp active ssh target", - "c cpp" - ] - }, - { - "id": "C_Cpp.sshTerminal", - "title": "C_Cpp.sshTerminal", - "keywords": [ - "c cpp ssh terminal", - "c cpp" - ] - }, - { - "id": "workbench.action.output.show.extension-output-ms-vscode.cpptools-#1-remote-attach", - "title": "workbench.action.output.show.extension-output-ms-vscode.cpptools-#1-remote-attach", - "keywords": [ - "output show extension output ms vscode cpptools #1 remote attach", - "output show" - ] - }, - { - "id": "workbench.action.output.show.extension-output-formulahendry.auto-rename-tag-#1-Auto Rename Tag", - "title": "workbench.action.output.show.extension-output-formulahendry.auto-rename-tag-#1-Auto Rename Tag", - "keywords": [ - "output show extension output formulahendry auto rename tag #1 auto rename tag", - "output show" - ] - }, - { - "id": "workbench.action.output.show.extension-output-nicollasr.vscode-streamdeck-#1-Stream Deck", - "title": "workbench.action.output.show.extension-output-nicollasr.vscode-streamdeck-#1-Stream Deck", - "keywords": [ - "output show extension output nicollasr vscode streamdeck #1 stream deck", - "output show" - ] - }, - { - "id": "workbench.action.output.show.extension-output-wayou.vscode-todo-highlight-#1-TodoHighlight", - "title": "workbench.action.output.show.extension-output-wayou.vscode-todo-highlight-#1-TodoHighlight", - "keywords": [ - "output show extension output wayou vscode todo highlight #1 todo highlight", - "output show" - ] - }, - { - "id": "workbench.action.output.show.vscode.git.Git.log", - "title": "workbench.action.output.show.vscode.git.Git.log", - "keywords": [ - "output show vscode git git log", - "output show" - ] - }, - { - "id": "workbench.action.output.show.vscode.github.GitHub.log", - "title": "workbench.action.output.show.vscode.github.GitHub.log", - "keywords": [ - "output show vscode github git hub log", - "output show" - ] - }, - { - "id": "github.copilot.chat.attachFile", - "title": "github.copilot.chat.attachFile", - "keywords": [ - "github copilot chat attach file", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.attachSelection", - "title": "github.copilot.chat.attachSelection", - "keywords": [ - "github copilot chat attach selection", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.extensionState", - "title": "github.copilot.debug.extensionState", - "keywords": [ - "github copilot debug extension state", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.triggerPermissiveSignIn", - "title": "github.copilot.chat.triggerPermissiveSignIn", - "keywords": [ - "github copilot chat trigger permissive sign in", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.collectDiagnostics", - "title": "github.copilot.debug.collectDiagnostics", - "keywords": [ - "github copilot debug collect diagnostics", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.collectDiagnostics.internal", - "title": "github.copilot.debug.collectDiagnostics.internal", - "keywords": [ - "github copilot debug collect diagnostics internal", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.showOutputChannel.internal", - "title": "github.copilot.debug.showOutputChannel.internal", - "keywords": [ - "github copilot debug show output channel internal", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.togglePowerSaveBlocker", - "title": "github.copilot.debug.togglePowerSaveBlocker", - "keywords": [ - "github copilot debug toggle power save blocker", - "github copilot" - ] - }, - { - "id": "github.copilot.refreshToken", - "title": "github.copilot.refreshToken", - "keywords": [ - "github copilot refresh token", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.showChatLogView", - "title": "github.copilot.debug.showChatLogView", - "keywords": [ - "github copilot debug show chat log view", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.startCopilotDebugCommand", - "title": "github.copilot.chat.startCopilotDebugCommand", - "keywords": [ - "github copilot chat start copilot debug command", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.generateSTest", - "title": "github.copilot.debug.generateSTest", - "keywords": [ - "github copilot debug generate stest", - "github copilot" - ] - }, - { - "id": "github.copilot.createLaunchJsonFileWithContents", - "title": "github.copilot.createLaunchJsonFileWithContents", - "keywords": [ - "github copilot create launch json file with contents", - "github copilot" - ] - }, - { - "id": "github.copilot.startDebugging", - "title": "github.copilot.startDebugging", - "keywords": [ - "github copilot start debugging", - "github copilot" - ] - }, - { - "id": "github.copilot.open.walkthrough", - "title": "github.copilot.open.walkthrough", - "keywords": [ - "github copilot open walkthrough", - "github copilot" - ] - }, - { - "id": "github.copilot.mcp.viewContext7", - "title": "github.copilot.mcp.viewContext7", - "keywords": [ - "github copilot mcp view context7", - "github copilot" - ] - }, - { - "id": "github.copilot.survey.signalUsage", - "title": "github.copilot.survey.signalUsage", - "keywords": [ - "github copilot survey signal usage", - "github copilot" - ] - }, - { - "id": "github.copilot.report", - "title": "github.copilot.report", - "keywords": [ - "github copilot report", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.showContextInspectorView", - "title": "github.copilot.debug.showContextInspectorView", - "keywords": [ - "github copilot debug show context inspector view", - "github copilot" - ] - }, - { - "id": "github.copilot.nes.prepareRename", - "title": "github.copilot.nes.prepareRename", - "keywords": [ - "github copilot nes prepare rename", - "github copilot" - ] - }, - { - "id": "github.copilot.nes.postRename", - "title": "github.copilot.nes.postRename", - "keywords": [ - "github copilot nes post rename", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.validateNesRename", - "title": "github.copilot.debug.validateNesRename", - "keywords": [ - "github copilot debug validate nes rename", - "github copilot" - ] - }, - { - "id": "github.copilot.search.markHelpful", - "title": "github.copilot.search.markHelpful", - "keywords": [ - "github copilot search mark helpful", - "github copilot" - ] - }, - { - "id": "github.copilot.search.markUnhelpful", - "title": "github.copilot.search.markUnhelpful", - "keywords": [ - "github copilot search mark unhelpful", - "github copilot" - ] - }, - { - "id": "github.copilot.search.feedback", - "title": "github.copilot.search.feedback", - "keywords": [ - "github copilot search feedback", - "github copilot" - ] - }, - { - "id": "chat.enableAdditionalUsage", - "title": "chat.enableAdditionalUsage", - "keywords": [ - "chat enable additional usage", - "chat enable" - ] - }, - { - "id": "github.copilot.chat.notebook.enableFollowCellExecution", - "title": "github.copilot.chat.notebook.enableFollowCellExecution", - "keywords": [ - "github copilot chat notebook enable follow cell execution", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.notebook.disableFollowCellExecution", - "title": "github.copilot.chat.notebook.disableFollowCellExecution", - "keywords": [ - "github copilot chat notebook disable follow cell execution", - "github copilot" - ] - }, - { - "id": "github.copilot.buildRemoteWorkspaceIndex", - "title": "github.copilot.buildRemoteWorkspaceIndex", - "keywords": [ - "github copilot build remote workspace index", - "github copilot" - ] - }, - { - "id": "github.copilot.enableExternalIngest", - "title": "github.copilot.enableExternalIngest", - "keywords": [ - "github copilot enable external ingest", - "github copilot" - ] - }, - { - "id": "github.copilot.deleteExternalIngestWorkspaceIndex", - "title": "github.copilot.deleteExternalIngestWorkspaceIndex", - "keywords": [ - "github copilot delete external ingest workspace index", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.collectWorkspaceIndexDiagnostics", - "title": "github.copilot.debug.collectWorkspaceIndexDiagnostics", - "keywords": [ - "github copilot debug collect workspace index diagnostics", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.checkoutPullRequestReroute", - "title": "github.copilot.chat.checkoutPullRequestReroute", - "keywords": [ - "github copilot chat checkout pull request reroute", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.openPullRequestReroute", - "title": "github.copilot.chat.openPullRequestReroute", - "keywords": [ - "github copilot chat open pull request reroute", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.cloudSessions.openRepository", - "title": "github.copilot.chat.cloudSessions.openRepository", - "keywords": [ - "github copilot chat cloud sessions open repository", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.cloudSessions.clearCaches", - "title": "github.copilot.chat.cloudSessions.clearCaches", - "keywords": [ - "github copilot chat cloud sessions clear caches", - "github copilot" - ] - }, - { - "id": "github.copilot.cloud.resetWorkspaceConfirmations", - "title": "github.copilot.cloud.resetWorkspaceConfirmations", - "keywords": [ - "github copilot cloud reset workspace confirmations", - "github copilot" - ] - }, - { - "id": "github.copilot.cloud.sessions.openInBrowser", - "title": "github.copilot.cloud.sessions.openInBrowser", - "keywords": [ - "github copilot cloud sessions open in browser", - "github copilot" - ] - }, - { - "id": "github.copilot.cloud.sessions.proxy.closeChatSessionPullRequest", - "title": "github.copilot.cloud.sessions.proxy.closeChatSessionPullRequest", - "keywords": [ - "github copilot cloud sessions proxy close chat session pull request", - "github copilot" - ] - }, - { - "id": "github.copilot.cloud.sessions.installPRExtension", - "title": "github.copilot.cloud.sessions.installPRExtension", - "keywords": [ - "github copilot cloud sessions install prextension", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.copilotCLI.addFileReference", - "title": "github.copilot.chat.copilotCLI.addFileReference", - "keywords": [ - "github copilot chat copilot cli add file reference", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.copilotCLI.addSelection", - "title": "github.copilot.chat.copilotCLI.addSelection", - "keywords": [ - "github copilot chat copilot cli add selection", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.copilotCLI.acceptDiff", - "title": "github.copilot.chat.copilotCLI.acceptDiff", - "keywords": [ - "github copilot chat copilot cli accept diff", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.copilotCLI.rejectDiff", - "title": "github.copilot.chat.copilotCLI.rejectDiff", - "keywords": [ - "github copilot chat copilot cli reject diff", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.delete", - "title": "github.copilot.cli.sessions.delete", - "keywords": [ - "github copilot cli sessions delete", - "github copilot" - ] - }, - { - "id": "agents.github.copilot.cli.deleteSessions", - "title": "agents.github.copilot.cli.deleteSessions", - "keywords": [ - "agents github copilot cli delete sessions", - "agents github" - ] - }, - { - "id": "github.copilot.cli.sessions.resumeInTerminal", - "title": "github.copilot.cli.sessions.resumeInTerminal", - "keywords": [ - "github copilot cli sessions resume in terminal", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.rename", - "title": "github.copilot.cli.sessions.rename", - "keywords": [ - "github copilot cli sessions rename", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.setTitle", - "title": "github.copilot.cli.sessions.setTitle", - "keywords": [ - "github copilot cli sessions set title", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.newSession", - "title": "github.copilot.cli.newSession", - "keywords": [ - "github copilot cli new session", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.newSessionToSide", - "title": "github.copilot.cli.newSessionToSide", - "keywords": [ - "github copilot cli new session to side", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.openInCopilotCLI", - "title": "github.copilot.cli.openInCopilotCLI", - "keywords": [ - "github copilot cli open in copilot cli", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.openWorktreeInNewWindow", - "title": "github.copilot.cli.sessions.openWorktreeInNewWindow", - "keywords": [ - "github copilot cli sessions open worktree in new window", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.openWorktreeInTerminal", - "title": "github.copilot.cli.sessions.openWorktreeInTerminal", - "keywords": [ - "github copilot cli sessions open worktree in terminal", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.copyWorktreeBranchName", - "title": "github.copilot.cli.sessions.copyWorktreeBranchName", - "keywords": [ - "github copilot cli sessions copy worktree branch name", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.openRepository", - "title": "github.copilot.cli.sessions.openRepository", - "keywords": [ - "github copilot cli sessions open repository", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.applyCopilotCLIAgentSessionChanges", - "title": "github.copilot.chat.applyCopilotCLIAgentSessionChanges", - "keywords": [ - "github copilot chat apply copilot cliagent session changes", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply", - "title": "github.copilot.chat.applyCopilotCLIAgentSessionChanges.apply", - "keywords": [ - "github copilot chat apply copilot cliagent session changes apply", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.merge", - "title": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.merge", - "keywords": [ - "github copilot chat merge copilot cliagent session changes merge", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.mergeAndSync", - "title": "github.copilot.chat.mergeCopilotCLIAgentSessionChanges.mergeAndSync", - "keywords": [ - "github copilot chat merge copilot cliagent session changes merge and sync", - "github copilot" - ] - }, - { - "id": "github.copilot.sessions.refreshChanges", - "title": "github.copilot.sessions.refreshChanges", - "keywords": [ - "github copilot sessions refresh changes", - "github copilot" - ] - }, - { - "id": "github.copilot.sessions.initializeRepository", - "title": "github.copilot.sessions.initializeRepository", - "keywords": [ - "github copilot sessions initialize repository", - "github copilot" - ] - }, - { - "id": "github.copilot.sessions.commit", - "title": "github.copilot.sessions.commit", - "keywords": [ - "github copilot sessions commit", - "github copilot" - ] - }, - { - "id": "github.copilot.sessions.commitAndSync", - "title": "github.copilot.sessions.commitAndSync", - "keywords": [ - "github copilot sessions commit and sync", - "github copilot" - ] - }, - { - "id": "github.copilot.sessions.sync", - "title": "github.copilot.sessions.sync", - "keywords": [ - "github copilot sessions sync", - "github copilot" - ] - }, - { - "id": "github.copilot.sessions.discardChanges", - "title": "github.copilot.sessions.discardChanges", - "keywords": [ - "github copilot sessions discard changes", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR", - "title": "github.copilot.chat.createPullRequestCopilotCLIAgentSession.createPR", - "keywords": [ - "github copilot chat create pull request copilot cliagent session create pr", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.createDraftPullRequestCopilotCLIAgentSession.createDraftPR", - "title": "github.copilot.chat.createDraftPullRequestCopilotCLIAgentSession.createDraftPR", - "keywords": [ - "github copilot chat create draft pull request copilot cliagent session create draft pr", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.commitToWorktree", - "title": "github.copilot.cli.sessions.commitToWorktree", - "keywords": [ - "github copilot cli sessions commit to worktree", - "github copilot" - ] - }, - { - "id": "github.copilot.cli.sessions.commitToRepository", - "title": "github.copilot.cli.sessions.commitToRepository", - "keywords": [ - "github copilot cli sessions commit to repository", - "github copilot" - ] - }, - { - "id": "copilot.claude.agents", - "title": "copilot.claude.agents", - "keywords": [ - "copilot claude agents", - "copilot claude" - ] - }, - { - "id": "copilot.claude.hooks", - "title": "copilot.claude.hooks", - "keywords": [ - "copilot claude hooks", - "copilot claude" - ] - }, - { - "id": "copilot.claude.memory", - "title": "copilot.claude.memory", - "keywords": [ - "copilot claude memory", - "copilot claude" - ] - }, - { - "id": "github.copilot.claude.sessions.rename", - "title": "github.copilot.claude.sessions.rename", - "keywords": [ - "github copilot claude sessions rename", - "github copilot" - ] - }, - { - "id": "github.copilot.claude.sessions.commit", - "title": "github.copilot.claude.sessions.commit", - "keywords": [ - "github copilot claude sessions commit", - "github copilot" - ] - }, - { - "id": "github.copilot.claude.sessions.commitAndSync", - "title": "github.copilot.claude.sessions.commitAndSync", - "keywords": [ - "github copilot claude sessions commit and sync", - "github copilot" - ] - }, - { - "id": "github.copilot.claude.sessions.sync", - "title": "github.copilot.claude.sessions.sync", - "keywords": [ - "github copilot claude sessions sync", - "github copilot" - ] - }, - { - "id": "github.copilot.claude.sessions.initializeRepository", - "title": "github.copilot.claude.sessions.initializeRepository", - "keywords": [ - "github copilot claude sessions initialize repository", - "github copilot" - ] - }, - { - "id": "agentSessions.filter.sortByCreated.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.sortByCreated.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter sort by created agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.sortByUpdated.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.sortByUpdated.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter sort by updated agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExclude:local.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExclude:local.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude:local agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExclude:claude-code.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExclude:claude-code.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude:claude code agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExclude:copilotcli.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExclude:copilotcli.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude:copilotcli agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExclude:copilot-cloud-agent.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExclude:copilot-cloud-agent.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude:copilot cloud agent agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExcludeState:1.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExcludeState:1.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude state:1 agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExcludeState:2.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExcludeState:2.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude state:2 agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExcludeState:3.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExcludeState:3.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude state:3 agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExcludeState:0.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExcludeState:0.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude state:0 agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExcludeArchived.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExcludeArchived.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude archived agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.toggleExcludeRead.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.toggleExcludeRead.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter toggle exclude read agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "agentSessions.filter.resetExcludes.agentsessionsviewerfiltersubmenu", - "title": "agentSessions.filter.resetExcludes.agentsessionsviewerfiltersubmenu", - "keywords": [ - "agent sessions filter reset excludes agentsessionsviewerfiltersubmenu", - "agent sessions" - ] - }, - { - "id": "github.copilot.chat.otel.flush", - "title": "github.copilot.chat.otel.flush", - "keywords": [ - "github copilot chat otel flush", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.otel.exportAgentTracesDB", - "title": "github.copilot.chat.otel.exportAgentTracesDB", - "keywords": [ - "github copilot chat otel export agent traces db", - "github copilot" - ] - }, - { - "id": "github.copilot.sessionSync.deleteSessions", - "title": "github.copilot.sessionSync.deleteSessions", - "keywords": [ - "github copilot session sync delete sessions", - "github copilot" - ] - }, - { - "id": "github.copilot.sessionSync.deleteSessionFromCloud", - "title": "github.copilot.sessionSync.deleteSessionFromCloud", - "keywords": [ - "github copilot session sync delete session from cloud", - "github copilot" - ] - }, - { - "id": "github.copilot.sessionSync.suggest", - "title": "github.copilot.sessionSync.suggest", - "keywords": [ - "github copilot session sync suggest", - "github copilot" - ] - }, - { - "id": "github.copilot.sessionSync.reindex", - "title": "github.copilot.sessionSync.reindex", - "keywords": [ - "github copilot session sync reindex", - "github copilot" - ] - }, - { - "id": "github.copilot.chronicle.reindex", - "title": "github.copilot.chronicle.reindex", - "keywords": [ - "github copilot chronicle reindex", - "github copilot" - ] - }, - { - "id": "C_Cpp.SwitchHeaderSource", - "title": "C_Cpp.SwitchHeaderSource", - "keywords": [ - "c cpp switch header source", - "c cpp" - ] - }, - { - "id": "C_Cpp.ResetDatabase", - "title": "C_Cpp.ResetDatabase", - "keywords": [ - "c cpp reset database", - "c cpp" - ] - }, - { - "id": "C_Cpp.SelectIntelliSenseConfiguration", - "title": "C_Cpp.SelectIntelliSenseConfiguration", - "keywords": [ - "c cpp select intelli sense configuration", - "c cpp" - ] - }, - { - "id": "C_Cpp.InstallCompiler", - "title": "C_Cpp.InstallCompiler", - "keywords": [ - "c cpp install compiler", - "c cpp" - ] - }, - { - "id": "C_Cpp.ConfigurationSelect", - "title": "C_Cpp.ConfigurationSelect", - "keywords": [ - "c cpp configuration select", - "c cpp" - ] - }, - { - "id": "C_Cpp.ConfigurationProviderSelect", - "title": "C_Cpp.ConfigurationProviderSelect", - "keywords": [ - "c cpp configuration provider select", - "c cpp" - ] - }, - { - "id": "C_Cpp.ConfigurationEditJSON", - "title": "C_Cpp.ConfigurationEditJSON", - "keywords": [ - "c cpp configuration edit json", - "c cpp" - ] - }, - { - "id": "C_Cpp.ConfigurationEditUI", - "title": "C_Cpp.ConfigurationEditUI", - "keywords": [ - "c cpp configuration edit ui", - "c cpp" - ] - }, - { - "id": "C_Cpp.ConfigurationEdit", - "title": "C_Cpp.ConfigurationEdit", - "keywords": [ - "c cpp configuration edit", - "c cpp" - ] - }, - { - "id": "C_Cpp.AddToIncludePath", - "title": "C_Cpp.AddToIncludePath", - "keywords": [ - "c cpp add to include path", - "c cpp" - ] - }, - { - "id": "C_Cpp.EnableErrorSquiggles", - "title": "C_Cpp.EnableErrorSquiggles", - "keywords": [ - "c cpp enable error squiggles", - "c cpp" - ] - }, - { - "id": "C_Cpp.DisableErrorSquiggles", - "title": "C_Cpp.DisableErrorSquiggles", - "keywords": [ - "c cpp disable error squiggles", - "c cpp" - ] - }, - { - "id": "C_Cpp.ToggleDimInactiveRegions", - "title": "C_Cpp.ToggleDimInactiveRegions", - "keywords": [ - "c cpp toggle dim inactive regions", - "c cpp" - ] - }, - { - "id": "C_Cpp.PauseParsing", - "title": "C_Cpp.PauseParsing", - "keywords": [ - "c cpp pause parsing", - "c cpp" - ] - }, - { - "id": "C_Cpp.ResumeParsing", - "title": "C_Cpp.ResumeParsing", - "keywords": [ - "c cpp resume parsing", - "c cpp" - ] - }, - { - "id": "C_Cpp.PauseCodeAnalysis", - "title": "C_Cpp.PauseCodeAnalysis", - "keywords": [ - "c cpp pause code analysis", - "c cpp" - ] - }, - { - "id": "C_Cpp.ResumeCodeAnalysis", - "title": "C_Cpp.ResumeCodeAnalysis", - "keywords": [ - "c cpp resume code analysis", - "c cpp" - ] - }, - { - "id": "C_Cpp.CancelCodeAnalysis", - "title": "C_Cpp.CancelCodeAnalysis", - "keywords": [ - "c cpp cancel code analysis", - "c cpp" - ] - }, - { - "id": "C_Cpp.ShowActiveCodeAnalysisCommands", - "title": "C_Cpp.ShowActiveCodeAnalysisCommands", - "keywords": [ - "c cpp show active code analysis commands", - "c cpp" - ] - }, - { - "id": "C_Cpp.ShowIdleCodeAnalysisCommands", - "title": "C_Cpp.ShowIdleCodeAnalysisCommands", - "keywords": [ - "c cpp show idle code analysis commands", - "c cpp" - ] - }, - { - "id": "C_Cpp.ShowReferencesProgress", - "title": "C_Cpp.ShowReferencesProgress", - "keywords": [ - "c cpp show references progress", - "c cpp" - ] - }, - { - "id": "C_Cpp.FindAllReferences", - "title": "C_Cpp.FindAllReferences", - "keywords": [ - "c cpp find all references", - "c cpp" - ] - }, - { - "id": "C_Cpp.GoToDefinition", - "title": "C_Cpp.GoToDefinition", - "keywords": [ - "c cpp go to definition", - "c cpp" - ] - }, - { - "id": "C_Cpp.PrepareCallHierarchy", - "title": "C_Cpp.PrepareCallHierarchy", - "keywords": [ - "c cpp prepare call hierarchy", - "c cpp" - ] - }, - { - "id": "C_Cpp.CallHierarchyCallsTo", - "title": "C_Cpp.CallHierarchyCallsTo", - "keywords": [ - "c cpp call hierarchy calls to", - "c cpp" - ] - }, - { - "id": "C_Cpp.CallHierarchyCallsFrom", - "title": "C_Cpp.CallHierarchyCallsFrom", - "keywords": [ - "c cpp call hierarchy calls from", - "c cpp" - ] - }, - { - "id": "C_Cpp.TakeSurvey", - "title": "C_Cpp.TakeSurvey", - "keywords": [ - "c cpp take survey", - "c cpp" - ] - }, - { - "id": "C_Cpp.LogDiagnostics", - "title": "C_Cpp.LogDiagnostics", - "keywords": [ - "c cpp log diagnostics", - "c cpp" - ] - }, - { - "id": "C_Cpp.RescanWorkspace", - "title": "C_Cpp.RescanWorkspace", - "keywords": [ - "c cpp rescan workspace", - "c cpp" - ] - }, - { - "id": "C_Cpp.ShowReferenceItem", - "title": "C_Cpp.ShowReferenceItem", - "keywords": [ - "c cpp show reference item", - "c cpp" - ] - }, - { - "id": "C_Cpp.referencesViewGroupByType", - "title": "C_Cpp.referencesViewGroupByType", - "keywords": [ - "c cpp references view group by type", - "c cpp" - ] - }, - { - "id": "C_Cpp.referencesViewUngroupByType", - "title": "C_Cpp.referencesViewUngroupByType", - "keywords": [ - "c cpp references view ungroup by type", - "c cpp" - ] - }, - { - "id": "C_Cpp.VcpkgClipboardInstallSuggested", - "title": "C_Cpp.VcpkgClipboardInstallSuggested", - "keywords": [ - "c cpp vcpkg clipboard install suggested", - "c cpp" - ] - }, - { - "id": "C_Cpp.VcpkgOnlineHelpSuggested", - "title": "C_Cpp.VcpkgOnlineHelpSuggested", - "keywords": [ - "c cpp vcpkg online help suggested", - "c cpp" - ] - }, - { - "id": "C_Cpp.GenerateEditorConfig", - "title": "C_Cpp.GenerateEditorConfig", - "keywords": [ - "c cpp generate editor config", - "c cpp" - ] - }, - { - "id": "C_Cpp.GoToNextDirectiveInGroup", - "title": "C_Cpp.GoToNextDirectiveInGroup", - "keywords": [ - "c cpp go to next directive in group", - "c cpp" - ] - }, - { - "id": "C_Cpp.GoToPrevDirectiveInGroup", - "title": "C_Cpp.GoToPrevDirectiveInGroup", - "keywords": [ - "c cpp go to prev directive in group", - "c cpp" - ] - }, - { - "id": "C_Cpp.RunCodeAnalysisOnActiveFile", - "title": "C_Cpp.RunCodeAnalysisOnActiveFile", - "keywords": [ - "c cpp run code analysis on active file", - "c cpp" - ] - }, - { - "id": "C_Cpp.RunCodeAnalysisOnOpenFiles", - "title": "C_Cpp.RunCodeAnalysisOnOpenFiles", - "keywords": [ - "c cpp run code analysis on open files", - "c cpp" - ] - }, - { - "id": "C_Cpp.RunCodeAnalysisOnAllFiles", - "title": "C_Cpp.RunCodeAnalysisOnAllFiles", - "keywords": [ - "c cpp run code analysis on all files", - "c cpp" - ] - }, - { - "id": "C_Cpp.RemoveCodeAnalysisProblems", - "title": "C_Cpp.RemoveCodeAnalysisProblems", - "keywords": [ - "c cpp remove code analysis problems", - "c cpp" - ] - }, - { - "id": "C_Cpp.RemoveAllCodeAnalysisProblems", - "title": "C_Cpp.RemoveAllCodeAnalysisProblems", - "keywords": [ - "c cpp remove all code analysis problems", - "c cpp" - ] - }, - { - "id": "C_Cpp.FixThisCodeAnalysisProblem", - "title": "C_Cpp.FixThisCodeAnalysisProblem", - "keywords": [ - "c cpp fix this code analysis problem", - "c cpp" - ] - }, - { - "id": "C_Cpp.FixAllTypeCodeAnalysisProblems", - "title": "C_Cpp.FixAllTypeCodeAnalysisProblems", - "keywords": [ - "c cpp fix all type code analysis problems", - "c cpp" - ] - }, - { - "id": "C_Cpp.FixAllCodeAnalysisProblems", - "title": "C_Cpp.FixAllCodeAnalysisProblems", - "keywords": [ - "c cpp fix all code analysis problems", - "c cpp" - ] - }, - { - "id": "C_Cpp.DisableAllTypeCodeAnalysisProblems", - "title": "C_Cpp.DisableAllTypeCodeAnalysisProblems", - "keywords": [ - "c cpp disable all type code analysis problems", - "c cpp" - ] - }, - { - "id": "C_Cpp.ShowCodeAnalysisDocumentation", - "title": "C_Cpp.ShowCodeAnalysisDocumentation", - "keywords": [ - "c cpp show code analysis documentation", - "c cpp" - ] - }, - { - "id": "cpptools.activeConfigName", - "title": "cpptools.activeConfigName", - "keywords": [ - "cpptools active config name", - "cpptools active" - ] - }, - { - "id": "cpptools.activeConfigCustomVariable", - "title": "cpptools.activeConfigCustomVariable", - "keywords": [ - "cpptools active config custom variable", - "cpptools active" - ] - }, - { - "id": "cpptools.setActiveConfigName", - "title": "cpptools.setActiveConfigName", - "keywords": [ - "cpptools set active config name", - "cpptools set" - ] - }, - { - "id": "cpptools.waitForTagParsing", - "title": "cpptools.waitForTagParsing", - "keywords": [ - "cpptools wait for tag parsing", - "cpptools wait" - ] - }, - { - "id": "C_Cpp.RestartIntelliSenseForFile", - "title": "C_Cpp.RestartIntelliSenseForFile", - "keywords": [ - "c cpp restart intelli sense for file", - "c cpp" - ] - }, - { - "id": "C_Cpp.GenerateDoxygenComment", - "title": "C_Cpp.GenerateDoxygenComment", - "keywords": [ - "c cpp generate doxygen comment", - "c cpp" - ] - }, - { - "id": "C_Cpp.CreateDeclarationOrDefinition", - "title": "C_Cpp.CreateDeclarationOrDefinition", - "keywords": [ - "c cpp create declaration or definition", - "c cpp" - ] - }, - { - "id": "C_Cpp.CopyDeclarationOrDefinition", - "title": "C_Cpp.CopyDeclarationOrDefinition", - "keywords": [ - "c cpp copy declaration or definition", - "c cpp" - ] - }, - { - "id": "C_Cpp.RescanCompilers", - "title": "C_Cpp.RescanCompilers", - "keywords": [ - "c cpp rescan compilers", - "c cpp" - ] - }, - { - "id": "C_Cpp.AddMissingInclude", - "title": "C_Cpp.AddMissingInclude", - "keywords": [ - "c cpp add missing include", - "c cpp" - ] - }, - { - "id": "C_Cpp.ExtractToFunction", - "title": "C_Cpp.ExtractToFunction", - "keywords": [ - "c cpp extract to function", - "c cpp" - ] - }, - { - "id": "C_Cpp.ExtractToFreeFunction", - "title": "C_Cpp.ExtractToFreeFunction", - "keywords": [ - "c cpp extract to free function", - "c cpp" - ] - }, - { - "id": "C_Cpp.ExtractToMemberFunction", - "title": "C_Cpp.ExtractToMemberFunction", - "keywords": [ - "c cpp extract to member function", - "c cpp" - ] - }, - { - "id": "C_Cpp.ExpandSelection", - "title": "C_Cpp.ExpandSelection", - "keywords": [ - "c cpp expand selection", - "c cpp" - ] - }, - { - "id": "C_Cpp.ShowCopilotHover", - "title": "C_Cpp.ShowCopilotHover", - "keywords": [ - "c cpp show copilot hover", - "c cpp" - ] - }, - { - "id": "C_Cpp.SetVsDeveloperEnvironment", - "title": "C_Cpp.SetVsDeveloperEnvironment", - "keywords": [ - "c cpp set vs developer environment", - "c cpp" - ] - }, - { - "id": "C_Cpp.ClearVsDeveloperEnvironment", - "title": "C_Cpp.ClearVsDeveloperEnvironment", - "keywords": [ - "c cpp clear vs developer environment", - "c cpp" - ] - }, - { - "id": "pythonIndent.newlineAndIndent", - "title": "pythonIndent.newlineAndIndent", - "keywords": [ - "python indent newline and indent", - "python indent" - ] - }, - { - "id": "python.configureTests", - "title": "python.configureTests", - "keywords": [ - "python configure tests", - "python configure" - ] - }, - { - "id": "python.copilotSetupTests", - "title": "python.copilotSetupTests", - "keywords": [ - "python copilot setup tests", - "python copilot" - ] - }, - { - "id": "python.copyTestId", - "title": "python.copyTestId", - "keywords": [ - "python copy test id", - "python copy" - ] - }, - { - "id": "workbench.action.output.show.extension-output-ms-toolsai.jupyter-#1-Jupyter", - "title": "workbench.action.output.show.extension-output-ms-toolsai.jupyter-#1-Jupyter", - "keywords": [ - "output show extension output ms toolsai jupyter #1 jupyter", - "output show" - ] - }, - { - "id": "jupyter.listPipPackages", - "title": "jupyter.listPipPackages", - "keywords": [ - "jupyter list pip packages", - "jupyter list" - ] - }, - { - "id": "jupyter.listVariables", - "title": "jupyter.listVariables", - "keywords": [ - "jupyter list variables", - "jupyter list" - ] - }, - { - "id": "workbench.action.output.show.extension-output-ms-toolsai.jupyter-#2-Jupyter Server Console", - "title": "workbench.action.output.show.extension-output-ms-toolsai.jupyter-#2-Jupyter Server Console", - "keywords": [ - "output show extension output ms toolsai jupyter #2 jupyter server console", - "output show" - ] - }, - { - "id": "jupyter.refreshDataViewer", - "title": "jupyter.refreshDataViewer", - "keywords": [ - "jupyter refresh data viewer", - "jupyter refresh" - ] - }, - { - "id": "jupyter.reloadVSCode", - "title": "jupyter.reloadVSCode", - "keywords": [ - "jupyter reload vscode", - "jupyter reload" - ] - }, - { - "id": "jupyter.runInDedicatedExtensionHost", - "title": "jupyter.runInDedicatedExtensionHost", - "keywords": [ - "jupyter run in dedicated extension host", - "jupyter run" - ] - }, - { - "id": "jupyter.filterKernels", - "title": "jupyter.filterKernels", - "keywords": [ - "jupyter filter kernels", - "jupyter filter" - ] - }, - { - "id": "jupyter.clearSavedJupyterUris", - "title": "jupyter.clearSavedJupyterUris", - "keywords": [ - "jupyter clear saved jupyter uris", - "jupyter clear" - ] - }, - { - "id": "jupyter.selectJupyterInterpreter", - "title": "jupyter.selectJupyterInterpreter", - "keywords": [ - "jupyter select jupyter interpreter", - "jupyter select" - ] - }, - { - "id": "jupyter.kernel.selectLocalKernelSpec", - "title": "jupyter.kernel.selectLocalKernelSpec", - "keywords": [ - "jupyter kernel select local kernel spec", - "jupyter kernel" - ] - }, - { - "id": "jupyter.kernel.selectLocalPythonEnvironment", - "title": "jupyter.kernel.selectLocalPythonEnvironment", - "keywords": [ - "jupyter kernel select local python environment", - "jupyter kernel" - ] - }, - { - "id": "jupyter.kernel.selectJupyterServerKernel", - "title": "jupyter.kernel.selectJupyterServerKernel", - "keywords": [ - "jupyter kernel select jupyter server kernel", - "jupyter kernel" - ] - }, - { - "id": "jupyter.createPythonEnvAndSelectController", - "title": "jupyter.createPythonEnvAndSelectController", - "keywords": [ - "jupyter create python env and select controller", - "jupyter create" - ] - }, - { - "id": "jupyter.notebookeditor.removeallcells", - "title": "jupyter.notebookeditor.removeallcells", - "keywords": [ - "jupyter notebookeditor removeallcells", - "jupyter notebookeditor" - ] - }, - { - "id": "jupyter.notebookeditor.runallcells", - "title": "jupyter.notebookeditor.runallcells", - "keywords": [ - "jupyter notebookeditor runallcells", - "jupyter notebookeditor" - ] - }, - { - "id": "jupyter.notebookeditor.addcellbelow", - "title": "jupyter.notebookeditor.addcellbelow", - "keywords": [ - "jupyter notebookeditor addcellbelow", - "jupyter notebookeditor" - ] - }, - { - "id": "jupyter.restartkernelandrunuptoselectedcell", - "title": "jupyter.restartkernelandrunuptoselectedcell", - "keywords": [ - "jupyter restartkernelandrunuptoselectedcell" - ] - }, - { - "id": "jupyter.restartkernel", - "title": "jupyter.restartkernel", - "keywords": [ - "jupyter restartkernel" - ] - }, - { - "id": "jupyter.interruptkernel", - "title": "jupyter.interruptkernel", - "keywords": [ - "jupyter interruptkernel" - ] - }, - { - "id": "jupyter.restartkernelandrunallcells", - "title": "jupyter.restartkernelandrunallcells", - "keywords": [ - "jupyter restartkernelandrunallcells" - ] - }, - { - "id": "jupyter.installPythonExtensionViaKernelPicker", - "title": "jupyter.installPythonExtensionViaKernelPicker", - "keywords": [ - "jupyter install python extension via kernel picker", - "jupyter install" - ] - }, - { - "id": "jupyter.installPythonViaKernelPicker", - "title": "jupyter.installPythonViaKernelPicker", - "keywords": [ - "jupyter install python via kernel picker", - "jupyter install" - ] - }, - { - "id": "jupyter.runByLine", - "title": "jupyter.runByLine", - "keywords": [ - "jupyter run by line", - "jupyter run" - ] - }, - { - "id": "jupyter.runByLineNext", - "title": "jupyter.runByLineNext", - "keywords": [ - "jupyter run by line next", - "jupyter run" - ] - }, - { - "id": "jupyter.runByLineStop", - "title": "jupyter.runByLineStop", - "keywords": [ - "jupyter run by line stop", - "jupyter run" - ] - }, - { - "id": "jupyter.runAndDebugCell", - "title": "jupyter.runAndDebugCell", - "keywords": [ - "jupyter run and debug cell", - "jupyter run" - ] - }, - { - "id": "jupyter.runallcells", - "title": "jupyter.runallcells", - "keywords": [ - "jupyter runallcells" - ] - }, - { - "id": "jupyter.runcell", - "title": "jupyter.runcell", - "keywords": [ - "jupyter runcell" - ] - }, - { - "id": "jupyter.runcurrentcell", - "title": "jupyter.runcurrentcell", - "keywords": [ - "jupyter runcurrentcell" - ] - }, - { - "id": "jupyter.runcurrentcelladvance", - "title": "jupyter.runcurrentcelladvance", - "keywords": [ - "jupyter runcurrentcelladvance" - ] - }, - { - "id": "jupyter.execSelectionInteractive", - "title": "jupyter.execSelectionInteractive", - "keywords": [ - "jupyter exec selection interactive", - "jupyter exec" - ] - }, - { - "id": "jupyter.runallcellsabove", - "title": "jupyter.runallcellsabove", - "keywords": [ - "jupyter runallcellsabove" - ] - }, - { - "id": "jupyter.runcellandallbelow", - "title": "jupyter.runcellandallbelow", - "keywords": [ - "jupyter runcellandallbelow" - ] - }, - { - "id": "jupyter.runallcellsabove.palette", - "title": "jupyter.runallcellsabove.palette", - "keywords": [ - "jupyter runallcellsabove palette", - "jupyter runallcellsabove" - ] - }, - { - "id": "jupyter.runcurrentcellandallbelow.palette", - "title": "jupyter.runcurrentcellandallbelow.palette", - "keywords": [ - "jupyter runcurrentcellandallbelow palette", - "jupyter runcurrentcellandallbelow" - ] - }, - { - "id": "jupyter.runcurrentcellandaddbelow", - "title": "jupyter.runcurrentcellandaddbelow", - "keywords": [ - "jupyter runcurrentcellandaddbelow" - ] - }, - { - "id": "jupyter.debugcell", - "title": "jupyter.debugcell", - "keywords": [ - "jupyter debugcell" - ] - }, - { - "id": "jupyter.debugstepover", - "title": "jupyter.debugstepover", - "keywords": [ - "jupyter debugstepover" - ] - }, - { - "id": "jupyter.debugcontinue", - "title": "jupyter.debugcontinue", - "keywords": [ - "jupyter debugcontinue" - ] - }, - { - "id": "jupyter.debugstop", - "title": "jupyter.debugstop", - "keywords": [ - "jupyter debugstop" - ] - }, - { - "id": "jupyter.debugcurrentcell.palette", - "title": "jupyter.debugcurrentcell.palette", - "keywords": [ - "jupyter debugcurrentcell palette", - "jupyter debugcurrentcell" - ] - }, - { - "id": "jupyter.openVariableView", - "title": "jupyter.openVariableView", - "keywords": [ - "jupyter open variable view", - "jupyter open" - ] - }, - { - "id": "jupyter.openOutlineView", - "title": "jupyter.openOutlineView", - "keywords": [ - "jupyter open outline view", - "jupyter open" - ] - }, - { - "id": "jupyter.runtoline", - "title": "jupyter.runtoline", - "keywords": [ - "jupyter runtoline" - ] - }, - { - "id": "jupyter.runfromline", - "title": "jupyter.runfromline", - "keywords": [ - "jupyter runfromline" - ] - }, - { - "id": "jupyter.runFileInteractive", - "title": "jupyter.runFileInteractive", - "keywords": [ - "jupyter run file interactive", - "jupyter run" - ] - }, - { - "id": "jupyter.debugFileInteractive", - "title": "jupyter.debugFileInteractive", - "keywords": [ - "jupyter debug file interactive", - "jupyter debug" - ] - }, - { - "id": "jupyter.insertCellBelowPosition", - "title": "jupyter.insertCellBelowPosition", - "keywords": [ - "jupyter insert cell below position", - "jupyter insert" - ] - }, - { - "id": "jupyter.insertCellBelow", - "title": "jupyter.insertCellBelow", - "keywords": [ - "jupyter insert cell below", - "jupyter insert" - ] - }, - { - "id": "jupyter.insertCellAbove", - "title": "jupyter.insertCellAbove", - "keywords": [ - "jupyter insert cell above", - "jupyter insert" - ] - }, - { - "id": "jupyter.deleteCells", - "title": "jupyter.deleteCells", - "keywords": [ - "jupyter delete cells", - "jupyter delete" - ] - }, - { - "id": "jupyter.selectCell", - "title": "jupyter.selectCell", - "keywords": [ - "jupyter select cell", - "jupyter select" - ] - }, - { - "id": "jupyter.selectCellContents", - "title": "jupyter.selectCellContents", - "keywords": [ - "jupyter select cell contents", - "jupyter select" - ] - }, - { - "id": "jupyter.extendSelectionByCellAbove", - "title": "jupyter.extendSelectionByCellAbove", - "keywords": [ - "jupyter extend selection by cell above", - "jupyter extend" - ] - }, - { - "id": "jupyter.extendSelectionByCellBelow", - "title": "jupyter.extendSelectionByCellBelow", - "keywords": [ - "jupyter extend selection by cell below", - "jupyter extend" - ] - }, - { - "id": "jupyter.moveCellsUp", - "title": "jupyter.moveCellsUp", - "keywords": [ - "jupyter move cells up", - "jupyter move" - ] - }, - { - "id": "jupyter.moveCellsDown", - "title": "jupyter.moveCellsDown", - "keywords": [ - "jupyter move cells down", - "jupyter move" - ] - }, - { - "id": "jupyter.changeCellToMarkdown", - "title": "jupyter.changeCellToMarkdown", - "keywords": [ - "jupyter change cell to markdown", - "jupyter change" - ] - }, - { - "id": "jupyter.changeCellToCode", - "title": "jupyter.changeCellToCode", - "keywords": [ - "jupyter change cell to code", - "jupyter change" - ] - }, - { - "id": "jupyter.gotoNextCellInFile", - "title": "jupyter.gotoNextCellInFile", - "keywords": [ - "jupyter goto next cell in file", - "jupyter goto" - ] - }, - { - "id": "jupyter.gotoPrevCellInFile", - "title": "jupyter.gotoPrevCellInFile", - "keywords": [ - "jupyter goto prev cell in file", - "jupyter goto" - ] - }, - { - "id": "jupyter.addcellbelow", - "title": "jupyter.addcellbelow", - "keywords": [ - "jupyter addcellbelow" - ] - }, - { - "id": "jupyter.createnewnotebook", - "title": "jupyter.createnewnotebook", - "keywords": [ - "jupyter createnewnotebook" - ] - }, - { - "id": "jupyter.latestExtension", - "title": "jupyter.latestExtension", - "keywords": [ - "jupyter latest extension", - "jupyter latest" - ] - }, - { - "id": "jupyter.enableLoadingWidgetScriptsFromThirdPartySource", - "title": "jupyter.enableLoadingWidgetScriptsFromThirdPartySource", - "keywords": [ - "jupyter enable loading widget scripts from third party source", - "jupyter enable" - ] - }, - { - "id": "jupyter.createnewinteractive", - "title": "jupyter.createnewinteractive", - "keywords": [ - "jupyter createnewinteractive" - ] - }, - { - "id": "jupyter.importnotebookfile", - "title": "jupyter.importnotebookfile", - "keywords": [ - "jupyter importnotebookfile" - ] - }, - { - "id": "jupyter.exportfileasnotebook", - "title": "jupyter.exportfileasnotebook", - "keywords": [ - "jupyter exportfileasnotebook" - ] - }, - { - "id": "jupyter.expandallcells", - "title": "jupyter.expandallcells", - "keywords": [ - "jupyter expandallcells" - ] - }, - { - "id": "jupyter.collapseallcells", - "title": "jupyter.collapseallcells", - "keywords": [ - "jupyter collapseallcells" - ] - }, - { - "id": "jupyter.interactive.exportasnotebook", - "title": "jupyter.interactive.exportasnotebook", - "keywords": [ - "jupyter interactive exportasnotebook", - "jupyter interactive" - ] - }, - { - "id": "jupyter.interactive.exportas", - "title": "jupyter.interactive.exportas", - "keywords": [ - "jupyter interactive exportas", - "jupyter interactive" - ] - }, - { - "id": "jupyter.scrolltocell", - "title": "jupyter.scrolltocell", - "keywords": [ - "jupyter scrolltocell" - ] - }, - { - "id": "jupyter.interactive.clearAllCells", - "title": "jupyter.interactive.clearAllCells", - "keywords": [ - "jupyter interactive clear all cells", - "jupyter interactive" - ] - }, - { - "id": "jupyter.interactive.goToCode", - "title": "jupyter.interactive.goToCode", - "keywords": [ - "jupyter interactive go to code", - "jupyter interactive" - ] - }, - { - "id": "jupyter.interactive.copyCell", - "title": "jupyter.interactive.copyCell", - "keywords": [ - "jupyter interactive copy cell", - "jupyter interactive" - ] - }, - { - "id": "jupyter.viewOutput", - "title": "jupyter.viewOutput", - "keywords": [ - "jupyter view output", - "jupyter view" - ] - }, - { - "id": "jupyter.selectjupyteruri", - "title": "jupyter.selectjupyteruri", - "keywords": [ - "jupyter selectjupyteruri" - ] - }, - { - "id": "jupyter.exportAsPythonScript", - "title": "jupyter.exportAsPythonScript", - "keywords": [ - "jupyter export as python script", - "jupyter export" - ] - }, - { - "id": "jupyter.exportToHTML", - "title": "jupyter.exportToHTML", - "keywords": [ - "jupyter export to html", - "jupyter export" - ] - }, - { - "id": "jupyter.exportToPDF", - "title": "jupyter.exportToPDF", - "keywords": [ - "jupyter export to pdf", - "jupyter export" - ] - }, - { - "id": "jupyter.export", - "title": "jupyter.export", - "keywords": [ - "jupyter export" - ] - }, - { - "id": "jupyter.notebookeditor.export", - "title": "jupyter.notebookeditor.export", - "keywords": [ - "jupyter notebookeditor export", - "jupyter notebookeditor" - ] - }, - { - "id": "jupyter.continueEditSessionInCodespace", - "title": "jupyter.continueEditSessionInCodespace", - "keywords": [ - "jupyter continue edit session in codespace", - "jupyter continue" - ] - }, - { - "id": "dataScience.ClearUserProviderJupyterServerCache", - "title": "dataScience.ClearUserProviderJupyterServerCache", - "keywords": [ - "data science clear user provider jupyter server cache", - "data science" - ] - }, - { - "id": "jupyter.getUsedAzMLServerHandles", - "title": "jupyter.getUsedAzMLServerHandles", - "keywords": [ - "jupyter get used az mlserver handles", - "jupyter get" - ] - }, - { - "id": "jupyter.manageAccessToKernels", - "title": "jupyter.manageAccessToKernels", - "keywords": [ - "jupyter manage access to kernels", - "jupyter manage" - ] - }, - { - "id": "jupyter.showDataViewer", - "title": "jupyter.showDataViewer", - "keywords": [ - "jupyter show data viewer", - "jupyter show" - ] - }, - { - "id": "jupyter.showJupyterDataViewer", - "title": "jupyter.showJupyterDataViewer", - "keywords": [ - "jupyter show jupyter data viewer", - "jupyter show" - ] - }, - { - "id": "autoDocstring.generateDocstring", - "title": "autoDocstring.generateDocstring", - "keywords": [ - "auto docstring generate docstring", - "auto docstring" - ] - }, - { - "id": "workbench.action.output.show.extension-output-njpwerner.autodocstring-#1-autoDocstring", - "title": "workbench.action.output.show.extension-output-njpwerner.autodocstring-#1-autoDocstring", - "keywords": [ - "output show extension output njpwerner autodocstring #1 auto docstring", - "output show" - ] - }, - { - "id": "editor.action.inlineSuggest.renameSymbol", - "title": "editor.action.inlineSuggest.renameSymbol", - "keywords": [ - "inline suggest rename symbol", - "inline suggest" - ] - }, - { - "id": "workbench.action.output.show.ms-python.python.Python.log", - "title": "workbench.action.output.show.ms-python.python.Python.log", - "keywords": [ - "output show ms python python python log", - "output show" - ] - }, - { - "id": "workbench.action.output.show.ms-python.python.Python Locator.log", - "title": "workbench.action.output.show.ms-python.python.Python Locator.log", - "keywords": [ - "output show ms python python python locator log", - "output show" - ] - }, - { - "id": "python.viewOutput", - "title": "python.viewOutput", - "keywords": [ - "python view output", - "python view" - ] - }, - { - "id": "pylance.runCommands", - "title": "pylance.runCommands", - "keywords": [ - "pylance run commands", - "pylance run" - ] - }, - { - "id": "pylance.triggerParameterHints", - "title": "pylance.triggerParameterHints", - "keywords": [ - "pylance trigger parameter hints", - "pylance trigger" - ] - }, - { - "id": "pylance.extractMethodWithRename", - "title": "pylance.extractMethodWithRename", - "keywords": [ - "pylance extract method with rename", - "pylance extract" - ] - }, - { - "id": "pylance.extractVariableWithRename", - "title": "pylance.extractVariableWithRename", - "keywords": [ - "pylance extract variable with rename", - "pylance extract" - ] - }, - { - "id": "pylance.reportIssue", - "title": "pylance.reportIssue", - "keywords": [ - "pylance report issue", - "pylance report" - ] - }, - { - "id": "pylance.addToExtraPaths", - "title": "pylance.addToExtraPaths", - "keywords": [ - "pylance add to extra paths", - "pylance add" - ] - }, - { - "id": "pylance.dumpTokens", - "title": "pylance.dumpTokens", - "keywords": [ - "pylance dump tokens", - "pylance dump" - ] - }, - { - "id": "pylance.dumpNodes", - "title": "pylance.dumpNodes", - "keywords": [ - "pylance dump nodes", - "pylance dump" - ] - }, - { - "id": "pylance.dumpTypes", - "title": "pylance.dumpTypes", - "keywords": [ - "pylance dump types", - "pylance dump" - ] - }, - { - "id": "pylance.dumpCachedTypes", - "title": "pylance.dumpCachedTypes", - "keywords": [ - "pylance dump cached types", - "pylance dump" - ] - }, - { - "id": "pylance.dumpCodeFlowGraph", - "title": "pylance.dumpCodeFlowGraph", - "keywords": [ - "pylance dump code flow graph", - "pylance dump" - ] - }, - { - "id": "pylance.showTypeCheckingPicker", - "title": "pylance.showTypeCheckingPicker", - "keywords": [ - "pylance show type checking picker", - "pylance show" - ] - }, - { - "id": "pylance.openConfig", - "title": "pylance.openConfig", - "keywords": [ - "pylance open config", - "pylance open" - ] - }, - { - "id": "pylance.setAutoImportCompletions", - "title": "pylance.setAutoImportCompletions", - "keywords": [ - "pylance set auto import completions", - "pylance set" - ] - }, - { - "id": "pylance.openUri", - "title": "pylance.openUri", - "keywords": [ - "pylance open uri", - "pylance open" - ] - }, - { - "id": "pylance.searchImports", - "title": "pylance.searchImports", - "keywords": [ - "pylance search imports", - "pylance search" - ] - }, - { - "id": "pylance.foldDocstrings", - "title": "pylance.foldDocstrings", - "keywords": [ - "pylance fold docstrings", - "pylance fold" - ] - }, - { - "id": "pylance.unfoldDocstrings", - "title": "pylance.unfoldDocstrings", - "keywords": [ - "pylance unfold docstrings", - "pylance unfold" - ] - }, - { - "id": "pylance.runCurrentFileInSandbox", - "title": "pylance.runCurrentFileInSandbox", - "keywords": [ - "pylance run current file in sandbox", - "pylance run" - ] - }, - { - "id": "pylance.showReferences", - "title": "pylance.showReferences", - "keywords": [ - "pylance show references", - "pylance show" - ] - }, - { - "id": "pylance.moveSymbolWithFileSelection", - "title": "pylance.moveSymbolWithFileSelection", - "keywords": [ - "pylance move symbol with file selection", - "pylance move" - ] - }, - { - "id": "pylance.troubleshootMissingImports", - "title": "pylance.troubleshootMissingImports", - "keywords": [ - "pylance troubleshoot missing imports", - "pylance troubleshoot" - ] - }, - { - "id": "pylance.findMatchingEnv", - "title": "pylance.findMatchingEnv", - "keywords": [ - "pylance find matching env", - "pylance find" - ] - }, - { - "id": "pylance.installMissingPackages", - "title": "pylance.installMissingPackages", - "keywords": [ - "pylance install missing packages", - "pylance install" - ] - }, - { - "id": "python.execInTerminal", - "title": "python.execInTerminal", - "keywords": [ - "python exec in terminal", - "python exec" - ] - }, - { - "id": "python.execInTerminal-icon", - "title": "python.execInTerminal-icon", - "keywords": [ - "python exec in terminal icon", - "python exec" - ] - }, - { - "id": "python.execInDedicatedTerminal", - "title": "python.execInDedicatedTerminal", - "keywords": [ - "python exec in dedicated terminal", - "python exec" - ] - }, - { - "id": "python.execSelectionInTerminal", - "title": "python.execSelectionInTerminal", - "keywords": [ - "python exec selection in terminal", - "python exec" - ] - }, - { - "id": "python.execSelectionInDjangoShell", - "title": "python.execSelectionInDjangoShell", - "keywords": [ - "python exec selection in django shell", - "python exec" - ] - }, - { - "id": "python.startREPL", - "title": "python.startREPL", - "keywords": [ - "python start repl", - "python start" - ] - }, - { - "id": "python.createTerminal", - "title": "python.createTerminal", - "keywords": [ - "python create terminal", - "python create" - ] - }, - { - "id": "python.createEnvironmentCheck", - "title": "python.createEnvironmentCheck", - "keywords": [ - "python create environment check", - "python create" - ] - }, - { - "id": "workbench.view.testing.toggleVisibility", - "title": "workbench.view.testing.toggleVisibility", - "keywords": [ - "workbench view testing toggle visibility", - "workbench view" - ] - }, - { - "id": "workbench.view.testing.removeView", - "title": "workbench.view.testing.removeView", - "keywords": [ - "workbench view testing remove view", - "workbench view" - ] - }, - { - "id": "python.clearCacheAndReload", - "title": "python.clearCacheAndReload", - "keywords": [ - "python clear cache and reload", - "python clear" - ] - }, - { - "id": "python.installJupyter", - "title": "python.installJupyter", - "keywords": [ - "python install jupyter", - "python install" - ] - }, - { - "id": "python.createNewFile", - "title": "python.createNewFile", - "keywords": [ - "python create new file", - "python create" - ] - }, - { - "id": "python.reloadVSCode", - "title": "python.reloadVSCode", - "keywords": [ - "python reload vscode", - "python reload" - ] - }, - { - "id": "python.reportIssue", - "title": "python.reportIssue", - "keywords": [ - "python report issue", - "python report" - ] - }, - { - "id": "python.installPython", - "title": "python.installPython", - "keywords": [ - "python install python", - "python install" - ] - }, - { - "id": "python.installPythonOnMac", - "title": "python.installPythonOnMac", - "keywords": [ - "python install python on mac", - "python install" - ] - }, - { - "id": "python.installPythonOnLinux", - "title": "python.installPythonOnLinux", - "keywords": [ - "python install python on linux", - "python install" - ] - }, - { - "id": "python.setInterpreter", - "title": "python.setInterpreter", - "keywords": [ - "python set interpreter", - "python set" - ] - }, - { - "id": "python.clearWorkspaceInterpreter", - "title": "python.clearWorkspaceInterpreter", - "keywords": [ - "python clear workspace interpreter", - "python clear" - ] - }, - { - "id": "python.interpreterPath", - "title": "python.interpreterPath", - "keywords": [ - "python interpreter path", - "python interpreter" - ] - }, - { - "id": "python.pickLocalProcess", - "title": "python.pickLocalProcess", - "keywords": [ - "python pick local process", - "python pick" - ] - }, - { - "id": "python.debugInTerminal", - "title": "python.debugInTerminal", - "keywords": [ - "python debug in terminal", - "python debug" - ] - }, - { - "id": "python.triggerEnvSelection", - "title": "python.triggerEnvSelection", - "keywords": [ - "python trigger env selection", - "python trigger" - ] - }, - { - "id": "python._loadLanguageServerExtension", - "title": "python._loadLanguageServerExtension", - "keywords": [ - "python load language server extension", - "python " - ] - }, - { - "id": "python.createEnvironment", - "title": "python.createEnvironment", - "keywords": [ - "python create environment", - "python create" - ] - }, - { - "id": "python.createEnvironment-button", - "title": "python.createEnvironment-button", - "keywords": [ - "python create environment button", - "python create" - ] - }, - { - "id": "python.startNativeREPL", - "title": "python.startNativeREPL", - "keywords": [ - "python start native repl", - "python start" - ] - }, - { - "id": "python.execInREPL", - "title": "python.execInREPL", - "keywords": [ - "python exec in repl", - "python exec" - ] - }, - { - "id": "python.execInREPLEnter", - "title": "python.execInREPLEnter", - "keywords": [ - "python exec in replenter", - "python exec" - ] - }, - { - "id": "python.execInInteractiveWindowEnter", - "title": "python.execInInteractiveWindowEnter", - "keywords": [ - "python exec in interactive window enter", - "python exec" - ] - }, - { - "id": "debugpy.viewOutput", - "title": "debugpy.viewOutput", - "keywords": [ - "debugpy view output", - "debugpy view" - ] - }, - { - "id": "python.getDebugpyPackagePath", - "title": "python.getDebugpyPackagePath", - "keywords": [ - "python get debugpy package path", - "python get" - ] - }, - { - "id": "debugpy.reportIssue", - "title": "debugpy.reportIssue", - "keywords": [ - "debugpy report issue", - "debugpy report" - ] - }, - { - "id": "debugpy.debugInTerminal", - "title": "debugpy.debugInTerminal", - "keywords": [ - "debugpy debug in terminal", - "debugpy debug" - ] - }, - { - "id": "debugpy.debugUsingLaunchConfig", - "title": "debugpy.debugUsingLaunchConfig", - "keywords": [ - "debugpy debug using launch config", - "debugpy debug" - ] - }, - { - "id": "debugpy.clearCacheAndReload", - "title": "debugpy.clearCacheAndReload", - "keywords": [ - "debugpy clear cache and reload", - "debugpy clear" - ] - }, - { - "id": "debugpy.pickLocalProcess", - "title": "debugpy.pickLocalProcess", - "keywords": [ - "debugpy pick local process", - "debugpy pick" - ] - }, - { - "id": "debugpy.pickArgs", - "title": "debugpy.pickArgs", - "keywords": [ - "debugpy pick args", - "debugpy pick" - ] - }, - { - "id": "debugpy.SelectAndInsertDebugConfiguration", - "title": "debugpy.SelectAndInsertDebugConfiguration", - "keywords": [ - "debugpy select and insert debug configuration", - "debugpy select" - ] - }, - { - "id": "workbench.action.output.show.ms-python.debugpy.Python Debugger.log", - "title": "workbench.action.output.show.ms-python.debugpy.Python Debugger.log", - "keywords": [ - "output show ms python debugpy python debugger log", - "output show" - ] - }, - { - "id": "python.getRecommendedEnvironment", - "title": "python.getRecommendedEnvironment", - "keywords": [ - "python get recommended environment", - "python get" - ] - }, - { - "id": "python.analysis.restartLanguageServer", - "title": "python.analysis.restartLanguageServer", - "keywords": [ - "python analysis restart language server", - "python analysis" - ] - }, - { - "id": "workbench.action.output.show.ms-python.python.Python Language Server.log", - "title": "workbench.action.output.show.ms-python.python.Python Language Server.log", - "keywords": [ - "output show ms python python python language server log", - "output show" - ] - }, - { - "id": "pylance.generateDocumentation", - "title": "pylance.generateDocumentation", - "keywords": [ - "pylance generate documentation", - "pylance generate" - ] - }, - { - "id": "pylance.addSummaryAsDocstringAndDismissTooltip", - "title": "pylance.addSummaryAsDocstringAndDismissTooltip", - "keywords": [ - "pylance add summary as docstring and dismiss tooltip", - "pylance add" - ] - }, - { - "id": "pylance.registerNotebookStartupCommands", - "title": "pylance.registerNotebookStartupCommands", - "keywords": [ - "pylance register notebook startup commands", - "pylance register" - ] - }, - { - "id": "python.viewLanguageServerOutput", - "title": "python.viewLanguageServerOutput", - "keywords": [ - "python view language server output", - "python view" - ] - }, - { - "id": "git.showOutput", - "title": "git.showOutput", - "keywords": [ - "git show output", - "git show" - ] - }, - { - "id": "git.refresh", - "title": "git.refresh", - "keywords": [ - "git refresh" - ] - }, - { - "id": "git.openResource", - "title": "git.openResource", - "keywords": [ - "git open resource", - "git open" - ] - }, - { - "id": "git.openAllChanges", - "title": "git.openAllChanges", - "keywords": [ - "git open all changes", - "git open" - ] - }, - { - "id": "git.openMergeEditor", - "title": "git.openMergeEditor", - "keywords": [ - "git open merge editor", - "git open" - ] - }, - { - "id": "git.continueInLocalClone", - "title": "git.continueInLocalClone", - "keywords": [ - "git continue in local clone", - "git continue" - ] - }, - { - "id": "git.clone", - "title": "git.clone", - "keywords": [ - "git clone" - ] - }, - { - "id": "git.cloneRecursive", - "title": "git.cloneRecursive", - "keywords": [ - "git clone recursive", - "git clone" - ] - }, - { - "id": "git.init", - "title": "git.init", - "keywords": [ - "git init" - ] - }, - { - "id": "git.openRepository", - "title": "git.openRepository", - "keywords": [ - "git open repository", - "git open" - ] - }, - { - "id": "git.reopenClosedRepositories", - "title": "git.reopenClosedRepositories", - "keywords": [ - "git reopen closed repositories", - "git reopen" - ] - }, - { - "id": "git.close", - "title": "git.close", - "keywords": [ - "git close" - ] - }, - { - "id": "git.closeOtherRepositories", - "title": "git.closeOtherRepositories", - "keywords": [ - "git close other repositories", - "git close" - ] - }, - { - "id": "git.openFile", - "title": "git.openFile", - "keywords": [ - "git open file", - "git open" - ] - }, - { - "id": "git.openFile2", - "title": "git.openFile2", - "keywords": [ - "git open file2", - "git open" - ] - }, - { - "id": "git.openHEADFile", - "title": "git.openHEADFile", - "keywords": [ - "git open headfile", - "git open" - ] - }, - { - "id": "git.openChange", - "title": "git.openChange", - "keywords": [ - "git open change", - "git open" - ] - }, - { - "id": "git.compareWithWorkspace", - "title": "git.compareWithWorkspace", - "keywords": [ - "git compare with workspace", - "git compare" - ] - }, - { - "id": "git.rename", - "title": "git.rename", - "keywords": [ - "git rename" - ] - }, - { - "id": "git.delete", - "title": "git.delete", - "keywords": [ - "git delete" - ] - }, - { - "id": "git.stage", - "title": "git.stage", - "keywords": [ - "git stage" - ] - }, - { - "id": "git.stageAll", - "title": "git.stageAll", - "keywords": [ - "git stage all", - "git stage" - ] - }, - { - "id": "git.stageAllTracked", - "title": "git.stageAllTracked", - "keywords": [ - "git stage all tracked", - "git stage" - ] - }, - { - "id": "git.stageAllUntracked", - "title": "git.stageAllUntracked", - "keywords": [ - "git stage all untracked", - "git stage" - ] - }, - { - "id": "git.stageAllMerge", - "title": "git.stageAllMerge", - "keywords": [ - "git stage all merge", - "git stage" - ] - }, - { - "id": "git.stageChange", - "title": "git.stageChange", - "keywords": [ - "git stage change", - "git stage" - ] - }, - { - "id": "git.diff.stageHunk", - "title": "git.diff.stageHunk", - "keywords": [ - "git diff stage hunk", - "git diff" - ] - }, - { - "id": "git.diff.stageSelection", - "title": "git.diff.stageSelection", - "keywords": [ - "git diff stage selection", - "git diff" - ] - }, - { - "id": "git.stageSelectedRanges", - "title": "git.stageSelectedRanges", - "keywords": [ - "git stage selected ranges", - "git stage" - ] - }, - { - "id": "git.stageFile", - "title": "git.stageFile", - "keywords": [ - "git stage file", - "git stage" - ] - }, - { - "id": "git.acceptMerge", - "title": "git.acceptMerge", - "keywords": [ - "git accept merge", - "git accept" - ] - }, - { - "id": "git.runGitMerge", - "title": "git.runGitMerge", - "keywords": [ - "git run git merge", - "git run" - ] - }, - { - "id": "git.runGitMergeDiff3", - "title": "git.runGitMergeDiff3", - "keywords": [ - "git run git merge diff3", - "git run" - ] - }, - { - "id": "git.revertChange", - "title": "git.revertChange", - "keywords": [ - "git revert change", - "git revert" - ] - }, - { - "id": "git.revertSelectedRanges", - "title": "git.revertSelectedRanges", - "keywords": [ - "git revert selected ranges", - "git revert" - ] - }, - { - "id": "git.unstage", - "title": "git.unstage", - "keywords": [ - "git unstage" - ] - }, - { - "id": "git.unstageAll", - "title": "git.unstageAll", - "keywords": [ - "git unstage all", - "git unstage" - ] - }, - { - "id": "git.unstageSelectedRanges", - "title": "git.unstageSelectedRanges", - "keywords": [ - "git unstage selected ranges", - "git unstage" - ] - }, - { - "id": "git.unstageFile", - "title": "git.unstageFile", - "keywords": [ - "git unstage file", - "git unstage" - ] - }, - { - "id": "git.unstageChange", - "title": "git.unstageChange", - "keywords": [ - "git unstage change", - "git unstage" - ] - }, - { - "id": "git.clean", - "title": "git.clean", - "keywords": [ - "git clean" - ] - }, - { - "id": "git.cleanAll", - "title": "git.cleanAll", - "keywords": [ - "git clean all", - "git clean" - ] - }, - { - "id": "git.cleanAllTracked", - "title": "git.cleanAllTracked", - "keywords": [ - "git clean all tracked", - "git clean" - ] - }, - { - "id": "git.cleanAllUntracked", - "title": "git.cleanAllUntracked", - "keywords": [ - "git clean all untracked", - "git clean" - ] - }, - { - "id": "git.commit", - "title": "git.commit", - "keywords": [ - "git commit" - ] - }, - { - "id": "git.commitAmend", - "title": "git.commitAmend", - "keywords": [ - "git commit amend", - "git commit" - ] - }, - { - "id": "git.commitSigned", - "title": "git.commitSigned", - "keywords": [ - "git commit signed", - "git commit" - ] - }, - { - "id": "git.commitStaged", - "title": "git.commitStaged", - "keywords": [ - "git commit staged", - "git commit" - ] - }, - { - "id": "git.commitStagedSigned", - "title": "git.commitStagedSigned", - "keywords": [ - "git commit staged signed", - "git commit" - ] - }, - { - "id": "git.commitStagedAmend", - "title": "git.commitStagedAmend", - "keywords": [ - "git commit staged amend", - "git commit" - ] - }, - { - "id": "git.commitAll", - "title": "git.commitAll", - "keywords": [ - "git commit all", - "git commit" - ] - }, - { - "id": "git.commitAllSigned", - "title": "git.commitAllSigned", - "keywords": [ - "git commit all signed", - "git commit" - ] - }, - { - "id": "git.commitAllAmend", - "title": "git.commitAllAmend", - "keywords": [ - "git commit all amend", - "git commit" - ] - }, - { - "id": "git.commitMessageAccept", - "title": "git.commitMessageAccept", - "keywords": [ - "git commit message accept", - "git commit" - ] - }, - { - "id": "git.commitMessageDiscard", - "title": "git.commitMessageDiscard", - "keywords": [ - "git commit message discard", - "git commit" - ] - }, - { - "id": "git.commitEmpty", - "title": "git.commitEmpty", - "keywords": [ - "git commit empty", - "git commit" - ] - }, - { - "id": "git.commitNoVerify", - "title": "git.commitNoVerify", - "keywords": [ - "git commit no verify", - "git commit" - ] - }, - { - "id": "git.commitStagedNoVerify", - "title": "git.commitStagedNoVerify", - "keywords": [ - "git commit staged no verify", - "git commit" - ] - }, - { - "id": "git.commitStagedSignedNoVerify", - "title": "git.commitStagedSignedNoVerify", - "keywords": [ - "git commit staged signed no verify", - "git commit" - ] - }, - { - "id": "git.commitAmendNoVerify", - "title": "git.commitAmendNoVerify", - "keywords": [ - "git commit amend no verify", - "git commit" - ] - }, - { - "id": "git.commitSignedNoVerify", - "title": "git.commitSignedNoVerify", - "keywords": [ - "git commit signed no verify", - "git commit" - ] - }, - { - "id": "git.commitStagedAmendNoVerify", - "title": "git.commitStagedAmendNoVerify", - "keywords": [ - "git commit staged amend no verify", - "git commit" - ] - }, - { - "id": "git.commitAllNoVerify", - "title": "git.commitAllNoVerify", - "keywords": [ - "git commit all no verify", - "git commit" - ] - }, - { - "id": "git.commitAllSignedNoVerify", - "title": "git.commitAllSignedNoVerify", - "keywords": [ - "git commit all signed no verify", - "git commit" - ] - }, - { - "id": "git.commitAllAmendNoVerify", - "title": "git.commitAllAmendNoVerify", - "keywords": [ - "git commit all amend no verify", - "git commit" - ] - }, - { - "id": "git.commitEmptyNoVerify", - "title": "git.commitEmptyNoVerify", - "keywords": [ - "git commit empty no verify", - "git commit" - ] - }, - { - "id": "git.restoreCommitTemplate", - "title": "git.restoreCommitTemplate", - "keywords": [ - "git restore commit template", - "git restore" - ] - }, - { - "id": "git.undoCommit", - "title": "git.undoCommit", - "keywords": [ - "git undo commit", - "git undo" - ] - }, - { - "id": "git.checkout", - "title": "git.checkout", - "keywords": [ - "git checkout" - ] - }, - { - "id": "git.graph.checkout", - "title": "git.graph.checkout", - "keywords": [ - "git graph checkout", - "git graph" - ] - }, - { - "id": "git.checkoutDetached", - "title": "git.checkoutDetached", - "keywords": [ - "git checkout detached", - "git checkout" - ] - }, - { - "id": "git.graph.checkoutDetached", - "title": "git.graph.checkoutDetached", - "keywords": [ - "git graph checkout detached", - "git graph" - ] - }, - { - "id": "git.branch", - "title": "git.branch", - "keywords": [ - "git branch" - ] - }, - { - "id": "git.branchFrom", - "title": "git.branchFrom", - "keywords": [ - "git branch from", - "git branch" - ] - }, - { - "id": "git.deleteBranch", - "title": "git.deleteBranch", - "keywords": [ - "git delete branch", - "git delete" - ] - }, - { - "id": "git.graph.deleteBranch", - "title": "git.graph.deleteBranch", - "keywords": [ - "git graph delete branch", - "git graph" - ] - }, - { - "id": "git.graph.compareWithRemote", - "title": "git.graph.compareWithRemote", - "keywords": [ - "git graph compare with remote", - "git graph" - ] - }, - { - "id": "git.graph.compareWithMergeBase", - "title": "git.graph.compareWithMergeBase", - "keywords": [ - "git graph compare with merge base", - "git graph" - ] - }, - { - "id": "git.graph.compareRef", - "title": "git.graph.compareRef", - "keywords": [ - "git graph compare ref", - "git graph" - ] - }, - { - "id": "git.deleteRemoteBranch", - "title": "git.deleteRemoteBranch", - "keywords": [ - "git delete remote branch", - "git delete" - ] - }, - { - "id": "git.renameBranch", - "title": "git.renameBranch", - "keywords": [ - "git rename branch", - "git rename" - ] - }, - { - "id": "git.merge", - "title": "git.merge", - "keywords": [ - "git merge" - ] - }, - { - "id": "git.mergeAbort", - "title": "git.mergeAbort", - "keywords": [ - "git merge abort", - "git merge" - ] - }, - { - "id": "git.rebase", - "title": "git.rebase", - "keywords": [ - "git rebase" - ] - }, - { - "id": "git.createTag", - "title": "git.createTag", - "keywords": [ - "git create tag", - "git create" - ] - }, - { - "id": "git.deleteTag", - "title": "git.deleteTag", - "keywords": [ - "git delete tag", - "git delete" - ] - }, - { - "id": "git.migrateWorktreeChanges", - "title": "git.migrateWorktreeChanges", - "keywords": [ - "git migrate worktree changes", - "git migrate" - ] - }, - { - "id": "git.openWorktreeMergeEditor", - "title": "git.openWorktreeMergeEditor", - "keywords": [ - "git open worktree merge editor", - "git open" - ] - }, - { - "id": "git.createWorktree", - "title": "git.createWorktree", - "keywords": [ - "git create worktree", - "git create" - ] - }, - { - "id": "git.deleteWorktree", - "title": "git.deleteWorktree", - "keywords": [ - "git delete worktree", - "git delete" - ] - }, - { - "id": "git.deleteWorktree2", - "title": "git.deleteWorktree2", - "keywords": [ - "git delete worktree2", - "git delete" - ] - }, - { - "id": "git.openWorktree", - "title": "git.openWorktree", - "keywords": [ - "git open worktree", - "git open" - ] - }, - { - "id": "git.openWorktreeInNewWindow", - "title": "git.openWorktreeInNewWindow", - "keywords": [ - "git open worktree in new window", - "git open" - ] - }, - { - "id": "git.graph.deleteTag", - "title": "git.graph.deleteTag", - "keywords": [ - "git graph delete tag", - "git graph" - ] - }, - { - "id": "git.deleteRemoteTag", - "title": "git.deleteRemoteTag", - "keywords": [ - "git delete remote tag", - "git delete" - ] - }, - { - "id": "git.fetch", - "title": "git.fetch", - "keywords": [ - "git fetch" - ] - }, - { - "id": "git.fetchPrune", - "title": "git.fetchPrune", - "keywords": [ - "git fetch prune", - "git fetch" - ] - }, - { - "id": "git.fetchAll", - "title": "git.fetchAll", - "keywords": [ - "git fetch all", - "git fetch" - ] - }, - { - "id": "git.fetchRef", - "title": "git.fetchRef", - "keywords": [ - "git fetch ref", - "git fetch" - ] - }, - { - "id": "git.pullFrom", - "title": "git.pullFrom", - "keywords": [ - "git pull from", - "git pull" - ] - }, - { - "id": "git.pull", - "title": "git.pull", - "keywords": [ - "git pull" - ] - }, - { - "id": "git.pullRebase", - "title": "git.pullRebase", - "keywords": [ - "git pull rebase", - "git pull" - ] - }, - { - "id": "git.pullRef", - "title": "git.pullRef", - "keywords": [ - "git pull ref", - "git pull" - ] - }, - { - "id": "git.push", - "title": "git.push", - "keywords": [ - "git push" - ] - }, - { - "id": "git.pushForce", - "title": "git.pushForce", - "keywords": [ - "git push force", - "git push" - ] - }, - { - "id": "git.pushWithTags", - "title": "git.pushWithTags", - "keywords": [ - "git push with tags", - "git push" - ] - }, - { - "id": "git.pushWithTagsForce", - "title": "git.pushWithTagsForce", - "keywords": [ - "git push with tags force", - "git push" - ] - }, - { - "id": "git.pushRef", - "title": "git.pushRef", - "keywords": [ - "git push ref", - "git push" - ] - }, - { - "id": "git.cherryPick", - "title": "git.cherryPick", - "keywords": [ - "git cherry pick", - "git cherry" - ] - }, - { - "id": "git.graph.cherryPick", - "title": "git.graph.cherryPick", - "keywords": [ - "git graph cherry pick", - "git graph" - ] - }, - { - "id": "git.cherryPickAbort", - "title": "git.cherryPickAbort", - "keywords": [ - "git cherry pick abort", - "git cherry" - ] - }, - { - "id": "git.pushTo", - "title": "git.pushTo", - "keywords": [ - "git push to", - "git push" - ] - }, - { - "id": "git.pushToForce", - "title": "git.pushToForce", - "keywords": [ - "git push to force", - "git push" - ] - }, - { - "id": "git.pushTags", - "title": "git.pushTags", - "keywords": [ - "git push tags", - "git push" - ] - }, - { - "id": "git.addRemote", - "title": "git.addRemote", - "keywords": [ - "git add remote", - "git add" - ] - }, - { - "id": "git.removeRemote", - "title": "git.removeRemote", - "keywords": [ - "git remove remote", - "git remove" - ] - }, - { - "id": "git.sync", - "title": "git.sync", - "keywords": [ - "git sync" - ] - }, - { - "id": "git._syncAll", - "title": "git._syncAll", - "keywords": [ - "git sync all", - "git " - ] - }, - { - "id": "git.syncRebase", - "title": "git.syncRebase", - "keywords": [ - "git sync rebase", - "git sync" - ] - }, - { - "id": "git.publish", - "title": "git.publish", - "keywords": [ - "git publish" - ] - }, - { - "id": "git.ignore", - "title": "git.ignore", - "keywords": [ - "git ignore" - ] - }, - { - "id": "git.revealInExplorer", - "title": "git.revealInExplorer", - "keywords": [ - "git reveal in explorer", - "git reveal" - ] - }, - { - "id": "git.revealFileInOS.windows", - "title": "git.revealFileInOS.windows", - "keywords": [ - "git reveal file in os windows", - "git reveal" - ] - }, - { - "id": "git.revealFileInOS.mac", - "title": "git.revealFileInOS.mac", - "keywords": [ - "git reveal file in os mac", - "git reveal" - ] - }, - { - "id": "git.revealFileInOS.linux", - "title": "git.revealFileInOS.linux", - "keywords": [ - "git reveal file in os linux", - "git reveal" - ] - }, - { - "id": "git.stash", - "title": "git.stash", - "keywords": [ - "git stash" - ] - }, - { - "id": "git.stashStaged", - "title": "git.stashStaged", - "keywords": [ - "git stash staged", - "git stash" - ] - }, - { - "id": "git.stashIncludeUntracked", - "title": "git.stashIncludeUntracked", - "keywords": [ - "git stash include untracked", - "git stash" - ] - }, - { - "id": "git.stashPop", - "title": "git.stashPop", - "keywords": [ - "git stash pop", - "git stash" - ] - }, - { - "id": "git.stashPopLatest", - "title": "git.stashPopLatest", - "keywords": [ - "git stash pop latest", - "git stash" - ] - }, - { - "id": "git.stashPopEditor", - "title": "git.stashPopEditor", - "keywords": [ - "git stash pop editor", - "git stash" - ] - }, - { - "id": "git.stashApply", - "title": "git.stashApply", - "keywords": [ - "git stash apply", - "git stash" - ] - }, - { - "id": "git.stashApplyLatest", - "title": "git.stashApplyLatest", - "keywords": [ - "git stash apply latest", - "git stash" - ] - }, - { - "id": "git.stashApplyEditor", - "title": "git.stashApplyEditor", - "keywords": [ - "git stash apply editor", - "git stash" - ] - }, - { - "id": "git.stashDrop", - "title": "git.stashDrop", - "keywords": [ - "git stash drop", - "git stash" - ] - }, - { - "id": "git.stashDropAll", - "title": "git.stashDropAll", - "keywords": [ - "git stash drop all", - "git stash" - ] - }, - { - "id": "git.stashDropEditor", - "title": "git.stashDropEditor", - "keywords": [ - "git stash drop editor", - "git stash" - ] - }, - { - "id": "git.stashView", - "title": "git.stashView", - "keywords": [ - "git stash view", - "git stash" - ] - }, - { - "id": "git.timeline.openDiff", - "title": "git.timeline.openDiff", - "keywords": [ - "git timeline open diff", - "git timeline" - ] - }, - { - "id": "git.timeline.viewCommit", - "title": "git.timeline.viewCommit", - "keywords": [ - "git timeline view commit", - "git timeline" - ] - }, - { - "id": "git.timeline.copyCommitId", - "title": "git.timeline.copyCommitId", - "keywords": [ - "git timeline copy commit id", - "git timeline" - ] - }, - { - "id": "git.timeline.copyCommitMessage", - "title": "git.timeline.copyCommitMessage", - "keywords": [ - "git timeline copy commit message", - "git timeline" - ] - }, - { - "id": "git.timeline.selectForCompare", - "title": "git.timeline.selectForCompare", - "keywords": [ - "git timeline select for compare", - "git timeline" - ] - }, - { - "id": "git.timeline.compareWithSelected", - "title": "git.timeline.compareWithSelected", - "keywords": [ - "git timeline compare with selected", - "git timeline" - ] - }, - { - "id": "git.rebaseAbort", - "title": "git.rebaseAbort", - "keywords": [ - "git rebase abort", - "git rebase" - ] - }, - { - "id": "git.closeAllDiffEditors", - "title": "git.closeAllDiffEditors", - "keywords": [ - "git close all diff editors", - "git close" - ] - }, - { - "id": "git.closeAllUnmodifiedEditors", - "title": "git.closeAllUnmodifiedEditors", - "keywords": [ - "git close all unmodified editors", - "git close" - ] - }, - { - "id": "git.openRepositoriesInParentFolders", - "title": "git.openRepositoriesInParentFolders", - "keywords": [ - "git open repositories in parent folders", - "git open" - ] - }, - { - "id": "git.manageUnsafeRepositories", - "title": "git.manageUnsafeRepositories", - "keywords": [ - "git manage unsafe repositories", - "git manage" - ] - }, - { - "id": "git.viewChanges", - "title": "git.viewChanges", - "keywords": [ - "git view changes", - "git view" - ] - }, - { - "id": "git.viewStagedChanges", - "title": "git.viewStagedChanges", - "keywords": [ - "git view staged changes", - "git view" - ] - }, - { - "id": "git.viewUntrackedChanges", - "title": "git.viewUntrackedChanges", - "keywords": [ - "git view untracked changes", - "git view" - ] - }, - { - "id": "git.copyCommitId", - "title": "git.copyCommitId", - "keywords": [ - "git copy commit id", - "git copy" - ] - }, - { - "id": "git.copyCommitMessage", - "title": "git.copyCommitMessage", - "keywords": [ - "git copy commit message", - "git copy" - ] - }, - { - "id": "git.viewCommit", - "title": "git.viewCommit", - "keywords": [ - "git view commit", - "git view" - ] - }, - { - "id": "git.copyContentToClipboard", - "title": "git.copyContentToClipboard", - "keywords": [ - "git copy content to clipboard", - "git copy" - ] - }, - { - "id": "git.blame.toggleEditorDecoration", - "title": "git.blame.toggleEditorDecoration", - "keywords": [ - "git blame toggle editor decoration", - "git blame" - ] - }, - { - "id": "git.blame.toggleStatusBarItem", - "title": "git.blame.toggleStatusBarItem", - "keywords": [ - "git blame toggle status bar item", - "git blame" - ] - }, - { - "id": "git.repositories.createBranch", - "title": "git.repositories.createBranch", - "keywords": [ - "git repositories create branch", - "git repositories" - ] - }, - { - "id": "git.repositories.createTag", - "title": "git.repositories.createTag", - "keywords": [ - "git repositories create tag", - "git repositories" - ] - }, - { - "id": "git.repositories.createWorktree", - "title": "git.repositories.createWorktree", - "keywords": [ - "git repositories create worktree", - "git repositories" - ] - }, - { - "id": "git.repositories.checkout", - "title": "git.repositories.checkout", - "keywords": [ - "git repositories checkout", - "git repositories" - ] - }, - { - "id": "git.repositories.checkoutDetached", - "title": "git.repositories.checkoutDetached", - "keywords": [ - "git repositories checkout detached", - "git repositories" - ] - }, - { - "id": "git.repositories.merge", - "title": "git.repositories.merge", - "keywords": [ - "git repositories merge", - "git repositories" - ] - }, - { - "id": "git.repositories.rebase", - "title": "git.repositories.rebase", - "keywords": [ - "git repositories rebase", - "git repositories" - ] - }, - { - "id": "git.repositories.createFrom", - "title": "git.repositories.createFrom", - "keywords": [ - "git repositories create from", - "git repositories" - ] - }, - { - "id": "git.repositories.compareRef", - "title": "git.repositories.compareRef", - "keywords": [ - "git repositories compare ref", - "git repositories" - ] - }, - { - "id": "git.repositories.deleteBranch", - "title": "git.repositories.deleteBranch", - "keywords": [ - "git repositories delete branch", - "git repositories" - ] - }, - { - "id": "git.repositories.deleteTag", - "title": "git.repositories.deleteTag", - "keywords": [ - "git repositories delete tag", - "git repositories" - ] - }, - { - "id": "git.repositories.stashView", - "title": "git.repositories.stashView", - "keywords": [ - "git repositories stash view", - "git repositories" - ] - }, - { - "id": "git.repositories.stashApply", - "title": "git.repositories.stashApply", - "keywords": [ - "git repositories stash apply", - "git repositories" - ] - }, - { - "id": "git.repositories.stashPop", - "title": "git.repositories.stashPop", - "keywords": [ - "git repositories stash pop", - "git repositories" - ] - }, - { - "id": "git.repositories.stashDrop", - "title": "git.repositories.stashDrop", - "keywords": [ - "git repositories stash drop", - "git repositories" - ] - }, - { - "id": "git.repositories.openWorktree", - "title": "git.repositories.openWorktree", - "keywords": [ - "git repositories open worktree", - "git repositories" - ] - }, - { - "id": "git.repositories.openWorktreeInNewWindow", - "title": "git.repositories.openWorktreeInNewWindow", - "keywords": [ - "git repositories open worktree in new window", - "git repositories" - ] - }, - { - "id": "git.repositories.deleteWorktree", - "title": "git.repositories.deleteWorktree", - "keywords": [ - "git repositories delete worktree", - "git repositories" - ] - }, - { - "id": "git.repositories.worktreeCopyBranchName", - "title": "git.repositories.worktreeCopyBranchName", - "keywords": [ - "git repositories worktree copy branch name", - "git repositories" - ] - }, - { - "id": "git.repositories.worktreeCopyCommitHash", - "title": "git.repositories.worktreeCopyCommitHash", - "keywords": [ - "git repositories worktree copy commit hash", - "git repositories" - ] - }, - { - "id": "git.repositories.worktreeCopyPath", - "title": "git.repositories.worktreeCopyPath", - "keywords": [ - "git repositories worktree copy path", - "git repositories" - ] - }, - { - "id": "git.repositories.copyCommitHash", - "title": "git.repositories.copyCommitHash", - "keywords": [ - "git repositories copy commit hash", - "git repositories" - ] - }, - { - "id": "git.repositories.copyBranchName", - "title": "git.repositories.copyBranchName", - "keywords": [ - "git repositories copy branch name", - "git repositories" - ] - }, - { - "id": "git.repositories.copyTagName", - "title": "git.repositories.copyTagName", - "keywords": [ - "git repositories copy tag name", - "git repositories" - ] - }, - { - "id": "git.repositories.copyStashName", - "title": "git.repositories.copyStashName", - "keywords": [ - "git repositories copy stash name", - "git repositories" - ] - }, - { - "id": "git.repositories.stashCopyBranchName", - "title": "git.repositories.stashCopyBranchName", - "keywords": [ - "git repositories stash copy branch name", - "git repositories" - ] - }, - { - "id": "git.api.getRepositories", - "title": "git.api.getRepositories", - "keywords": [ - "git api get repositories", - "git api" - ] - }, - { - "id": "git.api.getRepositoryState", - "title": "git.api.getRepositoryState", - "keywords": [ - "git api get repository state", - "git api" - ] - }, - { - "id": "git.api.getRemoteSources", - "title": "git.api.getRemoteSources", - "keywords": [ - "git api get remote sources", - "git api" - ] - }, - { - "id": "github.publish", - "title": "github.publish", - "keywords": [ - "github publish" - ] - }, - { - "id": "github.copyVscodeDevLink", - "title": "github.copyVscodeDevLink", - "keywords": [ - "github copy vscode dev link", - "github copy" - ] - }, - { - "id": "github.copyVscodeDevLinkFile", - "title": "github.copyVscodeDevLinkFile", - "keywords": [ - "github copy vscode dev link file", - "github copy" - ] - }, - { - "id": "github.copyVscodeDevLinkWithoutRange", - "title": "github.copyVscodeDevLinkWithoutRange", - "keywords": [ - "github copy vscode dev link without range", - "github copy" - ] - }, - { - "id": "github.openOnGitHub", - "title": "github.openOnGitHub", - "keywords": [ - "github open on git hub", - "github open" - ] - }, - { - "id": "github.graph.openOnGitHub", - "title": "github.graph.openOnGitHub", - "keywords": [ - "github graph open on git hub", - "github graph" - ] - }, - { - "id": "github.timeline.openOnGitHub", - "title": "github.timeline.openOnGitHub", - "keywords": [ - "github timeline open on git hub", - "github timeline" - ] - }, - { - "id": "github.openOnVscodeDev", - "title": "github.openOnVscodeDev", - "keywords": [ - "github open on vscode dev", - "github open" - ] - }, - { - "id": "github.createPullRequest", - "title": "github.createPullRequest", - "keywords": [ - "github create pull request", - "github create" - ] - }, - { - "id": "github.openPullRequest", - "title": "github.openPullRequest", - "keywords": [ - "github open pull request", - "github open" - ] - }, - { - "id": "workbench.action.output.show.extension-output-rvest.vs-code-prettier-eslint-#1-Prettier Eslint", - "title": "workbench.action.output.show.extension-output-rvest.vs-code-prettier-eslint-#1-Prettier Eslint", - "keywords": [ - "output show extension output rvest vs code prettier eslint #1 prettier eslint", - "output show" - ] - }, - { - "id": "extension.node-debug.toggleAutoAttach", - "title": "extension.node-debug.toggleAutoAttach", - "keywords": [ - "node debug toggle auto attach", - "node debug" - ] - }, - { - "id": "merge-conflict.accept.current", - "title": "merge-conflict.accept.current", - "keywords": [ - "merge conflict accept current", - "merge conflict" - ] - }, - { - "id": "merge-conflict.accept.incoming", - "title": "merge-conflict.accept.incoming", - "keywords": [ - "merge conflict accept incoming", - "merge conflict" - ] - }, - { - "id": "merge-conflict.accept.selection", - "title": "merge-conflict.accept.selection", - "keywords": [ - "merge conflict accept selection", - "merge conflict" - ] - }, - { - "id": "merge-conflict.accept.both", - "title": "merge-conflict.accept.both", - "keywords": [ - "merge conflict accept both", - "merge conflict" - ] - }, - { - "id": "merge-conflict.accept.all-current", - "title": "merge-conflict.accept.all-current", - "keywords": [ - "merge conflict accept all current", - "merge conflict" - ] - }, - { - "id": "merge-conflict.accept.all-incoming", - "title": "merge-conflict.accept.all-incoming", - "keywords": [ - "merge conflict accept all incoming", - "merge conflict" - ] - }, - { - "id": "merge-conflict.accept.all-both", - "title": "merge-conflict.accept.all-both", - "keywords": [ - "merge conflict accept all both", - "merge conflict" - ] - }, - { - "id": "merge-conflict.next", - "title": "merge-conflict.next", - "keywords": [ - "merge conflict next", - "merge conflict" - ] - }, - { - "id": "merge-conflict.previous", - "title": "merge-conflict.previous", - "keywords": [ - "merge conflict previous", - "merge conflict" - ] - }, - { - "id": "merge-conflict.compare", - "title": "merge-conflict.compare", - "keywords": [ - "merge conflict compare", - "merge conflict" - ] - }, - { - "id": "vscode-html-css.validate", - "title": "vscode-html-css.validate", - "keywords": [ - "vscode html css validate", - "vscode html" - ] - }, - { - "id": "vscode-html-css.clear", - "title": "vscode-html-css.clear", - "keywords": [ - "vscode html css clear", - "vscode html" - ] - }, - { - "id": "code-runner.run", - "title": "code-runner.run", - "keywords": [ - "code runner run", - "code runner" - ] - }, - { - "id": "code-runner.runCustomCommand", - "title": "code-runner.runCustomCommand", - "keywords": [ - "code runner run custom command", - "code runner" - ] - }, - { - "id": "code-runner.runByLanguage", - "title": "code-runner.runByLanguage", - "keywords": [ - "code runner run by language", - "code runner" - ] - }, - { - "id": "code-runner.stop", - "title": "code-runner.stop", - "keywords": [ - "code runner stop", - "code runner" - ] - }, - { - "id": "workbench.action.output.show.extension-output-formulahendry.code-runner-#1-Code", - "title": "workbench.action.output.show.extension-output-formulahendry.code-runner-#1-Code", - "keywords": [ - "output show extension output formulahendry code runner #1 code", - "output show" - ] - }, - { - "id": "github.api.preloadPullRequest", - "title": "github.api.preloadPullRequest", - "keywords": [ - "github api preload pull request", - "github api" - ] - }, - { - "id": "workbench.action.output.show.extension-output-GitLab.gitlab-workflow-#1-GitLab", - "title": "workbench.action.output.show.extension-output-GitLab.gitlab-workflow-#1-GitLab", - "keywords": [ - "output show extension output git lab gitlab workflow #1 git lab", - "output show" - ] - }, - { - "id": "prettier-sql-vscode.format-selection", - "title": "prettier-sql-vscode.format-selection", - "keywords": [ - "prettier sql vscode format selection", - "prettier sql" - ] - }, - { - "id": "dotnet.acquire", - "title": "dotnet.acquire", - "keywords": [ - "dotnet acquire" - ] - }, - { - "id": "dotnet.acquireGlobalSDK", - "title": "dotnet.acquireGlobalSDK", - "keywords": [ - "dotnet acquire global sdk", - "dotnet acquire" - ] - }, - { - "id": "dotnet.listVersions", - "title": "dotnet.listVersions", - "keywords": [ - "dotnet list versions", - "dotnet list" - ] - }, - { - "id": "dotnet.recommendedVersion", - "title": "dotnet.recommendedVersion", - "keywords": [ - "dotnet recommended version", - "dotnet recommended" - ] - }, - { - "id": "dotnet.acquireGlobalSDKPublic", - "title": "dotnet.acquireGlobalSDKPublic", - "keywords": [ - "dotnet acquire global sdkpublic", - "dotnet acquire" - ] - }, - { - "id": "dotnet.acquireStatus", - "title": "dotnet.acquireStatus", - "keywords": [ - "dotnet acquire status", - "dotnet acquire" - ] - }, - { - "id": "dotnet.availableInstalls", - "title": "dotnet.availableInstalls", - "keywords": [ - "dotnet available installs", - "dotnet available" - ] - }, - { - "id": "dotnet.resetData", - "title": "dotnet.resetData", - "keywords": [ - "dotnet reset data", - "dotnet reset" - ] - }, - { - "id": "dotnet._resetUpdateTimer", - "title": "dotnet._resetUpdateTimer", - "keywords": [ - "dotnet reset update timer", - "dotnet " - ] - }, - { - "id": "dotnet.uninstallPublic", - "title": "dotnet.uninstallPublic", - "keywords": [ - "dotnet uninstall public", - "dotnet uninstall" - ] - }, - { - "id": "dotnet.uninstall", - "title": "dotnet.uninstall", - "keywords": [ - "dotnet uninstall" - ] - }, - { - "id": "dotnet.forceUpdate", - "title": "dotnet.forceUpdate", - "keywords": [ - "dotnet force update", - "dotnet force" - ] - }, - { - "id": "dotnet.findPath", - "title": "dotnet.findPath", - "keywords": [ - "dotnet find path", - "dotnet find" - ] - }, - { - "id": "dotnet.uninstallAll", - "title": "dotnet.uninstallAll", - "keywords": [ - "dotnet uninstall all", - "dotnet uninstall" - ] - }, - { - "id": "dotnet.showAcquisitionLog", - "title": "dotnet.showAcquisitionLog", - "keywords": [ - "dotnet show acquisition log", - "dotnet show" - ] - }, - { - "id": "dotnet.getAcquisitionLog", - "title": "dotnet.getAcquisitionLog", - "keywords": [ - "dotnet get acquisition log", - "dotnet get" - ] - }, - { - "id": "dotnet.ensureDotnetDependencies", - "title": "dotnet.ensureDotnetDependencies", - "keywords": [ - "dotnet ensure dotnet dependencies", - "dotnet ensure" - ] - }, - { - "id": "dotnet.reportIssue", - "title": "dotnet.reportIssue", - "keywords": [ - "dotnet report issue", - "dotnet report" - ] - }, - { - "id": "workbench.action.output.show.extension-output-ms-dotnettools.vscode-dotnet-runtime-#1-.NET Install Tool", - "title": "workbench.action.output.show.extension-output-ms-dotnettools.vscode-dotnet-runtime-#1-.NET Install Tool", - "keywords": [ - "output show extension output ms dotnettools vscode dotnet runtime #1 net install tool", - "output show" - ] - }, - { - "id": "extension.liveServer.goOnline", - "title": "extension.liveServer.goOnline", - "keywords": [ - "live server go online", - "live server" - ] - }, - { - "id": "extension.liveServer.goOffline", - "title": "extension.liveServer.goOffline", - "keywords": [ - "live server go offline", - "live server" - ] - }, - { - "id": "extension.liveServer.changeWorkspace", - "title": "extension.liveServer.changeWorkspace", - "keywords": [ - "live server change workspace", - "live server" - ] - }, - { - "id": "workbench.action.output.show.extension-output-EchoCode.echo-code-#1-EchoCode", - "title": "workbench.action.output.show.extension-output-EchoCode.echo-code-#1-EchoCode", - "keywords": [ - "output show extension output echo code echo code #1 echo code", - "output show" - ] - }, - { - "id": "workbench.action.output.show.GitHub.vscode-pull-request-github.GitHub Pull Request.log", - "title": "workbench.action.output.show.GitHub.vscode-pull-request-github.GitHub Pull Request.log", - "keywords": [ - "output show git hub vscode pull request github git hub pull request log", - "output show" - ] - }, - { - "id": "workbench.action.output.show.extension-output-esbenp.prettier-vscode-#1-Prettier", - "title": "workbench.action.output.show.extension-output-esbenp.prettier-vscode-#1-Prettier", - "keywords": [ - "output show extension output esbenp prettier vscode #1 prettier", - "output show" - ] - }, - { - "id": "oneDarkPro.showChangelog", - "title": "oneDarkPro.showChangelog", - "keywords": [ - "one dark pro show changelog", - "one dark" - ] - }, - { - "id": "oneDarkPro.setVivid", - "title": "oneDarkPro.setVivid", - "keywords": [ - "one dark pro set vivid", - "one dark" - ] - }, - { - "id": "oneDarkPro.cancelVivid", - "title": "oneDarkPro.cancelVivid", - "keywords": [ - "one dark pro cancel vivid", - "one dark" - ] - }, - { - "id": "oneDarkPro.setItalic", - "title": "oneDarkPro.setItalic", - "keywords": [ - "one dark pro set italic", - "one dark" - ] - }, - { - "id": "oneDarkPro.cancelItalic", - "title": "oneDarkPro.cancelItalic", - "keywords": [ - "one dark pro cancel italic", - "one dark" - ] - }, - { - "id": "oneDarkPro.setBold", - "title": "oneDarkPro.setBold", - "keywords": [ - "one dark pro set bold", - "one dark" - ] - }, - { - "id": "oneDarkPro.cancelBold", - "title": "oneDarkPro.cancelBold", - "keywords": [ - "one dark pro cancel bold", - "one dark" - ] - }, - { - "id": "issuesAndMrs.toggleVisibility", - "title": "issuesAndMrs.toggleVisibility", - "keywords": [ - "issues and mrs toggle visibility", - "issues and" - ] - }, - { - "id": "issuesAndMrs.removeView", - "title": "issuesAndMrs.removeView", - "keywords": [ - "issues and mrs remove view", - "issues and" - ] - }, - { - "id": "formatter/configure/dfl/5c495ce8-029a-499c-a095-72bd02be46db", - "title": "formatter/configure/dfl/5c495ce8-029a-499c-a095-72bd02be46db", - "keywords": [ - "formatter/configure/dfl/5c495ce8 029a 499c a095 72bd02be46db", - "formatter/configure/dfl/5c495ce8 029a" - ] - }, - { - "id": "prettier.createConfigFile", - "title": "prettier.createConfigFile", - "keywords": [ - "prettier create config file", - "prettier create" - ] - }, - { - "id": "prettier.openOutput", - "title": "prettier.openOutput", - "keywords": [ - "prettier open output", - "prettier open" - ] - }, - { - "id": "prettier.forceFormatDocument", - "title": "prettier.forceFormatDocument", - "keywords": [ - "prettier force format document", - "prettier force" - ] - }, - { - "id": "terminal.integrated.suggest.clearCachedGlobals", - "title": "terminal.integrated.suggest.clearCachedGlobals", - "keywords": [ - "terminal integrated suggest clear cached globals", - "terminal integrated" - ] - }, - { - "id": "github.copilot.interactiveSession.feedback", - "title": "github.copilot.interactiveSession.feedback", - "keywords": [ - "github copilot interactive session feedback", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.compact", - "title": "github.copilot.chat.compact", - "keywords": [ - "github copilot chat compact", - "github copilot" - ] - }, - { - "id": "github.copilot.terminal.explainTerminalLastCommand", - "title": "github.copilot.terminal.explainTerminalLastCommand", - "keywords": [ - "github copilot terminal explain terminal last command", - "github copilot" - ] - }, - { - "id": "github.copilot.terminal.fixTerminalLastCommand", - "title": "github.copilot.terminal.fixTerminalLastCommand", - "keywords": [ - "github copilot terminal fix terminal last command", - "github copilot" - ] - }, - { - "id": "github.copilot.terminal.generateCommitMessage", - "title": "github.copilot.terminal.generateCommitMessage", - "keywords": [ - "github copilot terminal generate commit message", - "github copilot" - ] - }, - { - "id": "github.copilot.git.generateCommitMessage", - "title": "github.copilot.git.generateCommitMessage", - "keywords": [ - "github copilot git generate commit message", - "github copilot" - ] - }, - { - "id": "github.copilot.git.resolveMergeConflicts", - "title": "github.copilot.git.resolveMergeConflicts", - "keywords": [ - "github copilot git resolve merge conflicts", - "github copilot" - ] - }, - { - "id": "github.copilot.devcontainer.generateDevContainerConfig", - "title": "github.copilot.devcontainer.generateDevContainerConfig", - "keywords": [ - "github copilot devcontainer generate dev container config", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.openUserPreferences", - "title": "github.copilot.chat.openUserPreferences", - "keywords": [ - "github copilot chat open user preferences", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.explain", - "title": "github.copilot.chat.explain", - "keywords": [ - "github copilot chat explain", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.explain.palette", - "title": "github.copilot.chat.explain.palette", - "keywords": [ - "github copilot chat explain palette", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review", - "title": "github.copilot.chat.review", - "keywords": [ - "github copilot chat review", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.stagedChanges", - "title": "github.copilot.chat.review.stagedChanges", - "keywords": [ - "github copilot chat review staged changes", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.unstagedChanges", - "title": "github.copilot.chat.review.unstagedChanges", - "keywords": [ - "github copilot chat review unstaged changes", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.changes", - "title": "github.copilot.chat.review.changes", - "keywords": [ - "github copilot chat review changes", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.stagedFileChange", - "title": "github.copilot.chat.review.stagedFileChange", - "keywords": [ - "github copilot chat review staged file change", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.unstagedFileChange", - "title": "github.copilot.chat.review.unstagedFileChange", - "keywords": [ - "github copilot chat review unstaged file change", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.codeReview.run", - "title": "github.copilot.chat.codeReview.run", - "keywords": [ - "github copilot chat code review run", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.apply", - "title": "github.copilot.chat.review.apply", - "keywords": [ - "github copilot chat review apply", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.applyAndNext", - "title": "github.copilot.chat.review.applyAndNext", - "keywords": [ - "github copilot chat review apply and next", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.applyShort", - "title": "github.copilot.chat.review.applyShort", - "keywords": [ - "github copilot chat review apply short", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.continueInInlineChat", - "title": "github.copilot.chat.review.continueInInlineChat", - "keywords": [ - "github copilot chat review continue in inline chat", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.continueInChat", - "title": "github.copilot.chat.review.continueInChat", - "keywords": [ - "github copilot chat review continue in chat", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.discard", - "title": "github.copilot.chat.review.discard", - "keywords": [ - "github copilot chat review discard", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.discardAndNext", - "title": "github.copilot.chat.review.discardAndNext", - "keywords": [ - "github copilot chat review discard and next", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.discardShort", - "title": "github.copilot.chat.review.discardShort", - "keywords": [ - "github copilot chat review discard short", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.discardAll", - "title": "github.copilot.chat.review.discardAll", - "keywords": [ - "github copilot chat review discard all", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.markHelpful", - "title": "github.copilot.chat.review.markHelpful", - "keywords": [ - "github copilot chat review mark helpful", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.markUnhelpful", - "title": "github.copilot.chat.review.markUnhelpful", - "keywords": [ - "github copilot chat review mark unhelpful", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.previous", - "title": "github.copilot.chat.review.previous", - "keywords": [ - "github copilot chat review previous", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.next", - "title": "github.copilot.chat.review.next", - "keywords": [ - "github copilot chat review next", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.review.current", - "title": "github.copilot.chat.review.current", - "keywords": [ - "github copilot chat review current", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.generate", - "title": "github.copilot.chat.generate", - "keywords": [ - "github copilot chat generate", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.fix", - "title": "github.copilot.chat.fix", - "keywords": [ - "github copilot chat fix", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.generateAltText", - "title": "github.copilot.chat.generateAltText", - "keywords": [ - "github copilot chat generate alt text", - "github copilot" - ] - }, - { - "id": "github.copilot.createProject", - "title": "github.copilot.createProject", - "keywords": [ - "github copilot create project", - "github copilot" - ] - }, - { - "id": "github.copilot.openFile", - "title": "github.copilot.openFile", - "keywords": [ - "github copilot open file", - "github copilot" - ] - }, - { - "id": "github.copilot.createFile", - "title": "github.copilot.createFile", - "keywords": [ - "github copilot create file", - "github copilot" - ] - }, - { - "id": "github.copilot.executeSearch", - "title": "github.copilot.executeSearch", - "keywords": [ - "github copilot execute search", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.showElements", - "title": "github.copilot.chat.debug.showElements", - "keywords": [ - "github copilot chat debug show elements", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.hideElements", - "title": "github.copilot.chat.debug.hideElements", - "keywords": [ - "github copilot chat debug hide elements", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.showTools", - "title": "github.copilot.chat.debug.showTools", - "keywords": [ - "github copilot chat debug show tools", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.hideTools", - "title": "github.copilot.chat.debug.hideTools", - "keywords": [ - "github copilot chat debug hide tools", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.showNesRequests", - "title": "github.copilot.chat.debug.showNesRequests", - "keywords": [ - "github copilot chat debug show nes requests", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.hideNesRequests", - "title": "github.copilot.chat.debug.hideNesRequests", - "keywords": [ - "github copilot chat debug hide nes requests", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.showGhostRequests", - "title": "github.copilot.chat.debug.showGhostRequests", - "keywords": [ - "github copilot chat debug show ghost requests", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.hideGhostRequests", - "title": "github.copilot.chat.debug.hideGhostRequests", - "keywords": [ - "github copilot chat debug hide ghost requests", - "github copilot" - ] - }, - { - "id": "vscode.copilot.chat.showRequestHtmlItem", - "title": "vscode.copilot.chat.showRequestHtmlItem", - "keywords": [ - "vscode copilot chat show request html item", - "vscode copilot" - ] - }, - { - "id": "github.copilot.chat.debug.exportLogItem", - "title": "github.copilot.chat.debug.exportLogItem", - "keywords": [ - "github copilot chat debug export log item", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.saveCurrentMarkdown", - "title": "github.copilot.chat.debug.saveCurrentMarkdown", - "keywords": [ - "github copilot chat debug save current markdown", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.exportPromptArchive", - "title": "github.copilot.chat.debug.exportPromptArchive", - "keywords": [ - "github copilot chat debug export prompt archive", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.exportPromptLogsAsJson", - "title": "github.copilot.chat.debug.exportPromptLogsAsJson", - "keywords": [ - "github copilot chat debug export prompt logs as json", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.exportAllPromptLogsAsJson", - "title": "github.copilot.chat.debug.exportAllPromptLogsAsJson", - "keywords": [ - "github copilot chat debug export all prompt logs as json", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.debug.showRawRequestBody", - "title": "github.copilot.chat.debug.showRawRequestBody", - "keywords": [ - "github copilot chat debug show raw request body", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.showOutputChannel", - "title": "github.copilot.debug.showOutputChannel", - "keywords": [ - "github copilot debug show output channel", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.rerunWithCopilotDebug", - "title": "github.copilot.chat.rerunWithCopilotDebug", - "keywords": [ - "github copilot chat rerun with copilot debug", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.resetVirtualToolGroups", - "title": "github.copilot.debug.resetVirtualToolGroups", - "keywords": [ - "github copilot debug reset virtual tool groups", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.tools.memory.showMemories", - "title": "github.copilot.chat.tools.memory.showMemories", - "keywords": [ - "github copilot chat tools memory show memories", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.tools.memory.resolveMemoryFileUri", - "title": "github.copilot.chat.tools.memory.resolveMemoryFileUri", - "keywords": [ - "github copilot chat tools memory resolve memory file uri", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.tools.memory.clearMemories", - "title": "github.copilot.chat.tools.memory.clearMemories", - "keywords": [ - "github copilot chat tools memory clear memories", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.workbenchState", - "title": "github.copilot.debug.workbenchState", - "keywords": [ - "github copilot debug workbench state", - "github copilot" - ] - }, - { - "id": "github.copilot.tests.applyMutations", - "title": "github.copilot.tests.applyMutations", - "keywords": [ - "github copilot tests apply mutations", - "github copilot" - ] - }, - { - "id": "github.copilot.tests.fixTestFailure.fromInline", - "title": "github.copilot.tests.fixTestFailure.fromInline", - "keywords": [ - "github copilot tests fix test failure from inline", - "github copilot" - ] - }, - { - "id": "github.copilot.tests.fixTestFailure", - "title": "github.copilot.tests.fixTestFailure", - "keywords": [ - "github copilot tests fix test failure", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.mcp.setup.flow", - "title": "github.copilot.chat.mcp.setup.flow", - "keywords": [ - "github copilot chat mcp setup flow", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.mcp.setup.validatePackage", - "title": "github.copilot.chat.mcp.setup.validatePackage", - "keywords": [ - "github copilot chat mcp setup validate package", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.mcp.setup.check", - "title": "github.copilot.chat.mcp.setup.check", - "keywords": [ - "github copilot chat mcp setup check", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.inlineEdit.reportFeedback", - "title": "github.copilot.debug.inlineEdit.reportFeedback", - "keywords": [ - "github copilot debug inline edit report feedback", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.inlineEdit.pickProvider", - "title": "github.copilot.debug.inlineEdit.pickProvider", - "keywords": [ - "github copilot debug inline edit pick provider", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.inlineEdit.learnMore", - "title": "github.copilot.debug.inlineEdit.learnMore", - "keywords": [ - "github copilot debug inline edit learn more", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.inlineEdit.clearCache", - "title": "github.copilot.debug.inlineEdit.clearCache", - "keywords": [ - "github copilot debug inline edit clear cache", - "github copilot" - ] - }, - { - "id": "github.copilot.debug.inlineEdit.reportNotebookNESIssue", - "title": "github.copilot.debug.inlineEdit.reportNotebookNESIssue", - "keywords": [ - "github copilot debug inline edit report notebook nesissue", - "github copilot" - ] - }, - { - "id": "github.copilot.nes.captureExpected.start", - "title": "github.copilot.nes.captureExpected.start", - "keywords": [ - "github copilot nes capture expected start", - "github copilot" - ] - }, - { - "id": "github.copilot.nes.captureExpected.confirm", - "title": "github.copilot.nes.captureExpected.confirm", - "keywords": [ - "github copilot nes capture expected confirm", - "github copilot" - ] - }, - { - "id": "github.copilot.nes.captureExpected.abort", - "title": "github.copilot.nes.captureExpected.abort", - "keywords": [ - "github copilot nes capture expected abort", - "github copilot" - ] - }, - { - "id": "github.copilot.nes.captureExpected.submit", - "title": "github.copilot.nes.captureExpected.submit", - "keywords": [ - "github copilot nes capture expected submit", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.sendCompletionFeedback", - "title": "github.copilot.chat.sendCompletionFeedback", - "keywords": [ - "github copilot chat send completion feedback", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.completions.enable", - "title": "github.copilot.chat.completions.enable", - "keywords": [ - "github copilot chat completions enable", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.completions.disable", - "title": "github.copilot.chat.completions.disable", - "keywords": [ - "github copilot chat completions disable", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.completions.toggle", - "title": "github.copilot.chat.completions.toggle", - "keywords": [ - "github copilot chat completions toggle", - "github copilot" - ] - }, - { - "id": "github.copilot.completions.enable", - "title": "github.copilot.completions.enable", - "keywords": [ - "github copilot completions enable", - "github copilot" - ] - }, - { - "id": "github.copilot.completions.disable", - "title": "github.copilot.completions.disable", - "keywords": [ - "github copilot completions disable", - "github copilot" - ] - }, - { - "id": "github.copilot.completions.toggle", - "title": "github.copilot.completions.toggle", - "keywords": [ - "github copilot completions toggle", - "github copilot" - ] - }, - { - "id": "github.copilot.toggleStatusMenu", - "title": "github.copilot.toggleStatusMenu", - "keywords": [ - "github copilot toggle status menu", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.toggleStatusMenu", - "title": "github.copilot.chat.toggleStatusMenu", - "keywords": [ - "github copilot chat toggle status menu", - "github copilot" - ] - }, - { - "id": "github.copilot.openDocs", - "title": "github.copilot.openDocs", - "keywords": [ - "github copilot open docs", - "github copilot" - ] - }, - { - "id": "github.copilot.openLogs", - "title": "github.copilot.openLogs", - "keywords": [ - "github copilot open logs", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.openSuggestionsPanel", - "title": "github.copilot.chat.openSuggestionsPanel", - "keywords": [ - "github copilot chat open suggestions panel", - "github copilot" - ] - }, - { - "id": "github.copilot.generate", - "title": "github.copilot.generate", - "keywords": [ - "github copilot generate", - "github copilot" - ] - }, - { - "id": "github.copilot.acceptCursorPanelSolution", - "title": "github.copilot.acceptCursorPanelSolution", - "keywords": [ - "github copilot accept cursor panel solution", - "github copilot" - ] - }, - { - "id": "github.copilot.previousPanelSolution", - "title": "github.copilot.previousPanelSolution", - "keywords": [ - "github copilot previous panel solution", - "github copilot" - ] - }, - { - "id": "github.copilot.nextPanelSolution", - "title": "github.copilot.nextPanelSolution", - "keywords": [ - "github copilot next panel solution", - "github copilot" - ] - }, - { - "id": "github.copilot.openModelPicker", - "title": "github.copilot.openModelPicker", - "keywords": [ - "github copilot open model picker", - "github copilot" - ] - }, - { - "id": "github.copilot.chat.openModelPicker", - "title": "github.copilot.chat.openModelPicker", - "keywords": [ - "github copilot chat open model picker", - "github copilot" - ] - }, - { - "id": "code-tutor.analyzeBigO", - "title": "code-tutor.analyzeBigO", - "keywords": [ - "code tutor analyze big o", - "code tutor" - ] - }, - { - "id": "code-tutor.iterateBigOQueue", - "title": "code-tutor.iterateBigOQueue", - "keywords": [ - "code tutor iterate big oqueue", - "code tutor" - ] - }, - { - "id": "code-tutor.readEntireBigOQueue", - "title": "code-tutor.readEntireBigOQueue", - "keywords": [ - "code tutor read entire big oqueue", - "code tutor" - ] - } -] \ No newline at end of file diff --git a/Core/program_settings/program_settings/httpClient.js b/Core/program_settings/program_settings/httpClient.js new file mode 100644 index 0000000..c7308dd --- /dev/null +++ b/Core/program_settings/program_settings/httpClient.js @@ -0,0 +1,222 @@ +const http = require("http"); +const https = require("https"); + +/** + * Shared HTTP transport for every EchoCode AI backend (Ollama, OpenAI-compatible + * APIs, Anthropic). Kept dependency-free on purpose: the extension ships as a VSIX + * and Node's built-ins are all this needs. + */ +function openRequest(url, { method, headers, timeoutMs, label }, onResponse) { + let parsedUrl; + try { + parsedUrl = new URL(url); + } catch { + throw new Error(`Invalid ${label || "request"} URL: ${url}`); + } + + const isHttps = parsedUrl.protocol === "https:"; + const transport = isHttps ? https : http; + + const req = transport.request( + { + method, + hostname: parsedUrl.hostname, + port: parsedUrl.port || (isHttps ? 443 : 80), + path: `${parsedUrl.pathname}${parsedUrl.search}`, + headers, + }, + onResponse, + ); + + if (timeoutMs) { + req.setTimeout(timeoutMs, () => { + req.destroy( + new Error(`Request to ${url} timed out after ${timeoutMs}ms`), + ); + }); + } + + return req; +} + +function buildHeaders(payload, extraHeaders) { + const headers = { ...(extraHeaders || {}) }; + if (payload) { + headers["Content-Type"] = "application/json"; + headers["Content-Length"] = Buffer.byteLength(payload); + } + return headers; +} + +/** + * Buffers a JSON response. `label` only shapes the error text so failures name the + * backend the user actually picked instead of always blaming Ollama. + */ +function requestJson(url, body, options = {}) { + return new Promise((resolve, reject) => { + const label = options.label || "AI provider"; + const payload = body ? JSON.stringify(body) : null; + const method = options.method || (body ? "POST" : "GET"); + + let req; + try { + req = openRequest( + url, + { + method, + headers: buildHeaders(payload, options.headers), + timeoutMs: options.timeoutMs, + label, + }, + (res) => { + let raw = ""; + res.setEncoding("utf8"); + res.on("data", (chunk) => { + raw += chunk; + }); + res.on("end", () => { + if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) { + reject( + new Error( + `${label} request failed (${res.statusCode || "unknown"}): ${describeError(raw)}`, + ), + ); + return; + } + + try { + resolve(JSON.parse(raw)); + } catch { + reject(new Error(`${label} returned invalid JSON.`)); + } + }); + }, + ); + } catch (err) { + reject(err); + return; + } + + req.on("error", (err) => reject(err)); + if (payload) req.write(payload); + req.end(); + }); +} + +/** + * Streams a newline-delimited JSON response, invoking `onEvent` per parsed line. + * Ollama's /api/pull reports download progress this way, which is the only way to + * show a real progress bar while a multi-gigabyte model downloads. + */ +function requestNdjson(url, body, options = {}) { + return new Promise((resolve, reject) => { + const label = options.label || "AI provider"; + const payload = body ? JSON.stringify(body) : null; + const onEvent = options.onEvent || (() => {}); + + let req; + try { + req = openRequest( + url, + { + method: options.method || (body ? "POST" : "GET"), + headers: buildHeaders(payload, options.headers), + timeoutMs: options.timeoutMs, + label, + }, + (res) => { + res.setEncoding("utf8"); + + if (!res.statusCode || res.statusCode < 200 || res.statusCode >= 300) { + let raw = ""; + res.on("data", (chunk) => { + raw += chunk; + }); + res.on("end", () => + reject( + new Error( + `${label} request failed (${res.statusCode || "unknown"}): ${describeError(raw)}`, + ), + ), + ); + return; + } + + let buffer = ""; + let handlerError = null; + + const flush = (line) => { + const trimmed = line.trim(); + if (!trimmed || handlerError) return; + + let event; + try { + event = JSON.parse(trimmed); + } catch { + // A partial or non-JSON keepalive line is not worth failing over. + return; + } + + // A throw from onEvent is how a caller reports a failure carried *inside* + // the stream β€” Ollama returns HTTP 200 with an {"error": …} line for an + // unknown model, so swallowing this would silently "succeed" on a failed + // download. Abort the request and surface it. + try { + onEvent(event); + } catch (err) { + handlerError = err; + req.destroy(); + reject(err); + } + }; + + res.on("data", (chunk) => { + buffer += chunk; + let newlineIndex = buffer.indexOf("\n"); + while (newlineIndex !== -1) { + flush(buffer.slice(0, newlineIndex)); + buffer = buffer.slice(newlineIndex + 1); + newlineIndex = buffer.indexOf("\n"); + } + }); + + res.on("end", () => { + flush(buffer); + if (!handlerError) resolve(); + }); + }, + ); + } catch (err) { + reject(err); + return; + } + + req.on("error", (err) => reject(err)); + if (options.cancellationToken) { + options.cancellationToken.onCancellationRequested(() => + req.destroy(new Error("Cancelled.")), + ); + } + if (payload) req.write(payload); + req.end(); + }); +} + +/** + * Providers return their failure reason in a JSON envelope; surfacing that instead + * of the whole body is the difference between "invalid api key" and a wall of HTML. + */ +function describeError(raw) { + if (!raw) return "no response body"; + try { + const parsed = JSON.parse(raw); + const message = + parsed?.error?.message || parsed?.error || parsed?.message || null; + if (message) return typeof message === "string" ? message : JSON.stringify(message); + } catch { + // Fall through to the raw body. + } + return raw.length > 300 ? `${raw.slice(0, 300)}…` : raw; +} + +module.exports = { requestJson, requestNdjson }; diff --git a/Core/program_settings/program_settings/secretStore.js b/Core/program_settings/program_settings/secretStore.js new file mode 100644 index 0000000..b891fc5 --- /dev/null +++ b/Core/program_settings/program_settings/secretStore.js @@ -0,0 +1,58 @@ +/** + * API keys must never land in settings.json β€” that file is plaintext, is often + * committed, and syncs across machines. VS Code's SecretStorage keeps them in the OS + * keychain instead. The extension hands us its context once at activation. + */ +let secrets = null; + +function initSecretStorage(context) { + secrets = context?.secrets || null; + return Boolean(secrets); +} + +function keyName(providerId) { + return `echocode.apiKey.${providerId || "custom"}`; +} + +async function getApiKey(providerId) { + if (!secrets) return ""; + try { + return (await secrets.get(keyName(providerId))) || ""; + } catch { + return ""; + } +} + +async function setApiKey(providerId, value) { + if (!secrets) { + throw new Error( + "EchoCode cannot store the API key: VS Code secret storage is unavailable.", + ); + } + if (value) { + await secrets.store(keyName(providerId), value); + } else { + await secrets.delete(keyName(providerId)); + } +} + +async function deleteApiKey(providerId) { + if (!secrets) return; + try { + await secrets.delete(keyName(providerId)); + } catch { + // Nothing useful to do if the keychain refuses a delete. + } +} + +function isAvailable() { + return Boolean(secrets); +} + +module.exports = { + initSecretStorage, + getApiKey, + setApiKey, + deleteApiKey, + isAvailable, +}; diff --git a/Core/program_settings/program_settings/systemScan.js b/Core/program_settings/program_settings/systemScan.js new file mode 100644 index 0000000..cc91766 --- /dev/null +++ b/Core/program_settings/program_settings/systemScan.js @@ -0,0 +1,200 @@ +const os = require("os"); +const { exec } = require("child_process"); + +const BYTES_PER_GB = 1024 ** 3; + +/** + * Runs a short shell probe and resolves to "" instead of rejecting. Every caller + * here is a best-effort hardware sniff β€” a missing tool must never block setup. + */ +function probe(command, timeoutMs = 4000) { + return new Promise((resolve) => { + try { + exec(command, { timeout: timeoutMs, windowsHide: true }, (err, stdout) => { + resolve(err ? "" : String(stdout || "").trim()); + }); + } catch { + resolve(""); + } + }); +} + +/** + * Best-effort discrete-GPU VRAM lookup. NVIDIA is the only vendor that exposes a + * reliable number from a CLI that ships with the driver, so that's the only one we + * trust for sizing; anything else is reported by name only and sizing falls back to RAM. + */ +async function detectGpu() { + const platform = os.platform(); + + const nvidia = await probe( + "nvidia-smi --query-gpu=name,memory.total --format=csv,noheader,nounits", + ); + if (nvidia) { + // "NVIDIA GeForce RTX 4070, 12282" β€” take the largest card if several are listed. + const cards = nvidia + .split(/\r?\n/) + .map((line) => { + const [name, mib] = line.split(",").map((p) => (p || "").trim()); + const vramGb = Number(mib) / 1024; + return name && Number.isFinite(vramGb) && vramGb > 0 + ? { name, vramGb } + : null; + }) + .filter(Boolean); + + if (cards.length) { + return cards.reduce((best, c) => (c.vramGb > best.vramGb ? c : best)); + } + } + + if (platform === "darwin" && os.arch() === "arm64") { + // Apple Silicon shares one memory pool between CPU and GPU, so total RAM is + // the budget. Ollama will happily use most of it via Metal. + const chip = await probe( + "sysctl -n machdep.cpu.brand_string", + ); + return { + name: chip || "Apple Silicon (unified memory)", + vramGb: os.totalmem() / BYTES_PER_GB, + unified: true, + }; + } + + if (platform === "win32") { + // AdapterRAM is capped at 4 GB by Windows and is wrong on modern cards, so we + // take the name for display only and let RAM drive the recommendation. + const name = await probe( + 'powershell -NoProfile -NonInteractive -Command "(Get-CimInstance Win32_VideoController | Select-Object -First 1 -ExpandProperty Name)"', + ); + if (name) return { name, vramGb: 0 }; + } + + return null; +} + +/** + * Snapshot of what this machine can actually run. Never throws. + */ +async function scanSystemSpecs() { + const totalRamGb = os.totalmem() / BYTES_PER_GB; + const freeRamGb = os.freemem() / BYTES_PER_GB; + const cpus = os.cpus() || []; + + let gpu = null; + try { + gpu = await detectGpu(); + } catch { + gpu = null; + } + + return { + platform: os.platform(), + arch: os.arch(), + cpuModel: cpus[0]?.model?.trim() || "unknown CPU", + cpuCores: cpus.length, + totalRamGb, + freeRamGb, + gpu, + }; +} + +/** + * How much memory we're willing to hand a model. A dedicated GPU is the fast path, + * so its VRAM wins when it's big enough to be worth using; otherwise we budget a + * share of system RAM and leave the rest for the OS, VS Code and the user's program. + */ +function memoryBudgetGb(specs) { + const ramBudget = specs.totalRamGb * 0.55; + const vram = specs.gpu?.vramGb || 0; + + if (specs.gpu?.unified) { + // Unified memory: the same pool, so don't double-count it. + return ramBudget; + } + return vram >= 4 ? Math.max(vram, ramBudget * 0.75) : ramBudget; +} + +/** + * Ollama models ordered smallest first. `requiredGb` is roughly the resident size of + * the default (Q4) quantisation plus room for a modest context window. + * Coder-tuned models are preferred at each size because EchoCode is a coding assistant. + */ +const MODEL_CATALOG = [ + { + name: "qwen2.5-coder:1.5b", + requiredGb: 2.5, + blurb: "Tiny coder model β€” runs on almost anything, basic explanations.", + }, + { + name: "llama3.2:3b", + requiredGb: 4, + blurb: "Small general model β€” decent explanations, very fast.", + }, + { + name: "qwen2.5-coder:7b", + requiredGb: 7, + blurb: "Strong code understanding at a modest size. Good default.", + }, + { + name: "qwen2.5-coder:14b", + requiredGb: 12, + blurb: "Noticeably better reasoning about code. Needs a healthy machine.", + }, + { + name: "qwen2.5-coder:32b", + requiredGb: 24, + blurb: "Best local quality EchoCode recommends. Workstation class.", + }, +]; + +/** + * Picks the largest catalog entry that fits the budget, and returns the rest as + * ranked alternates so the user can override the recommendation. + */ +function recommendModel(specs) { + const budgetGb = memoryBudgetGb(specs); + const fits = MODEL_CATALOG.filter((m) => m.requiredGb <= budgetGb); + + // Even a machine below the smallest entry gets an offer β€” the tiny model will be + // slow but it will run, and refusing outright is worse than a warning. + const recommended = fits.length + ? fits[fits.length - 1] + : MODEL_CATALOG[0]; + + return { + budgetGb, + recommended, + underpowered: fits.length === 0, + alternatives: MODEL_CATALOG.filter((m) => m.name !== recommended.name), + }; +} + +/** + * One-line, screen-reader friendly summary of the scan. EchoCode speaks this aloud, + * so it avoids symbols and abbreviations that TTS mangles. + */ +function describeSpecs(specs) { + const parts = [ + `${specs.cpuCores} CPU cores`, + `${specs.totalRamGb.toFixed(1)} gigabytes of RAM`, + ]; + if (specs.gpu?.vramGb) { + parts.push( + specs.gpu.unified + ? `${specs.gpu.name}` + : `${specs.gpu.name} with ${specs.gpu.vramGb.toFixed(1)} gigabytes of video memory`, + ); + } else if (specs.gpu?.name) { + parts.push(specs.gpu.name); + } + return parts.join(", "); +} + +module.exports = { + scanSystemSpecs, + recommendModel, + describeSpecs, + memoryBudgetGb, + MODEL_CATALOG, +}; diff --git a/Core/program_settings/speech_settings/speechHandler.js b/Core/program_settings/speech_settings/speechHandler.js index 973de55..ebb8558 100644 --- a/Core/program_settings/speech_settings/speechHandler.js +++ b/Core/program_settings/speech_settings/speechHandler.js @@ -36,90 +36,250 @@ function getSpeechSpeed() { return speechSpeed; } -// Speak a message aloud -function speakMessage(message) { - return new Promise((resolve) => { - if (isSpeaking) { - stopSpeaking(); - } +/** + * EchoCode speaks one utterance at a time, and a new message interrupts the one in + * progress rather than waiting behind it β€” a stale announcement is worse than a + * truncated one. + * + * Doing that safely is the whole point of the machinery below, because say.js cannot be + * interrupted naively. Its `say` export is a singleton holding a single `this.child`, + * and `speak()` overwrites that handle unconditionally: + * + * this.child = childProcess.spawn(command, args, options) + * + * so calling speak() while audio is playing orphans the previous process β€” the handle is + * gone, nothing can kill it, and it talks over the new utterance to completion. say.js + * flags this in its own source ("no way to kill previous"). Worse, `stop()` nulls the + * handle and returns *before* the process exits; on Windows it shells out to `taskkill` + * through an async exec, so it returns before the kill has even started and SAPI keeps + * talking. "Stop then immediately speak" is therefore exactly how two voices end up + * running at once. + * + * The invariant that prevents it: never spawn while a child is alive. An interrupt kills + * the current utterance and parks the new one until the dying process reports `exit` β€” + * say.js surfaces that as the speak callback, which is the only trustworthy signal that + * the audio has actually ceased. + */ + +// The utterance being spoken right now: { id, resolve }. Null when silent. +let activeUtterance = null; + +// The utterance waiting for the active one to die: { text, resolve }. At most one β€” +// each new message supersedes the last, since either would interrupt the other anyway. +let pendingUtterance = null; + +// Distinguishes an utterance from its successor so a late callback can be ignored. +let utteranceId = 0; + +// Set while an interrupt is waiting on a dying process; see forceHandoff. +let handoffTimer = null; + +// A killed process should report exit almost immediately. If one wedges, staying silent +// forever is the worse failure, so give up waiting after this and start the next +// utterance anyway. +const HANDOFF_TIMEOUT_MS = 2000; - isSpeaking = true; - - // Get the configured voice from settings - const config = vscode.workspace.getConfiguration("echocode"); - let voice = config.get("voice") || null; - - // AUTO-DETECT BETTER VOICES - if (!voice) { - const platform = process.platform; - if (platform === "darwin") { - // macOS: 'Samantha' is a high quality default, or 'Alex'. - // Passing null often defaults to user's system pref, which is usually good. - voice = null; - } else if (platform === "win32") { - // Windows: Try forcing 'Microsoft Zira Desktop' (US English Female) - // or 'Microsoft David Desktop' (US English Male) if available. - // Otherwise, leaving it null uses the robotic default SAPI voice. - voice = "Microsoft Zira Desktop"; +// Resolve the voice to speak with, from settings or a per-platform default. +function resolveVoice() { + const config = vscode.workspace.getConfiguration("echocode"); + const configured = config.get("voice") || null; + if (configured) return configured; + + const platform = process.platform; + if (platform === "darwin") { + // macOS: 'Samantha' is a high quality default, or 'Alex'. + // Passing null often defaults to user's system pref, which is usually good. + return null; + } + if (platform === "win32") { + // Windows: Try forcing 'Microsoft Zira Desktop' (US English Female) + // or 'Microsoft David Desktop' (US English Male) if available. + // Otherwise, leaving it null uses the robotic default SAPI voice. + return "Microsoft Zira Desktop"; + } + return null; +} + +function clearHandoffTimer() { + if (handoffTimer) { + clearTimeout(handoffTimer); + handoffTimer = null; + } +} + +/** + * Retires the active utterance and starts whatever is pending. Called when a process + * reports exit β€” the point at which its audio has genuinely stopped. + */ +function retireActive() { + clearHandoffTimer(); + + if (activeUtterance) { + const { resolve } = activeUtterance; + activeUtterance = null; + resolve(); + } + + isSpeaking = false; + currentSpeechProcess = null; + + // Yield rather than recursing: a backend failing synchronously would otherwise run + // the whole backlog in one unbroken stack. + queueMicrotask(startPending); +} + +/** + * Spawns the pending utterance, if there is one and nothing is currently speaking. + * The "nothing is currently speaking" half is the no-overlap guarantee. + */ +function startPending() { + if (activeUtterance || !pendingUtterance) return; + + const entry = pendingUtterance; + pendingUtterance = null; + + const utterance = { id: (utteranceId += 1), resolve: entry.resolve }; + activeUtterance = utterance; + isSpeaking = true; + + try { + const returned = say.speak(entry.text, resolveVoice(), speechSpeed, (err) => { + // Superseded: a later utterance owns the speaker now, so this callback is the + // tail end of a process we already stopped caring about. + if (activeUtterance !== utterance) return; + + // An interrupted utterance is reported as a failure by say.js. That is the + // expected outcome of an interrupt, not something to warn about. + if (err && !utterance.interrupted) { + console.warn("[EchoCode] say.speak error", err); } - } + retireActive(); + }); - try { - let settled = false; - const finish = () => { - if (settled) return; - settled = true; - isSpeaking = false; - currentSpeechProcess = null; - resolve(); - }; - - // Use say.js to speak the message with the configured voice - const speechProcess = say.speak(message, voice, speechSpeed, (err) => { - if (err) { - console.warn("[EchoCode] say.speak error", err); - } - finish(); + // say.js attaches no 'error' listener to its child, so a missing TTS binary + // (e.g. no festival on Linux) is an uncaught exception, and the exit callback may + // never arrive to retire the utterance. say 0.16 returns nothing from speak() but + // exposes the child as say.child. + const child = + returned && typeof returned.on === "function" ? returned : say.child; + if (child && typeof child.on === "function") { + child.on("error", (err) => { + if (activeUtterance !== utterance) return; + console.warn("[EchoCode] TTS unavailable; continuing without speech", err); + retireActive(); }); + } + currentSpeechProcess = say; + } catch (err) { + // Gracefully degrade when TTS backend is unavailable (e.g., headless CI, missing 'say') + console.warn("[EchoCode] TTS unavailable; continuing without speech", err); + retireActive(); + } +} - if (speechProcess && typeof speechProcess.on === "function") { - speechProcess.on("error", (err) => { - console.warn("[EchoCode] say.speak process error", err); - finish(); - }); - } +/** + * Last resort when a killed process never reports exit. Waiting forever would leave + * EchoCode permanently mute, which is worse than the overlap risk of proceeding. + */ +function forceHandoff() { + handoffTimer = null; + if (!activeUtterance) return; - // Store the speech process - currentSpeechProcess = - speechProcess && typeof speechProcess.kill === "function" - ? { stop: () => speechProcess.kill() } - : say; - } catch (err) { - // Gracefully degrade when TTS backend is unavailable (e.g., headless CI, missing 'say') - console.warn( - "[EchoCode] TTS unavailable; continuing without speech", - err - ); - isSpeaking = false; - currentSpeechProcess = null; - resolve(); + console.warn( + `[EchoCode] speech process did not exit within ${HANDOFF_TIMEOUT_MS}ms; continuing`, + ); + retireActive(); +} + +/** + * Speaks a message, interrupting anything already being spoken. + * + * Resolves once the message has finished being read aloud, or as soon as it is + * superseded or cancelled β€” it never leaves a caller awaiting forever. + * + * The interrupt is not instantaneous: the current utterance is killed and the new one + * starts only once that process confirms it has exited. That handoff is what stops the + * two from talking over each other (see the note at the top of this file). + */ +function speakMessage(message) { + const text = String(message ?? "").trim(); + if (!text) return Promise.resolve(); + + return new Promise((resolve) => { + // Anything already waiting is now stale β€” it never reached the speaker, and the + // message replacing it is the one the user actually wants to hear. + if (pendingUtterance) { + pendingUtterance.resolve(); } + pendingUtterance = { text, resolve }; + + if (activeUtterance) { + interruptActive(); + return; + } + startPending(); }); } -// Stop speaking +/** + * Kills the active utterance so the pending one can take over. Does not start it β€” the + * dying process's exit callback does that, which is what keeps the two from overlapping. + */ +function interruptActive() { + if (!activeUtterance || handoffTimer) return; + + activeUtterance.interrupted = true; + + try { + if (currentSpeechProcess) currentSpeechProcess.stop(); + } catch (err) { + console.warn("[EchoCode] say.stop error", err); + } + + handoffTimer = setTimeout(forceHandoff, HANDOFF_TIMEOUT_MS); +} + +/** + * Stops the current utterance and discards anything waiting β€” this backs the user's + * "stop speech" command, which means stop, not pause. Returns whether there was + * anything to stop. + */ function stopSpeaking() { - if (isSpeaking && currentSpeechProcess) { + const hadPending = Boolean(pendingUtterance); + const wasSpeaking = Boolean(activeUtterance); + + // Dropped before the kill: the exit callback can arrive synchronously, and anything + // still pending at that moment would immediately start playing. + if (pendingUtterance) { + pendingUtterance.resolve(); + pendingUtterance = null; + } + + clearHandoffTimer(); + + if (activeUtterance) { + // Retires the utterance up front so its exit callback is recognised as stale and + // cannot advance the queue. + const { resolve } = activeUtterance; + activeUtterance = null; + try { - currentSpeechProcess.stop(); + if (currentSpeechProcess) currentSpeechProcess.stop(); } catch (err) { - console.warn("[EchoCode] Failed to stop speech process", err); + console.warn("[EchoCode] say.stop error", err); } + isSpeaking = false; currentSpeechProcess = null; - return true; + resolve(); } - return false; + + return wasSpeaking || hadPending; +} + +// True while audio is actually playing. Exposed for tests. +function isCurrentlySpeaking() { + return Boolean(activeUtterance); } // Register speech-related commands @@ -172,6 +332,7 @@ function registerSpeechCommands(context, outputChannel) { module.exports = { speakMessage, stopSpeaking, + isCurrentlySpeaking, increaseSpeechSpeed, decreaseSpeechSpeed, getSpeechSpeed, diff --git a/Language/Python/errorHandler.js b/Language/Python/errorHandler.js index 98fd966..52e40ed 100644 --- a/Language/Python/errorHandler.js +++ b/Language/Python/errorHandler.js @@ -1,40 +1,148 @@ const vscode = require("vscode"); -const { runPylint } = require("./pylintHandler"); +const { + runPylint, + isPylintAvailable, + promptToInstallPylint, + PYLINT_NOT_INSTALLED, + PYTHON_NOT_FOUND, +} = require("./pylintHandler"); const { speakMessage } = require("../../Core/program_settings/speech_settings/speechHandler"); let outputChannel; let isRunning = false; +// Coalesces bursts of saves into one lint run. Save-all across a project, or editors +// with auto-save on, otherwise spawn one Python interpreter per file per keystroke-ish +// interval β€” and pylint's startup dominates its runtime. +const SAVE_DEBOUNCE_MS = 400; +const pendingLints = new Map(); + +// Last document version linted per file. A save that changes nothing (Ctrl+S out of +// habit, or a formatter save with no edits) does not need a fresh run. +const lastLintedVersion = new Map(); + +// Identifies the current spoken readout. A newer lint bumps it so an in-progress +// readout of stale results stops instead of narrating findings that no longer apply. +let readoutId = 0; + +// Latches so a missing toolchain is reported once per session rather than on every save. +let notifiedMissingPylint = false; +let notifiedMissingPython = false; + function initializeErrorHandling(channel) { outputChannel = channel; } -async function handlePythonErrorsOnSave(filePath) { - if (isRunning) return; - isRunning = true; +/** + * Reads the critical findings aloud, one after another. + * + * Deliberately runs outside the lint lock. Speech is slow β€” several seconds per finding + * β€” and holding `isRunning` across it meant every save during the readout was silently + * dropped: the user saved, heard nothing, and had no way to know why. + */ +async function announceErrors(errors) { + const id = (readoutId += 1); - try { - const errors = await runPylint(filePath, outputChannel); - if (errors.length === 0) { - vscode.window.showInformationMessage("βœ… No issues detected!"); - return; + for (const error of errors) { + // A newer lint has produced newer results; this readout is now stale. + if (id !== readoutId) return; + if (error.critical) { + await speakMessage(`Line ${error.line}: ${error.message}`); } + } +} - outputChannel.appendLine(`πŸ“’ Found ${errors.length} Pylint error(s):`); - for (const error of errors) { - const message = `Line ${error.line}: ${error.message}`; - outputChannel.appendLine(message); - if (error.critical) { - await speakMessage(message); - } +function reportLintFailure(err) { + const code = err && err.code; + + if (code === PYLINT_NOT_INSTALLED) { + outputChannel?.appendLine("Pylint is not installed; skipping error checks."); + if (!notifiedMissingPylint) { + notifiedMissingPylint = true; + promptToInstallPylint(); + } + return; + } + + if (code === PYTHON_NOT_FOUND) { + outputChannel?.appendLine(`Python not found: ${err.message}`); + if (!notifiedMissingPython) { + notifiedMissingPython = true; + vscode.window.showWarningMessage(`EchoCode: ${err.message}`); } + return; + } - outputChannel.show(); + // A real failure from a working toolchain is worth surfacing each time. + vscode.window.showErrorMessage( + `Failed to run Pylint: ${err && err.message ? err.message : err}` + ); +} + +async function handlePythonErrorsOnSave(filePath) { + if (isRunning) return; + isRunning = true; + + let errors; + try { + errors = await runPylint(filePath, outputChannel); } catch (err) { - vscode.window.showErrorMessage(`Failed to run Pylint: ${err}`); + reportLintFailure(err); + return; } finally { + // Released before the readout below, so a save during it is still linted. isRunning = false; } + + if (errors.length === 0) { + vscode.window.showInformationMessage("βœ… No issues detected!"); + return; + } + + outputChannel.appendLine(`πŸ“’ Found ${errors.length} Pylint error(s):`); + for (const error of errors) { + outputChannel.appendLine(`Line ${error.line}: ${error.message}`); + } + outputChannel.show(); + + await announceErrors(errors); +} + +/** + * Queues a lint for a saved document, skipping work that would tell the user nothing + * new. Both guards exist because a pylint run costs a fresh interpreter start. + */ +// True once a save-triggered run has proved the toolchain is missing. Further automatic +// runs would each spawn an interpreter to rediscover the same thing, so they stop; the +// explicit "read errors" command clears this to retry after the user installs. +function toolchainUnavailable() { + return notifiedMissingPylint || notifiedMissingPython; +} + +function scheduleLint(document) { + const key = document.uri.toString(); + + if (toolchainUnavailable()) return; + + if (lastLintedVersion.get(key) === document.version) { + outputChannel?.appendLine( + `Skipping Pylint on ${document.uri.fsPath}: unchanged since last run.` + ); + return; + } + + clearTimeout(pendingLints.get(key)); + pendingLints.set( + key, + setTimeout(() => { + pendingLints.delete(key); + // Re-checked at fire time: a run scheduled before an in-flight run reported a + // missing toolchain would otherwise spawn an interpreter to learn the same thing. + if (toolchainUnavailable()) return; + lastLintedVersion.set(key, document.version); + handlePythonErrorsOnSave(document.uri.fsPath); + }, SAVE_DEBOUNCE_MS) + ); } function registerErrorHandlingCommands(context) { @@ -45,6 +153,12 @@ function registerErrorHandlingCommands(context) { outputChannel.appendLine("echocode.readErrors command triggered"); const editor = vscode.window.activeTextEditor; if (editor && editor.document.languageId === "python") { + // Explicit user request: run it now, and do not let the unchanged-file guard + // swallow it β€” asking to hear the errors should always read them. Clearing the + // missing-toolchain latches also makes this the way to retry after installing. + lastLintedVersion.delete(editor.document.uri.toString()); + notifiedMissingPylint = false; + notifiedMissingPython = false; handlePythonErrorsOnSave(editor.document.uri.fsPath); } else { vscode.window.showWarningMessage( @@ -57,16 +171,22 @@ function registerErrorHandlingCommands(context) { // Automatically trigger on save const saveListener = vscode.workspace.onDidSaveTextDocument((document) => { if (document.languageId === "python") { - handlePythonErrorsOnSave(document.uri.fsPath); + scheduleLint(document); } }); - context.subscriptions.push(readErrors, saveListener); -} + const cleanup = new vscode.Disposable(() => { + pendingLints.forEach((timer) => clearTimeout(timer)); + pendingLints.clear(); + lastLintedVersion.clear(); + }); + context.subscriptions.push(readErrors, saveListener, cleanup); +} module.exports = { initializeErrorHandling, handlePythonErrorsOnSave, registerErrorHandlingCommands, -}; \ No newline at end of file + isPylintAvailable, +}; diff --git a/Language/Python/pylintHandler.js b/Language/Python/pylintHandler.js index 94cb598..3b589ee 100644 --- a/Language/Python/pylintHandler.js +++ b/Language/Python/pylintHandler.js @@ -1,29 +1,80 @@ const vscode = require("vscode"); const { spawn, exec } = require("child_process"); -function ensurePylintInstalled() { - return new Promise((resolve, reject) => { - exec(`python -m pylint --version`, (error) => { - if (error) { - vscode.window - .showErrorMessage( - "Pylint is not installed. Click here to install it.", - "Install" - ) - .then((selection) => { - if (selection === "Install") { - vscode.commands.executeCommand("workbench.action.terminal.new"); - vscode.window.showInformationMessage( - "Run: pip install pylint in the terminal." - ); - } - }); - reject("Pylint not installed"); - return; +/** + * Why a lint run failed. The caller needs to tell these apart: a missing toolchain is a + * one-time setup problem to prompt about once, whereas a genuine pylint failure is worth + * reporting every time. Previously every failure looked the same, so a user without + * pylint got an error toast on every single save. + */ +const PYLINT_NOT_INSTALLED = "pylint-not-installed"; +const PYTHON_NOT_FOUND = "python-not-found"; +const PYLINT_TIMED_OUT = "pylint-timed-out"; +const PYLINT_FAILED = "pylint-failed"; + +// Interpreter to invoke. Windows installs it as `python`; elsewhere `python` may be a +// Python 2 stub or absent, so `python3` is the safe name. +function pythonCommand() { + return process.platform === "win32" ? "python" : "python3"; +} + +function lintError(code, message) { + const err = new Error(message); + err.code = code; + return err; +} + +/** + * Offers to install pylint. Callers are responsible for showing this at most once β€” + * see the `notifiedMissingPylint` latch in errorHandler. + */ +function promptToInstallPylint() { + return vscode.window + .showWarningMessage( + "EchoCode: Pylint is not installed, so Python errors cannot be read aloud.", + "How do I install it?" + ) + .then((selection) => { + if (selection === "How do I install it?") { + vscode.commands.executeCommand("workbench.action.terminal.new"); + vscode.window.showInformationMessage( + `Run: ${pythonCommand()} -m pip install pylint` + ); } - resolve(true); + }); +} + +/** + * Resolves true when pylint can actually be run. Cached after the first probe: this + * spawns an interpreter, and re-checking on every save is exactly the cost we are + * trying to avoid. Call `resetPylintAvailability()` after the user installs it. + */ +let availabilityProbe = null; + +function isPylintAvailable() { + if (availabilityProbe) return availabilityProbe; + + availabilityProbe = new Promise((resolve) => { + exec(`${pythonCommand()} -m pylint --version`, (error) => { + resolve(!error); }); }); + + return availabilityProbe; +} + +function resetPylintAvailability() { + availabilityProbe = null; +} + +/** + * Kept for backwards compatibility with callers that just want the prompt behaviour. + * Resolves true when pylint is present, false when the user was prompted instead. + */ +async function ensurePylintInstalled() { + if (await isPylintAvailable()) return true; + await promptToInstallPylint(); + return false; } /** @@ -38,7 +89,7 @@ function runPylint(filePath, outputChannel) { } return new Promise((resolve, reject) => { - const pythonCmd = process.platform === "win32" ? "python" : "python3"; + const pythonCmd = pythonCommand(); const command = ["-m", "pylint", filePath, "--output-format=json"]; if (outputChannel) { @@ -50,40 +101,82 @@ function runPylint(filePath, outputChannel) { const pylint = spawn(pythonCmd, command); let stdoutData = ""; let stderrData = ""; + let settled = false; + + // Cleared on every exit path. Left running, it fired ten seconds after a successful + // run and killed an already-dead process, and kept the timer alive for that long. + const timeout = setTimeout(() => { + finish(() => + reject(lintError(PYLINT_TIMED_OUT, "Pylint took too long to respond.")) + ); + pylint.kill(); + }, 10000); + + function finish(action) { + if (settled) return; + settled = true; + clearTimeout(timeout); + action(); + } - // Collect output pylint.stdout.on("data", (data) => (stdoutData += data.toString())); pylint.stderr.on("data", (data) => (stderrData += data.toString())); - // Handle completion + // Previously unhandled: when the interpreter itself is missing, spawn emits 'error' + // and never emits 'close', so the promise hung until the timeout fired. + pylint.on("error", (err) => { + finish(() => + reject( + err.code === "ENOENT" + ? lintError( + PYTHON_NOT_FOUND, + `Could not run "${pythonCmd}". Is Python installed and on your PATH?` + ) + : lintError(PYLINT_FAILED, err.message) + ) + ); + }); + pylint.on("close", (code) => { if (code !== 0 && !stdoutData) { - reject( - `Pylint error: ${stderrData.trim() || `Exited with code ${code}`}` + const stderr = stderrData.trim(); + // Python reports a missing module on stderr and exits non-zero. Recognising it + // is what lets the caller prompt once instead of erroring on every save. + const missing = /No module named pylint/i.test(stderr); + finish(() => + reject( + missing + ? lintError(PYLINT_NOT_INSTALLED, "Pylint is not installed.") + : lintError( + PYLINT_FAILED, + `Pylint error: ${stderr || `Exited with code ${code}`}` + ) + ) ); return; } + let results; try { - const results = JSON.parse(stdoutData.trim() || "[]"); - const simplifiedErrors = results.map((error) => ({ - line: error.line, - message: simplifyError(error.symbol, error.message), - type: error.type, - critical: isCriticalError(error.symbol), - })); - - resolve(simplifiedErrors); + results = JSON.parse(stdoutData.trim() || "[]"); } catch (parseError) { - reject("Error parsing Pylint output."); + finish(() => + reject(lintError(PYLINT_FAILED, "Error parsing Pylint output.")) + ); + return; } - }); - // Timeout if Pylint takes too long (prevents infinite hanging) - setTimeout(() => { - reject("Pylint took too long. Check if it's installed correctly."); - pylint.kill(); - }, 10000); // 10s timeout + finish(() => + resolve( + results.map((error) => ({ + line: error.line, + message: simplifyError(error.symbol, error.message), + type: error.type, + critical: isCriticalError(error.symbol), + })) + ) + ); + }); }); } @@ -123,4 +216,14 @@ function simplifyError(symbol, message) { return explanations[symbol] || message; } -module.exports = { ensurePylintInstalled, runPylint }; +module.exports = { + ensurePylintInstalled, + runPylint, + isPylintAvailable, + resetPylintAvailability, + promptToInstallPylint, + PYLINT_NOT_INSTALLED, + PYTHON_NOT_FOUND, + PYLINT_TIMED_OUT, + PYLINT_FAILED, +}; diff --git a/README.md b/README.md index 79b9d45..ef5453d 100644 --- a/README.md +++ b/README.md @@ -49,50 +49,94 @@ Originally built for Python debugging, Echo Code has evolved into a **language-a - **Integration with GitHub Copilot** Leverages GitHub Copilot and Copilot Chat for enhanced AI-powered coding assistance. +- **Bring Your Own Model β€” API or Local** + Every EchoCode AI feature can run through GitHub Copilot, a hosted API (OpenAI, OpenRouter, Groq, Anthropic, or any OpenAI-compatible endpoint such as LM Studio or vLLM), or a local Ollama model. Whisper speech-to-text is unaffected either way. API keys are stored in VS Code's secret storage (your OS keychain), never in `settings.json`. + +- **AI Provider Setup Wizard (Auto-Detection)** πŸ†• + On first launch, EchoCode asks whether you want an **API model** or a **local model**, then walks the rest of the way: + + - **API β†’** GitHub Copilot (auto-detected via `vscode.lm`, no key needed) or another model API. For the latter, pick a preset, confirm the base URL, enter your key, and EchoCode contacts the endpoint to verify it's reachable **before** saving β€” the model list you pick from is the one the API actually reports. + - **Local β†’** EchoCode scans your Ollama server. If it isn't running, you get a spoken warning and a way back to the API choice instead of a dead end. If it's running but has no models, EchoCode reads your machine's specs (CPU, RAM, and GPU VRAM where detectable), **recommends a model that will actually fit**, and β€” with your confirmation β€” downloads it for you with a live progress bar. + + Re-open the picker anytime with `EchoCode: Select AI Provider & Model`, or refresh detection with `EchoCode: Check for AI Provider/Model Updates` (useful since Copilot's model lineup, hosted API catalogs, and your installed Ollama models all change over time). + +- **Adjustable AI Guidance Level** + Control how verbose AI explanations are (Guided, Balanced, or Concise) across the summarizer, Big O annotations, code annotations, and "What's This" explanations. + +- **Voice Modes (Chat / Code / Command)** + Cycle between three voice modes and use a single hotkey to start/stop recording in whichever mode is active, or jump directly into a specific mode with its own hotkey. + +- **Microphone Selection** + Choose which input device EchoCode uses for voice features, with manual entry and reset-to-default options. + - **Line Reader** Allows the user to see what is put on the line exactly and generate a brief summary that also checks for issues - **Character Reader** Alerts the user to what key is being pressed while typing and alerts to where their cursor is. +- **Assignment Task Sync** + Rescan the workspace to refresh which assignment tasks are complete, and read tasks back in their original sequential order. + --- ## **Keyboard Shortcuts** -| Shortcut | Command | Description | -| ---------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------- | -| `Ctrl+Alt+A` | `code-tutor.Annotate` | Generates the annotations. | -| `Ctrl+Alt+S` | `code-tutor.speakNextAnnotation` | Reads the next annotation, including line number and suggestion. | -| `Ctrl+Alt+Q` | `code-tutor.readAllAnnotation` | Reads all the annotations in the queue. | -| `Ctrl+Alt+Down` | `echocode.jumpToNextFunction` | Jumps to the next function in the file. | -| `Ctrl+Alt+Up` | `echocode.jumpToPreviousFunction` | Jumps to the previous function. | -| `Ctrl+Alt+E C` | `echocode.summarizeClass` | Summarizes the current class. | -| `Ctrl+Alt+E F` | `echocode.summarizeFunction` | Summarizes the current function. | -| `Ctrl+Alt+E P` | `echocode.summarizeProgram` | Summarize full program. | -| `Ctrl+Alt+E W` | `echocode.whereAmI` | Describes the scope the user is in. | -| `Ctrl+Alt+U` | `echocode.increaseSpeechSpeed` | Increases speech rate. | -| `Ctrl+Alt+D` | `echocode.decreaseSpeechSpeed` | Decreases speech rate. | -| `Ctrl+Alt+X` | `echocode.stopSpeech` | Stops current speech playback. | -| `Ctrl+Alt+O` | `echocode.loadAssignmentFile` | Uploads an assignment file for task tracking. | -| `Ctrl+Alt+T` | `echocode.readNextTask` | Reads the next task aloud. | -| `Ctrl+Alt+M` | `echocode.markTaskComplete` | Marks the current task as complete. | -| `Ctrl + Alt + N` | `code-tutor.analyzeBigO` | Queue up the big O annotations | -| `Ctrl + Alt + B` | `code-tutor.iterateBigOQueue` | Read next big O recommendation outloud | -| `Ctrl + Alt + H` | `code-tutor.readEntireBigOQueue` | Read all big O recommendations one at a time | -| `Ctrl+Alt+C` | `echocode.openChat` | Opens the EchoCode Tutor chat interface. | -| `Ctrl+Alt+V` | `echocode.voiceInput` | Starts voice input to ask the chat a question. Future feature | -| `f1` | Reads out the hotkey options by letting you choose 1-7 depending on the option | -| `Ctrl+Alt+L` | `echocode.readCurrentLine` | Tells the user what is on the line exactly | -| `Ctrl+Alt+K` | `echocode.describeCurrentLine` | Generates and tells a user what is on the line | -| `Ctrl+Alt+I` | `echocode.copyFileNameForImport` | Generates an import function for Python and C++ to be pasted for connecting multiple files at the cursor πŸ†• | -| `Ctrl+Shift+I` | `echocode.pasteImportAtCursor` | Pastes the import function for Python and C++ at the cursor πŸ†• | -| `Ctrl+Alt+;` | `echocode.createFile` | Creates a new file in the current folder πŸ†• | -| `Ctrl+Alt+F` | `echocode.createFolder` | Creates a new folder in the workspace πŸ†• | -| `Ctrl+Alt+P` | `echocode.navigateToNextFile` | Moves to the next file in the current folder πŸ†• | -| `Ctrl+Alt+[` | `echocode.moveToNextFolder` | Navigates to the next folder in the workspace πŸ†• | -| `Ctrl+Alt+]` | `echocode.moveToPreviousFolder` | Navigates to the previous folder in the workspace πŸ†• | -| `Ctrl+Alt+G` | `echocode.compileAndParseCpp` / `echocode checkPythonErrors` | Compiles C++ or checks Python syntax, reading errors and fixes aloud πŸ†• | -| `Ctrl+Alt+9` | `echocode.StudentAndDevMode` / `echocode StudentDevMode` | Switches mode depending on the users preference | +Rows marked **Dev** only fire in Developer mode (locked in Student mode). + +| Shortcut | Command | Description | +| ------------------ | ------------------------------------------------------------ | ------------------------------------------------------------------------------------ | +| `Ctrl+Alt+9` | `echocode.toggleMode` | Switches between Student and Developer mode. | +| `F1` | `echocode.readHotkeyGuide` | Reads out the hotkey guide, letting you choose an option by number. | +| `Ctrl+Alt+A` | `echocode.annotate` | Generates AI annotations for your code. **Dev** | +| `Ctrl+Alt+S` | `echocode.speakNextAnnotation` | Reads the next annotation, including line number and suggestion. **Dev** | +| `Ctrl+Alt+Q` | `echocode.readAllAnnotations` | Reads all the annotations in the queue. **Dev** | +| `Ctrl+Alt+N` | `code-tutor.analyzeBigO` | Queues up Big O complexity annotations. **Dev** | +| `Ctrl+Alt+B` | `code-tutor.iterateBigOQueue` | Reads the next Big O recommendation aloud. **Dev** | +| `Ctrl+Alt+H` | `code-tutor.readEntireBigOQueue` | Reads all Big O recommendations one at a time. **Dev** | +| `Ctrl+Alt+Down` | `echocode.jumpToNextFunction` | Jumps to the next function in the file. | +| `Ctrl+Alt+Up` | `echocode.jumpToPreviousFunction` | Jumps to the previous function. | +| `Ctrl+Alt+E C` | `echocode.summarizeClass` | Summarizes the current class. **Dev** | +| `Ctrl+Alt+E F` | `echocode.summarizeFunction` | Summarizes the current function. **Dev** | +| `Ctrl+Alt+E P` | `echocode.summarizeProgram` | Summarizes the full program. **Dev** | +| `Ctrl+Alt+E W` | `echocode.whereAmI` | Describes the scope the user is in. | +| `Ctrl+Alt+C` | `echocode.openChat` | Opens the EchoCode Tutor chat interface. **Dev** | +| `Ctrl+Alt+V` | `echocode.startVoiceInput` | Starts voice input for the chat tutor. **Dev** | +| `Ctrl+Alt+Space` | `echocode.toggleVoice` | Starts/stops voice recording using the currently active voice mode. | +| `Ctrl+Alt+Shift+C` | `echocode.voiceCode` | Toggles voice-to-code recording directly, regardless of the active mode. | +| `Ctrl+Alt+Shift+V` | `echocode.voiceCommand` | Toggles voice-command recording directly, regardless of the active mode. | +| `Ctrl+Alt+Shift+T` | `echocode.voiceChat` | Toggles voice-to-chat recording directly, regardless of the active mode. | +| `Ctrl+Alt+'` | `echocode.cycleVoiceMode` | Cycles the active voice mode between Chat, Code, and Command. | +| `Ctrl+Alt+U` | `echocode.increaseSpeechSpeed` | Increases speech rate. | +| `Ctrl+Alt+D` | `echocode.decreaseSpeechSpeed` | Decreases speech rate. | +| `Ctrl+Alt+X` | `echocode.stopSpeech` | Stops current speech playback. | +| `Ctrl+Alt+Shift+Z` | `echocode.setGuidanceLevel` | Opens a picker to set AI guidance verbosity (Guided, Balanced, Concise). | +| `Ctrl+Alt+Z` | `echocode.cycleGuidanceLevel` | Cycles through AI guidance verbosity levels. | +| `Ctrl+Alt+O` | `echocode.loadAssignmentFile` | Uploads an assignment file for task tracking. | +| `Ctrl+Alt+T` | `echocode.readNextTask` | Reads the next incomplete task aloud. | +| `Ctrl+Alt+/` | `echocode.readNextSequentialTask` | Reads the next task aloud in its original file order. | +| `Ctrl+Alt+M` | `echocode.markTaskComplete` | Marks the current task as complete. | +| `Ctrl+Alt+Y` | `echocode.rescanUserCode` | Rescans the workspace to refresh which assignment tasks are complete. | +| `Ctrl+Alt+L` | `echocode.readCurrentLine` | Tells the user what is on the line exactly. | +| `Ctrl+Alt+K` | `echocode.describeCurrentLine` | Generates and reads an AI description of the current line. **Dev** | +| `Ctrl+Alt+R` | `echocode.toggleCharacterReadOut` | Toggles character-by-character read-out while typing. | +| `Ctrl+Alt+I` | `echocode.copyFileNameForImport` | Generates an import statement for Python/C++ to connect files at the cursor. **Dev** | +| `Ctrl+Shift+I` | `echocode.pasteImportAtCursor` | Pastes the generated import statement at the cursor. **Dev** | +| `Ctrl+Alt+;` | `echocode.createFile` | Creates a new file in the current folder. | +| `Ctrl+Alt+F` | `echocode.createFolder` | Creates a new folder in the workspace. | +| `Ctrl+Alt+P` | `echocode.navigateToNextFile` | Moves to the next file in the current folder. | +| `Ctrl+Alt+[` | `echocode.moveToNextFolder` | Navigates to the next folder in the workspace. | +| `Ctrl+Alt+]` | `echocode.moveToPreviousFolder` | Navigates to the previous folder in the workspace. | +| `Ctrl+Alt+G` | `echocode.compileAndParseCpp` / `echocode.checkPythonErrors` | Compiles C++ or checks Python syntax, reading errors and fixes aloud. **Dev** | + +### Command Palette Only (no default keybinding) + +| Command | Description | +| -------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `EchoCode: Select AI Provider & Model` (`echocode.selectAIProvider`) πŸ†• | Choose an API model (GitHub Copilot, OpenAI, OpenRouter, Groq, Anthropic, or a custom endpoint) or a local Ollama model, then pick from the live model list. Can scan your hardware and install a fitting Ollama model for you. | +| `EchoCode: Check for AI Provider/Model Updates` (`echocode.checkAIProviderUpdates`) πŸ†• | Re-detects available Copilot, hosted API, and Ollama models and warns if your currently configured model is no longer available. Also runs automatically on every startup. | +| `echocode.selectMicrophone` | Choose which microphone EchoCode uses for voice input, enter a device name manually, or reset to the system default. | +| `echocode.switchToStudentMode` / `echocode.switchToDevMode` | Explicitly set the mode instead of toggling with `Ctrl+Alt+9`. | --- @@ -157,6 +201,43 @@ Before using Echo Code, ensure the following are installed: ## **Release Notes** +### **2.1** (Current) + +- **AI Provider Setup Wizard**: On first launch, EchoCode now shows a quick-pick menu to choose your AI provider β€” GitHub Copilot or a local Ollama model β€” with available models for each auto-detected live (Copilot models via `vscode.lm`, Ollama models via a live `/api/tags` call to your local server). Re-open the picker anytime with `EchoCode: Select AI Provider & Model`. +- **AI Provider/Model Update Checks**: Because both Copilot's model lineup and locally-installed Ollama models change over time, EchoCode now re-checks availability on every startup and warns if your configured model has disappeared. Trigger it manually with `EchoCode: Check for AI Provider/Model Updates`. +- Added optional local Ollama backend: route chat, annotations, Big O analysis, summaries, voice-command classification, and voice-to-code generation to a local Ollama model instead of GitHub Copilot (`echocode.useLocalOllama`, `echocode.ollamaBaseUrl`, `echocode.ollamaModel`). +- Added adjustable AI Guidance Level (Guided / Balanced / Concise) that controls explanation verbosity across the summarizer, Big O annotations, code annotations, and "What's This" β€” set it with a picker or cycle through it with a hotkey. +- Added Voice Modes: switch between Chat, Code, and Command voice modes, cycle between them, and start/stop recording with a single hotkey (or jump directly into a specific mode). +- Added a microphone selection command with manual entry and reset-to-default options. +- Added a toggleable character-by-character read-out while typing. +- Added assignment tracker sync improvements: rescan the workspace to refresh completed tasks, and read tasks back in their original sequential order. +- Various stability fixes and expanded automated test coverage across command routing, voice handling, and AI request routing. + + **New Hotkeys:** + +| Shortcut | Description | +| ------------------ | ------------------------------------------------------------------- | +| `Ctrl+Alt+Space` | Starts/stops voice recording using the currently active voice mode. | +| `Ctrl+Alt+Shift+C` | Toggles voice-to-code recording directly. | +| `Ctrl+Alt+Shift+V` | Toggles voice-command recording directly. | +| `Ctrl+Alt+Shift+T` | Toggles voice-to-chat recording directly. | +| `Ctrl+Alt+'` | Cycles the active voice mode between Chat, Code, and Command. | +| `Ctrl+Alt+Shift+Z` | Opens a picker to set AI guidance verbosity. | +| `Ctrl+Alt+Z` | Cycles through AI guidance verbosity levels. | +| `Ctrl+Alt+R` | Toggles character-by-character read-out while typing. | +| `Ctrl+Alt+Y` | Rescans the workspace to refresh assignment task completion. | +| `Ctrl+Alt+/` | Reads the next task aloud in its original file order. | + + **New Commands (Command Palette only):** + +| Command | Description | +| ----------------------------------------------- | ------------------------------------------------------------------------------ | +| `EchoCode: Select AI Provider & Model` | Choose your AI provider and model from live auto-detected lists. | +| `EchoCode: Check for AI Provider/Model Updates` | Re-detect available Copilot/API/Ollama models and flag a missing configured model. | +| `echocode.selectMicrophone` | Choose which microphone EchoCode uses for voice input. | + +--- + ### **2.0** - Added function to copy a function for importing to another file. Autogenerates the necessary imports and calls. diff --git a/extension.js b/extension.js index 9486786..f135afd 100644 --- a/extension.js +++ b/extension.js @@ -1,5 +1,5 @@ const vscode = require("vscode"); -require("dotenv").config(); +require("dotenv").config({ quiet: true }); const { loadProgramFeatureModule, resolveClassExport, @@ -10,6 +10,7 @@ const { const { matchExternalCommand, buildExternalCommandRegistry, + initExternalCommandRegistry, } = require("./Core/program_settings/program_settings/ExternalIntentRouter"); //student/dev mode system @@ -24,6 +25,19 @@ const { STUDENT_LOCKED_COMMANDS, } = require("./Core/program_settings/guard"); +// AI provider selection (Copilot, hosted API, or local Ollama) + model auto-detection +const { + initializeAIProviderOnStartup, + pickProviderAndModel, + checkForProviderUpdates, +} = require("./Core/program_settings/program_settings/aiProviderSetup"); +const { + initSecretStorage, +} = require("./Core/program_settings/program_settings/secretStore"); +const { + getAiSettings, +} = require("./Core/program_settings/program_settings/AIrequest"); + // Python (optional adapter) const { ensurePylintInstalled } = require("./Language/Python/pylintHandler"); const { @@ -316,6 +330,34 @@ async function ensureCopilotActivated(channel) { return copilotExtension; } +/** + * Warming Copilot is only ever an optimisation β€” it lets the provider picker list + * Copilot's models. It must never be able to stop the picker from opening, so a slow + * or failed Copilot activation degrades to "no Copilot models detected" instead of + * hanging the command with no visible feedback. + */ +async function tryEnsureCopilotActivated(channel, timeoutMs = 5000) { + let timer; + try { + return await Promise.race([ + ensureCopilotActivated(channel), + new Promise((resolve) => { + timer = setTimeout(() => { + channel.appendLine( + `[EchoCode] Copilot did not activate within ${timeoutMs}ms; continuing without it.`, + ); + resolve(null); + }, timeoutMs); + }), + ]); + } catch (err) { + channel.appendLine(`[EchoCode] Copilot activation failed: ${err.message}`); + return null; + } finally { + clearTimeout(timer); + } +} + async function activate(context) { outputChannel = vscode.window.createOutputChannel("EchoCode"); outputChannel.appendLine("[EchoCode] Activated"); @@ -512,8 +554,72 @@ async function activate(context) { ); context.subscriptions.push(toggleModeCommand); - // Ensure Copilot (stable, chat, or nightly) is available for AI features - await ensureCopilotActivated(outputChannel); + + // --- AI PROVIDER SETUP START --- + // First activation ever: pop up the API-vs-Local + model picker. + // Every activation after that: silently recheck availability, since Copilot's + // model lineup, hosted API catalogs, and installed Ollama models all change often. + // + // Hosted API keys live in the OS keychain via SecretStorage, so the store has to be + // handed the extension context before any provider code can read a key. + initSecretStorage(context); + + context.subscriptions.push( + vscode.commands.registerCommand("echocode.selectAIProvider", async () => { + try { + // The picker lists Copilot's models via vscode.lm, which reports nothing + // until Copilot has activated. Time-bounded so it can't stall the picker. + await tryEnsureCopilotActivated(outputChannel); + await pickProviderAndModel(context, outputChannel); + } catch (err) { + // A command that fails silently is indistinguishable from one that never + // ran, which makes this impossible to diagnose from the UI. Always speak up. + outputChannel.appendLine(`[AI Setup] Provider picker failed: ${err.stack || err.message}`); + vscode.window.showErrorMessage( + `EchoCode: could not open the AI provider picker β€” ${err.message}`, + ); + } + }), + ); + context.subscriptions.push( + vscode.commands.registerCommand( + "echocode.checkAIProviderUpdates", + async () => { + try { + await tryEnsureCopilotActivated(outputChannel); + await checkForProviderUpdates(context, outputChannel); + vscode.window.showInformationMessage( + "EchoCode: AI provider/model check complete.", + ); + } catch (err) { + outputChannel.appendLine(`[AI Setup] Update check failed: ${err.stack || err.message}`); + vscode.window.showErrorMessage( + `EchoCode: AI provider check failed β€” ${err.message}`, + ); + } + }, + ), + ); + + // Warm Copilot only when it's the selected backend β€” Ollama and hosted-API users + // shouldn't pay the cost of activating an extension they never call. The startup + // check still has to run *after* that activation, because it probes Copilot through + // vscode.lm, which reports no models until Copilot is live; hence the chain rather + // than two independent calls. + // + // Nothing below depends on the result, so this is intentionally not awaited: it used + // to block the rest of activate() on a full Copilot Chat startup. + const copilotWarmup = + getAiSettings().provider === "copilot" + ? tryEnsureCopilotActivated(outputChannel) + : Promise.resolve(null); + + copilotWarmup + .then(() => initializeAIProviderOnStartup(context, outputChannel)) + .catch((err) => { + outputChannel.appendLine(`[AI Setup] ${err.message}`); + }); + // --- AI PROVIDER SETUP END --- // --- DEPENDENCY CHECK START --- // This runs once on startup and ensures the venv exists @@ -522,8 +628,9 @@ async function activate(context) { context, outputChannel, ); - // We don't await this blocking if we want faster startup, - // but for safety we await to ensure python is ready before first voice command + // Deliberately not awaited β€” the venv bootstrap can pip-install faster-whisper, + // which is far too slow to hold up activation. Voice commands resolve the Python + // path from globalState when they actually run. depManager.ensureDependencies().catch((err) => { outputChannel.appendLine(`[Dependency Error] ${err.message}`); }); @@ -555,7 +662,10 @@ async function activate(context) { setFeatureDisposable(featureKey, registerFeatureCommands(featureKey)); }); - // Build external command registry on activation (non-blocking) + // Build external command registry on activation (non-blocking). + // Init first: it resolves the registry's path under this extension's global storage + // and registers the file watcher's disposal against the extension lifetime. + initExternalCommandRegistry(context); buildExternalCommandRegistry().catch((err) => outputChannel.appendLine( `[ExternalIntentRouter] Registry build failed: ${err.message}`, @@ -665,7 +775,11 @@ async function activate(context) { outputChannel, context.globalState, ); - const voiceResult = await tryExecuteVoiceCommand(text, outputChannel); + const voiceResult = await tryExecuteVoiceCommand( + text, + outputChannel, + { allowCodeGeneration: false }, + ); if (voiceResult.handled) { return; } @@ -725,39 +839,18 @@ async function activate(context) { // Toggle Voice Command (Smart Router) context.subscriptions.push( vscode.commands.registerCommand("echocode.toggleVoice", async () => { - if (featureImplementations.isRecording()) { - // Sync UI: Stop immediately - if (chatProvider) chatProvider.setRecordingState(false); + const activeMode = VOICE_MODES[currentVoiceMode] || "chat"; + const modeCommandMap = { + chat: "echocode.voiceChat", + code: "echocode.voiceCode", + command: "echocode.voiceCommand", + }; + const targetCommand = modeCommandMap[activeMode] || "echocode.voiceChat"; - // Announce processing (don't await to avoid blocking stop) - speakMessage("Processing"); - - const result = await vscode.commands.executeCommand( - "echocode._voiceStop", - ); - - if (result && result.ok && result.text) { - // Attempt to execute as a voice command first - const voiceResult = await tryExecuteVoiceCommand( - result.text, - outputChannel, - ); - - if (!voiceResult.handled) { - // Fallback: Send to Chat Tutor - await vscode.commands.executeCommand("echocode.openChat"); - if (chatProvider) { - await chatProvider.handleUserMessage(result.text); - } - } - } - } else { - // Sync UI: Start immediately - if (chatProvider) chatProvider.setRecordingState(true); - - await speakMessage("Listening"); - await vscode.commands.executeCommand("echocode._voiceStart"); - } + outputChannel.appendLine( + `[Voice Mode] toggleVoice dispatch -> ${activeMode} (${targetCommand})`, + ); + await vscode.commands.executeCommand(targetCommand); }), ); diff --git a/package.json b/package.json index 01f3aa8..0360d68 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "version": "2.1.0", "repository": { "type": "git", - "url": "https://github.com/Team-1-4910-Edward/EchoCode-Capstone-Repo.git" + "url": "https://github.com/hpcclab/EchoCode.git" }, "icon": "images/icon.png", "publisher": "EchoCode", @@ -40,7 +40,9 @@ "onCommand:echocode.whereAmI", "onCommand:echocode.compileAndParseCpp", "onCommand:echocode.setGuidanceLevel", - "onCommand:echocode.cycleGuidanceLevel" + "onCommand:echocode.cycleGuidanceLevel", + "onCommand:echocode.selectAIProvider", + "onCommand:echocode.checkAIProviderUpdates" ], "main": "./extension.js", "extensionDependencies": [ @@ -250,6 +252,14 @@ { "command": "echocode.cycleGuidanceLevel", "title": "EchoCode: Cycle Guidance Level" + }, + { + "command": "echocode.selectAIProvider", + "title": "EchoCode: Select AI Provider & Model" + }, + { + "command": "echocode.checkAIProviderUpdates", + "title": "EchoCode: Check for AI Provider/Model Updates" } ], "configuration": { @@ -289,6 +299,43 @@ ], "description": "Controls how much detail EchoCode provides when explaining errors." }, + "echocode.aiProvider": { + "type": "string", + "default": "", + "enum": [ + "", + "copilot", + "ollama", + "api" + ], + "enumDescriptions": [ + "Not chosen yet - EchoCode falls back to the legacy echocode.useLocalOllama toggle.", + "GitHub Copilot via the VS Code language model API.", + "A local Ollama server.", + "A hosted model API (OpenAI, OpenRouter, Groq, Anthropic, or a custom endpoint)." + ], + "description": "Which AI backend EchoCode routes requests to. Set via 'EchoCode: Select AI Provider & Model'." + }, + "echocode.useLocalOllama": { + "type": "boolean", + "default": false, + "description": "Legacy toggle for local Ollama. Only used when echocode.aiProvider is blank; prefer echocode.aiProvider." + }, + "echocode.ollamaBaseUrl": { + "type": "string", + "default": "http://127.0.0.1:11434", + "description": "Base URL for your local Ollama server." + }, + "echocode.ollamaModel": { + "type": "string", + "default": "llama3.2", + "description": "Model name to use when local Ollama is enabled (for example: llama3.2, qwen2.5:7b)." + }, + "echocode.copilotModel": { + "type": "string", + "default": "", + "description": "Copilot model id to prefer when Copilot is the active provider. Leave blank to auto-select. Set via 'EchoCode: Select AI Provider & Model'." + }, "echocode.featureImplementation.annotationsBigO": { "type": "string", "default": "builtin", @@ -360,6 +407,37 @@ "user" ], "description": "Select built-in or user implementation for WhatIsThis. User files are loaded from program_features/WhatIsThis/UserImplementation/ and changes are auto-reloaded." + }, + "echocode.apiProvider": { + "type": "string", + "default": "", + "enum": [ + "", + "openai", + "openrouter", + "groq", + "anthropic", + "custom" + ], + "enumDescriptions": [ + "No hosted API configured.", + "OpenAI (api.openai.com).", + "OpenRouter (openrouter.ai).", + "Groq (api.groq.com).", + "Anthropic Claude (api.anthropic.com).", + "Any other OpenAI-compatible endpoint." + ], + "description": "Hosted API preset used when echocode.aiProvider is 'api'. The API key is kept in VS Code secret storage, never in settings." + }, + "echocode.apiBaseUrl": { + "type": "string", + "default": "", + "description": "Base URL of the hosted API, including the version path (for example https://api.openai.com/v1). Blank uses the preset default." + }, + "echocode.apiModel": { + "type": "string", + "default": "", + "description": "Model id to send to the hosted API. Set via 'EchoCode: Select AI Provider & Model'." } } }, @@ -630,33 +708,32 @@ }, "devDependencies": { "@eslint/js": "^9.24.0", + "@semantic-release/changelog": "^6.0.3", + "@semantic-release/exec": "^6.0.3", + "@semantic-release/git": "^10.0.1", + "@semantic-release/github": "^11.0.1", + "@semantic-release/npm": "^12.0.1", "@stylistic/eslint-plugin": "^2.13.0", "@types/mocha": "^10.0.10", "@types/node": "^20.17.30", - "@types/vscode": "^1.105.0", + "@types/vscode": "^1.99.1", "@vscode/test-cli": "^0.0.9", "@vscode/test-electron": "^2.5.2", - "chai": "^6.2.0", + "@vscode/vsce": "^3.2.1", "eslint": "^9.24.0", - "glob": "^10.3.10", "mocha": "^10.8.2", - "nyc": "^17.1.0", + "semantic-release": "^24.2.0", "ts-node": "^10.9.2", - "typescript": "^5.8.3", + "typescript": "^5.9.3", "typescript-eslint": "^8.29.1" }, "dependencies": { - "@vscode/codicons": "^0.0.33", - "axios": "^1.8.3", - "dotenv": "^16.6.1", + "dotenv": "^17.4.2", "ffmpeg-static": "^5.2.0", "fluent-ffmpeg": "^2.1.3", "mammoth": "^1.9.0", - "mic": "^2.1.2", - "openai": "^5.20.0", "pdf-parse": "^1.1.1", "say": "^0.16.0", - "sound-play": "^1.1.0", - "uuid": "^13.0.0" + "sound-play": "^1.1.0" } } diff --git a/program_features/Annotations_BigO/annotations.js b/program_features/Annotations_BigO/annotations.js index 6ba0b84..cd78803 100644 --- a/program_features/Annotations_BigO/annotations.js +++ b/program_features/Annotations_BigO/annotations.js @@ -10,6 +10,9 @@ const { const { formatHelpByGuidance, } = require("../../Core/program_settings/guide_settings/guidanceLevel"); +const { + requestTextFromMessages, +} = require("../../Core/program_settings/program_settings/AIrequest"); let activeDecorations = []; const annotatedLines = new Set(); @@ -223,10 +226,16 @@ function clearDecorations() { // ------------------------- // Parse Copilot response // ------------------------- -async function parseChatResponse(chatResponse, textEditor) { +async function parseChatResponse(chatResponseOrText, textEditor) { let buffer = ""; - for await (const fragment of chatResponse.text) { + const fragments = + typeof chatResponseOrText === "string" + ? [chatResponseOrText] + : chatResponseOrText?.text; + if (!fragments) return; + + for await (const fragment of fragments) { buffer += fragment; const { objects, leftover } = extractJsonObjectsFromStream(buffer); @@ -312,41 +321,15 @@ function registerAnnotationCommands(context, outputChannel) { "$(loading~spin) EchoCode is analyzing your file with AI...", ); - const [model] = await vscode.lm.selectChatModels({ - vendor: "copilot", - family: "gpt-4o", - }); - - if (!model) { - statusBarMessage.dispose(); - - // If AI fails but we have local annotations, still mark as visible - if (foundLocalIssues) { - annotationsVisible = true; - vscode.window.showInformationMessage( - "Local annotations applied. AI unavailable - please ensure Copilot is enabled.", - ); - } else { - vscode.window.showErrorMessage( - "No language model available. Please ensure Copilot is enabled.", - ); - } - outputChannel.appendLine("No language model available"); - return; - } - - const messages = [ - new vscode.LanguageModelChatMessage(0, buildAnnotationPrompt()), - new vscode.LanguageModelChatMessage(0, codeWithLineNumbers), - ]; - - const chatResponse = await model.sendRequest( - messages, - {}, - new vscode.CancellationTokenSource().token, + const responseText = await requestTextFromMessages( + [ + { role: "system", content: buildAnnotationPrompt() }, + { role: "user", content: codeWithLineNumbers }, + ], + { cancellationToken: new vscode.CancellationTokenSource().token }, ); - await parseChatResponse(chatResponse, textEditor); + await parseChatResponse(responseText, textEditor); annotationsVisible = true; statusBarMessage.dispose(); diff --git a/program_features/Annotations_BigO/bigOAnalysis.js b/program_features/Annotations_BigO/bigOAnalysis.js index 9e303b8..9f898fe 100644 --- a/program_features/Annotations_BigO/bigOAnalysis.js +++ b/program_features/Annotations_BigO/bigOAnalysis.js @@ -5,6 +5,9 @@ const { speakMessage, } = require("../../Core/program_settings/speech_settings/speechHandler"); const { annotationQueue } = require("./annotations"); +const { + requestTextFromMessages, +} = require("../../Core/program_settings/program_settings/AIrequest"); const bigOQueue = new Queue(); @@ -185,9 +188,20 @@ function addBigOIssue(editor, line, suggestion, collectedIssues) { /** * Parses the chat response and applies decorations. */ -async function parseChatResponse(chatResponse, textEditor, collectedIssues) { +async function parseChatResponse( + chatResponseOrText, + textEditor, + collectedIssues, +) { let accumulatedResponse = ""; - for await (const fragment of chatResponse.text) { + + const fragments = + typeof chatResponseOrText === "string" + ? [chatResponseOrText] + : chatResponseOrText?.text; + if (!fragments) return; + + for await (const fragment of fragments) { accumulatedResponse += fragment; // Check if the accumulated response contains a complete JSON array @@ -321,26 +335,12 @@ async function analyzeLoops(editor, loops, collectedIssues) { `; try { - const [model] = await vscode.lm.selectChatModels({ - vendor: "copilot", - family: "gpt-4o", - }); - - if (!model) { - vscode.window.showErrorMessage( - "No language model available. Please ensure Copilot is enabled.", - ); - return; - } - - const messages = [new vscode.LanguageModelChatMessage(0, prompt)]; - const chatResponse = await model.sendRequest( - messages, - {}, - new vscode.CancellationTokenSource().token, + const responseText = await requestTextFromMessages( + [{ role: "user", content: prompt }], + { cancellationToken: new vscode.CancellationTokenSource().token }, ); - await parseChatResponse(chatResponse, editor, collectedIssues); + await parseChatResponse(responseText, editor, collectedIssues); } catch (error) { console.error("Error analyzing loop:", error); } diff --git a/program_features/Assignment_Tracker/assignmentTracker.js b/program_features/Assignment_Tracker/assignmentTracker.js index 63f82e1..59c71ed 100644 --- a/program_features/Assignment_Tracker/assignmentTracker.js +++ b/program_features/Assignment_Tracker/assignmentTracker.js @@ -5,6 +5,9 @@ const path = require("path"); const { speakMessage, } = require("../../Core/program_settings/speech_settings/speechHandler"); +const { + requestTextFromMessages, +} = require("../../Core/program_settings/program_settings/AIrequest"); const pdfParse = require("pdf-parse"); const mammoth = require("mammoth"); @@ -78,36 +81,18 @@ async function loadAssignmentFile() { async function parseTasksWithAI(text) { try { - const [model] = await vscode.lm.selectChatModels({ - vendor: "copilot", - family: "gpt-4o", - }); - if (!model) { - vscode.window.showErrorMessage( - "Copilot model not available. Using fallback parser.", - ); - return parseTasksFromText(text); - } - - const messages = [ - new vscode.LanguageModelChatMessage( - 0, - "You are helping a blind beginner Python student. Extract only the concrete steps required to complete the coding assignment below. Each step should be short and clear -- under 15 words. Format each step as a bullet point like '- [ ] Define a function named add().'", - ), - new vscode.LanguageModelChatMessage(0, text), - ]; - - const response = await model.sendRequest( - messages, - {}, - new vscode.CancellationTokenSource().token, + const result = await requestTextFromMessages( + [ + { + role: "system", + content: + "You are helping a blind beginner Python student. Extract only the concrete steps required to complete the coding assignment below. Each step should be short and clear -- under 15 words. Format each step as a bullet point like '- [ ] Define a function named add().'", + }, + { role: "user", content: text }, + ], + { cancellationToken: new vscode.CancellationTokenSource().token }, ); - let result = ""; - for await (const chunk of response.text) { - result += chunk; - } - parseTasksFromText(result); } catch (err) { vscode.window.showErrorMessage("AI parsing failed: " + err.message); @@ -167,42 +152,23 @@ async function rescanUserCode() { } try { - const [model] = await vscode.lm.selectChatModels({ - vendor: "copilot", - family: "gpt-4o", - }); - if (!model) { - vscode.window.showErrorMessage("Copilot model not available."); - return; - } - const assignmentText = taskList .map((task, i) => `Task ${i + 1}: ${task}`) .join("\n"); - const messages = [ - new vscode.LanguageModelChatMessage( - 0, - `You are helping a blind beginner Python student. \nCompare this assignment list to the student's code. \nRespond only with the task numbers that are already complete.`, - ), - new vscode.LanguageModelChatMessage( - 0, - `Assignment Tasks:\n${assignmentText}`, - ), - new vscode.LanguageModelChatMessage(0, `User's Code:\n${userCode}`), - ]; - - const response = await model.sendRequest( - messages, - {}, - new vscode.CancellationTokenSource().token, + const result = await requestTextFromMessages( + [ + { + role: "system", + content: + "You are helping a blind beginner Python student. Compare this assignment list to the student's code. Respond only with the task numbers that are already complete.", + }, + { role: "user", content: `Assignment Tasks:\n${assignmentText}` }, + { role: "user", content: `User's Code:\n${userCode}` }, + ], + { cancellationToken: new vscode.CancellationTokenSource().token }, ); - let result = ""; - for await (const chunk of response.text) { - result += chunk; - } - updateCompletedTasksFromAI(result); } catch (err) { vscode.window.showErrorMessage("AI rescan failed: " + err.message); diff --git a/program_features/ChatBot/Chat_Tutor.js b/program_features/ChatBot/Chat_Tutor.js index 06ee2b5..a330839 100644 --- a/program_features/ChatBot/Chat_Tutor.js +++ b/program_features/ChatBot/Chat_Tutor.js @@ -3,6 +3,9 @@ const vscode = require("vscode"); const { speakMessage, } = require("../../Core/program_settings/speech_settings/speechHandler"); +const { + requestTextFromMessages, +} = require("../../Core/program_settings/program_settings/AIrequest"); // --- Simple mock voice input (kept for dev/demo) --- function performVoiceRecognition() { @@ -188,38 +191,22 @@ class EchoCodeChatViewProvider { // Assemble messages with small running history const messages = []; - messages.push(vscode.LanguageModelChatMessage.User(prompt)); + messages.push({ role: "system", content: prompt }); for (const turn of this.conversationHistory.slice(-6)) { - messages.push(vscode.LanguageModelChatMessage.User(turn.user)); - messages.push(vscode.LanguageModelChatMessage.Assistant(turn.response)); - } - messages.push(vscode.LanguageModelChatMessage.User(userInput)); - - // FIX: Select ANY copilot model, do not hardcode 'gpt-4o' family - const models = await vscode.lm.selectChatModels({ vendor: "copilot" }); - const model = models[0]; - - if (!model) { - this._safePost({ - type: "response", - text: "No language model available. Please enable GitHub Copilot.", - }); - this.outputChannel.appendLine("No chat model available."); - return; + messages.push({ role: "user", content: turn.user }); + messages.push({ role: "assistant", content: turn.response }); } + messages.push({ role: "user", content: userInput }); // UI: loading this._safePost({ type: "responseLoading", started: true }); try { const cts = new vscode.CancellationTokenSource(); - const chatResponse = await model.sendRequest(messages, {}, cts.token); - - let responseText = ""; - for await (const fragment of chatResponse.text) { - responseText += fragment; - this._safePost({ type: "responseFragment", text: fragment }); - } + const responseText = await requestTextFromMessages(messages, { + cancellationToken: cts.token, + }); + this._safePost({ type: "responseFragment", text: responseText }); this.conversationHistory.push({ user: userInput, diff --git a/program_features/Voice/dependencyManager.js b/program_features/Voice/dependencyManager.js index 4c0ee8c..cee8ce1 100644 --- a/program_features/Voice/dependencyManager.js +++ b/program_features/Voice/dependencyManager.js @@ -1,11 +1,55 @@ const vscode = require("vscode"); const fs = require("fs"); const path = require("path"); -const { spawn, exec } = require("child_process"); +const { exec } = require("child_process"); // We create a hidden venv folder within the extension const VENV_NAME = "echo_venv"; +// Guards the venv against concurrent setup. Every VS Code window runs its own extension +// host, so without this two windows will run `python -m venv` into the same directory, +// or two pip installs into the same site-packages, at the same time. +const LOCK_NAME = "echo_venv.setup.lock"; + +// A lock older than this is treated as abandoned β€” a window that crashed mid-install +// must not leave voice features permanently wedged. +const LOCK_STALE_MS = 20 * 60 * 1000; + +// Records whether setup succeeded or failed, so a failing install is not repeated in +// full on every single activation. +const INSTALL_STATE_KEY = "echoCode.voiceDependencyState"; + +// After this many consecutive failures EchoCode stops retrying automatically and asks. +// Re-running a multi-hundred-megabyte pip install on every window open β€” when it has +// already failed twice for the same reason β€” is the difference between "slow once" and +// "slow forever". +const MAX_INSTALL_ATTEMPTS = 2; + +// Every shell-out is bounded. `exec` has no default timeout, and on Windows +// `python --version` can hang indefinitely on the Microsoft Store App Execution Alias, +// which is indistinguishable from the extension simply never finishing. +const PROBE_TIMEOUT_MS = 15 * 1000; +const VENV_CREATE_TIMEOUT_MS = 3 * 60 * 1000; +const PIP_INSTALL_TIMEOUT_MS = 20 * 60 * 1000; + +// pip is chatty. exec's default maxBuffer is 1 MB, and overrunning it kills the child +// with ENOBUFS β€” which surfaces as a failed install that then retries forever. +const MAX_OUTPUT_BYTES = 32 * 1024 * 1024; + +/** + * One setup attempt per extension host, shared across every DependencyManager instance. + * + * This has to be module-level, not per-instance: extension.js constructs a brand new + * manager on activation *and* on every feature reload (a UserImplementation file change + * or a featureImplementation setting change both trigger one). Instance state cannot + * stop those from each re-entering the prompt-and-create path, which is what produced a + * loop of repeated "Creating isolated Python environment..." notifications. + * + * Only the slow path is memoised. The cheap "is everything already here" check still + * runs on every call, so a setup that succeeds is picked up immediately afterwards. + */ +let sharedSetupRun = null; + class DependencyManager { constructor(context, outputChannel) { this.context = context; @@ -13,61 +57,341 @@ class DependencyManager { this.extensionUri = context.extensionUri; // Path to the virtual environment folder this.venvPath = path.join(context.globalStorageUri.fsPath, VENV_NAME); + this.lockPath = path.join(context.globalStorageUri.fsPath, LOCK_NAME); } /** * Main entry point: Ensures Python and packages are ready. - * Returns the path to the python executable to use. + * Returns the path to the python executable to use, or null. */ async ensureDependencies() { this.log("Checking voice dependencies..."); - // 1. Ensure storage directory exists - if (!fs.existsSync(this.context.globalStorageUri.fsPath)) { - fs.mkdirSync(this.context.globalStorageUri.fsPath, { recursive: true }); + const storageDir = this.context.globalStorageUri.fsPath; + if (!fs.existsSync(storageDir)) { + fs.mkdirSync(storageDir, { recursive: true }); } - // 2. Determine Python Executable Path inside venv - const isWin = process.platform === "win32"; - const pythonName = isWin ? "python.exe" : "bin/python3"; - const venvPythonPath = path.join( - this.venvPath, - isWin ? "Scripts" : "", - pythonName - ); + const venvPythonPath = this.venvPythonPath(); - // 3. Check if venv exists - const venvExists = fs.existsSync(venvPythonPath); + // Fast path, and the common one: everything is already in place. Costs two stats + // and no process launches, so a warm activation pays essentially nothing. + if (fs.existsSync(venvPythonPath) && this.packagesPresentOnDisk()) { + await this.context.globalState.update( + "echoCodePythonPath", + venvPythonPath, + ); + await this.recordState({ status: "ok", attempts: 0 }); + this.log(`Voice dependencies ready. Using: ${venvPythonPath}`); + return venvPythonPath; + } - if (!venvExists) { - const selection = await vscode.window.showInformationMessage( - "EchoCode requires a one-time setup for Voice features. Install AI dependencies?", - "Yes", - "No" + // Something is missing, so the slow path has to run β€” but at most once per session. + if (sharedSetupRun) { + this.log( + "Voice dependency setup already ran in this session; not repeating.", ); + return sharedSetupRun; + } + + sharedSetupRun = (async () => { + const lock = await this.acquireLock(); + if (!lock) { + this.log( + "Another window is setting up voice dependencies; skipping this run.", + ); + return fs.existsSync(venvPythonPath) ? venvPythonPath : null; + } - if (selection !== "Yes") { - this.log("User declined dependency installation."); + try { + return await this.runSetup(venvPythonPath); + } finally { + this.releaseLock(); + } + })(); + + return sharedSetupRun; + } + + /** + * The slow path, always under the lock: create the venv if needed, then install. + */ + async runSetup(venvPythonPath) { + let state = this.readState(); + + // Gate the *whole* slow path, not just the pip install. Creating the venv is itself + // expensive and can itself keep failing β€” an unguarded retry there is exactly the + // loop of "Creating isolated Python environment..." this is meant to end. + if (!(await this.shouldAttemptSetup(state))) { + this.log( + `Skipping setup: ${state.attempts} previous attempt(s) failed ` + + `(${state.lastError || "unknown error"}). Use the prompt's Retry action.`, + ); + return null; + } + state = this.readState(); + + if (!fs.existsSync(venvPythonPath)) { + if (!(await this.confirmFirstRun(state))) return null; + + const created = await this.setupVirtualEnvironment(); + if (!created) { + await this.recordFailure(state, "venv creation failed"); + return null; + } + + // `python -m venv` can exit 0 and still not produce the interpreter we expect + // (a layout difference, or a half-written directory from an earlier crash). + // Without this check that state loops forever: the path stays missing, nothing + // records a failure, and every activation tries to create it again. + if (!fs.existsSync(venvPythonPath)) { + await this.recordFailure( + state, + `venv created but ${path.basename(venvPythonPath)} is missing`, + ); + this.log(`Expected interpreter at: ${venvPythonPath}`); + this.log(`Actually present: ${this.describeVenvContents()}`); + vscode.window.showErrorMessage( + "EchoCode: the Python environment was created but its interpreter is missing. See the EchoCode output channel.", + ); return null; } - const success = await this.setupVirtualEnvironment(); - if (!success) return null; + // A fresh venv seeds its own pip, so this is the only moment an upgrade is worth + // a network round trip. + await this.upgradePip(venvPythonPath); } - // 4. Check if packages are installed - const packagesReady = await this.checkPackages(venvPythonPath); - if (!packagesReady) { - await this.installPackages(venvPythonPath); + if (await this.checkPackages(venvPythonPath)) { + await this.context.globalState.update( + "echoCodePythonPath", + venvPythonPath, + ); + await this.recordState({ status: "ok", attempts: 0 }); + this.log(`Voice dependencies ready. Using: ${venvPythonPath}`); + return venvPythonPath; } - // Save this path to global state so other files can easier access it - await this.context.globalState.update("echoCodePythonPath", venvPythonPath); + const installed = await this.installPackages(venvPythonPath); + if (!installed) { + await this.recordFailure(state, this.lastInstallError || "install failed"); + return null; + } + await this.context.globalState.update("echoCodePythonPath", venvPythonPath); + await this.recordState({ status: "ok", attempts: 0 }); this.log(`Voice dependencies ready. Using: ${venvPythonPath}`); return venvPythonPath; } + /* ---------------------------------------------------------------- * + * Paths + * ---------------------------------------------------------------- */ + + venvPythonPath() { + const isWin = process.platform === "win32"; + return path.join( + this.venvPath, + isWin ? "Scripts" : "bin", + isWin ? "python.exe" : "python3", + ); + } + + /** + * Resolves the venv's site-packages directory. Windows keeps it at a fixed path; + * POSIX nests it under a version-specific folder, so that one has to be discovered. + */ + sitePackagesDir() { + if (process.platform === "win32") { + return path.join(this.venvPath, "Lib", "site-packages"); + } + + const libDir = path.join(this.venvPath, "lib"); + try { + const pythonDir = fs + .readdirSync(libDir) + .find((entry) => entry.startsWith("python3")); + return pythonDir ? path.join(libDir, pythonDir, "site-packages") : null; + } catch { + return null; + } + } + + /** + * Whether faster-whisper's package directory is on disk. + * + * Spawning Python to `import faster_whisper` costs an interpreter start plus loading + * ctranslate2 and onnxruntime β€” measured at over a second cold, and worse on Windows + * where each DLL is scanned. A stat answers the same question for free. + * + * Deliberately not cached in globalState: a stale "ready" flag would survive the user + * removing the package and break voice at runtime, whereas this re-checks reality on + * every launch and still costs nothing. + */ + packagesPresentOnDisk() { + const sitePackages = this.sitePackagesDir(); + return Boolean( + sitePackages && fs.existsSync(path.join(sitePackages, "faster_whisper")), + ); + } + + /* ---------------------------------------------------------------- * + * Cross-window lock + * ---------------------------------------------------------------- */ + + /** + * Takes an exclusive setup lock, or returns false if another window holds it. + * Uses an O_EXCL create, which is atomic, rather than an exists-then-write race. + */ + async acquireLock() { + for (let attempt = 0; attempt < 2; attempt += 1) { + try { + await fs.promises.writeFile( + this.lockPath, + JSON.stringify({ pid: process.pid, startedAt: new Date().toISOString() }), + { flag: "wx" }, + ); + return true; + } catch (err) { + if (err.code !== "EEXIST") { + // Cannot even create a lock file; proceed unlocked rather than disabling + // voice entirely, since the single-window case is the common one. + this.log(`Could not create setup lock (${err.code}); continuing.`); + return true; + } + + // Held. Steal it only if it is old enough to be certainly abandoned. + let age = 0; + try { + age = Date.now() - (await fs.promises.stat(this.lockPath)).mtimeMs; + } catch { + // Vanished between the failed create and the stat: retry the create. + continue; + } + + if (age < LOCK_STALE_MS) return false; + + this.log( + `Clearing stale setup lock (${Math.round(age / 1000)}s old).`, + ); + await fs.promises.rm(this.lockPath, { force: true }); + } + } + return false; + } + + releaseLock() { + try { + fs.rmSync(this.lockPath, { force: true }); + } catch (err) { + this.log(`Could not remove setup lock: ${err.message}`); + } + } + + /* ---------------------------------------------------------------- * + * Attempt bookkeeping + * ---------------------------------------------------------------- */ + + readState() { + const stored = this.context.globalState.get(INSTALL_STATE_KEY); + return { + status: stored?.status || "unknown", + attempts: Number(stored?.attempts) || 0, + lastError: stored?.lastError || "", + declined: Boolean(stored?.declined), + }; + } + + async recordState(patch) { + const next = { ...this.readState(), ...patch }; + await this.context.globalState.update(INSTALL_STATE_KEY, next); + return next; + } + + async recordFailure(state, reason) { + const attempts = state.attempts + 1; + await this.recordState({ + status: "failed", + attempts, + lastError: String(reason).slice(0, 500), + }); + this.log(`Setup failed (attempt ${attempts}): ${reason}`); + } + + /** + * First-run consent. A previous decline is remembered so the prompt does not reappear + * on every window open; the user can still opt in later from the same prompt path. + */ + async confirmFirstRun(state) { + if (state.declined) { + this.log("Voice setup was previously declined; skipping."); + return false; + } + + const selection = await vscode.window.showInformationMessage( + "EchoCode requires a one-time setup for Voice features. Install AI dependencies?", + "Yes", + "Not now", + "Don't ask again", + ); + + if (selection === "Yes") return true; + + if (selection === "Don't ask again") { + await this.recordState({ declined: true }); + } + this.log(`User declined dependency installation (${selection || "dismissed"}).`); + return false; + } + + /** + * Whether to spend another full setup. Past the attempt limit we ask instead of + * silently redoing it, because a repeat failure is almost always a persistent cause + * (no compiler, no network, unsupported Python) that retrying will not fix. + */ + async shouldAttemptSetup(state) { + if (state.attempts < MAX_INSTALL_ATTEMPTS) return true; + + const selection = await vscode.window.showWarningMessage( + `EchoCode Voice setup has failed ${state.attempts} times (${state.lastError || "unknown error"}). Retry?`, + "Retry Setup", + "Not now", + ); + + if (selection !== "Retry Setup") return false; + + await this.recordState({ attempts: 0, status: "unknown" }); + return true; + } + + /** + * One-line summary of what the venv directory actually contains. Logged only when the + * expected interpreter is missing β€” that is the moment the contents are the whole + * diagnostic, and guessing from the outside is hopeless. + */ + describeVenvContents() { + const binDir = path.join( + this.venvPath, + process.platform === "win32" ? "Scripts" : "bin", + ); + try { + const entries = fs.readdirSync(binDir); + return entries.length + ? `${binDir} -> ${entries.slice(0, 12).join(", ")}` + : `${binDir} is empty`; + } catch (err) { + try { + return `${binDir} unreadable (${err.code}); venv root -> ${fs.readdirSync(this.venvPath).join(", ")}`; + } catch { + return `${this.venvPath} does not exist or is unreadable`; + } + } + } + + /* ---------------------------------------------------------------- * + * Setup steps + * ---------------------------------------------------------------- */ + async setupVirtualEnvironment() { return vscode.window.withProgress( { @@ -75,105 +399,166 @@ class DependencyManager { title: "EchoCode: Creating isolated Python environment...", cancellable: false, }, - async (progress) => { + async () => { try { const systemPython = await this.findSystemPython(); if (!systemPython) { vscode.window.showErrorMessage( - "Could not find Python installed on your system. Please install Python 3.10 or 3.11." + "Could not find Python installed on your system. Please install Python 3.10 or newer.", ); return false; } - this.log( - `Creating venv at ${this.venvPath} using ${systemPython}...` + this.log(`Creating venv at ${this.venvPath} using ${systemPython}...`); + await this.runCommand( + `${systemPython} -m venv "${this.venvPath}"`, + VENV_CREATE_TIMEOUT_MS, ); - - await this.runCommand(`${systemPython} -m venv "${this.venvPath}"`); return true; } catch (error) { vscode.window.showErrorMessage( - `Failed to create python environment: ${error.message}` + `Failed to create python environment: ${error.message}`, ); return false; } - } + }, ); } + /** + * Best-effort only. An old pip can break a wheel install, but a failed *upgrade* is + * no reason to abandon setup β€” so this never propagates. + */ + async upgradePip(pythonPath) { + try { + await this.runCommand( + `"${pythonPath}" -m pip install --upgrade pip --disable-pip-version-check --no-input`, + PIP_INSTALL_TIMEOUT_MS, + ); + } catch (err) { + this.log(`pip self-upgrade failed (continuing anyway): ${err.message}`); + } + } + async installPackages(pythonPath) { + this.lastInstallError = ""; + return vscode.window.withProgress( { location: vscode.ProgressLocation.Notification, - title: - "EchoCode: Installing Faster-Whisper (This may take a minute)...", + title: "EchoCode: Installing Faster-Whisper (this can take several minutes)...", cancellable: false, }, - async (progress) => { + async () => { try { this.log("Installing faster-whisper and packages..."); - - // Upgrade pip first - await this.runCommand(`"${pythonPath}" -m pip install --upgrade pip`); - - // FIX: Install 'faster-whisper' instead of 'openai-whisper' await this.runCommand( - `"${pythonPath}" -m pip install faster-whisper` + `"${pythonPath}" -m pip install faster-whisper --disable-pip-version-check --no-input`, + PIP_INSTALL_TIMEOUT_MS, ); - vscode.window.showInformationMessage( - "EchoCode Voice setup complete!" - ); + // Trust the filesystem over pip's exit code: a partially-installed wheel can + // still exit 0, and shipping a broken venv is worse than reporting a failure. + if (!this.packagesPresentOnDisk()) { + this.lastInstallError = + "pip reported success but faster_whisper is not in site-packages"; + vscode.window.showErrorMessage( + `EchoCode: voice setup did not complete β€” ${this.lastInstallError}.`, + ); + return false; + } + + vscode.window.showInformationMessage("EchoCode Voice setup complete!"); return true; } catch (error) { + this.lastInstallError = error.message; vscode.window.showErrorMessage( - `Failed to install packages: ${error.message}` + `Failed to install packages: ${error.message}`, ); return false; } - } + }, ); } + /** + * Authoritative package check: the on-disk stat first, falling back to an import only + * when the stat is inconclusive. + */ async checkPackages(pythonPath) { + if (this.packagesPresentOnDisk()) { + this.log("faster-whisper found in site-packages; skipping import check."); + return true; + } + try { - // FIX: Check for faster_whisper import - await this.runCommand(`"${pythonPath}" -c "import faster_whisper"`); + await this.runCommand( + `"${pythonPath}" -c "import faster_whisper"`, + PROBE_TIMEOUT_MS, + ); return true; - } catch (e) { + } catch { return false; } } async findSystemPython() { - // Try specifically stable versions first + // Newest first: faster-whisper's wheels track current Python, and macOS's bundled + // python3 is an old Xcode build that should only ever be the last resort. const candidates = process.platform === "win32" - ? ["py -3.11", "py -3.10", "python3.11", "python3.10", "python"] - : ["python3.11", "python3.10", "python3"]; + ? ["py -3.12", "py -3.11", "py -3.10", "python"] + : ["python3.12", "python3.11", "python3.10", "python3"]; for (const cmd of candidates) { try { - await this.runCommand(`${cmd} --version`); + const version = await this.runCommand( + `${cmd} --version`, + PROBE_TIMEOUT_MS, + ); + this.log(`Using ${cmd} (${version}).`); return cmd; - } catch (e) { - // continue + } catch { + // Not present, or it hung and was killed by the timeout. Try the next. } } return null; } - runCommand(cmd) { + /** + * Runs a shell command with a hard timeout and a generous output buffer, logging how + * long it took. The duration matters: it is what makes a slow activation diagnosable + * from the output channel instead of a guess. + */ + runCommand(cmd, timeoutMs = PROBE_TIMEOUT_MS) { return new Promise((resolve, reject) => { this.log(`> ${cmd}`); - exec(cmd, (error, stdout, stderr) => { - if (error) { - this.log(`Error: ${stderr}`); - reject(error); - } else { - resolve(stdout.trim()); - } - }); + const startedAt = Date.now(); + + exec( + cmd, + { + timeout: timeoutMs, + windowsHide: true, + maxBuffer: MAX_OUTPUT_BYTES, + }, + (error, stdout, stderr) => { + const elapsed = Date.now() - startedAt; + + if (error) { + // `killed` is how exec reports both its own timeout and an ENOBUFS kill. + const detail = error.killed + ? `timed out or was killed after ${timeoutMs}ms` + : (stderr || error.message || "").trim().split("\n")[0]; + this.log(` failed in ${elapsed}ms: ${detail}`); + reject(new Error(detail || `Command failed: ${cmd}`)); + return; + } + + this.log(` ok in ${elapsed}ms`); + resolve(String(stdout || "").trim()); + }, + ); }); } diff --git a/program_features/Voice/voiceCommandRouter.js b/program_features/Voice/voiceCommandRouter.js index 1d016ea..d2d99f6 100644 --- a/program_features/Voice/voiceCommandRouter.js +++ b/program_features/Voice/voiceCommandRouter.js @@ -80,28 +80,211 @@ function loadVoiceCommands() { return JSON.parse(fs.readFileSync(commandsPath, "utf-8")); } -async function tryExecuteInternalCommand(cleanedTranscript, outputChannel) { - const commands = loadVoiceCommands(); +function normalizeForMatching(text) { + return String(text || "") + .toLowerCase() + .replace(/[^a-z0-9\s]/g, " ") + .replace(/\s+/g, " ") + .trim(); +} + +function levenshteinDistance(a, b) { + if (a === b) return 0; + if (!a.length) return b.length; + if (!b.length) return a.length; + + const rows = a.length + 1; + const cols = b.length + 1; + const dp = Array.from({ length: rows }, () => Array(cols).fill(0)); + + for (let i = 0; i < rows; i++) dp[i][0] = i; + for (let j = 0; j < cols; j++) dp[0][j] = j; + + for (let i = 1; i < rows; i++) { + for (let j = 1; j < cols; j++) { + const cost = a[i - 1] === b[j - 1] ? 0 : 1; + dp[i][j] = Math.min( + dp[i - 1][j] + 1, + dp[i][j - 1] + 1, + dp[i - 1][j - 1] + cost, + ); + } + } + + return dp[a.length][b.length]; +} + +function normalizedEditSimilarity(a, b) { + const x = normalizeForMatching(a); + const y = normalizeForMatching(b); + if (!x || !y) return 0; + + const maxLen = Math.max(x.length, y.length); + if (maxLen === 0) return 1; + + const distance = levenshteinDistance(x, y); + return 1 - distance / maxLen; +} + +function tokenSet(text) { + const normalized = normalizeForMatching(text); + if (!normalized) return new Set(); + return new Set(normalized.split(" ").filter(Boolean)); +} + +function jaccardSimilarity(a, b) { + const setA = tokenSet(a); + const setB = tokenSet(b); + if (setA.size === 0 || setB.size === 0) return 0; + + let intersection = 0; + for (const item of setA) { + if (setB.has(item)) intersection++; + } + + const union = setA.size + setB.size - intersection; + return union > 0 ? intersection / union : 0; +} + +function bestWindowEditSimilarity(transcript, phrase) { + const transcriptTokens = normalizeForMatching(transcript) + .split(" ") + .filter(Boolean); + const phraseTokens = normalizeForMatching(phrase).split(" ").filter(Boolean); + + if (!transcriptTokens.length || !phraseTokens.length) return 0; + + const phraseLen = phraseTokens.length; + let best = 0; + + for ( + let windowLen = Math.max(1, phraseLen - 1); + windowLen <= phraseLen + 1; + windowLen++ + ) { + if (windowLen > transcriptTokens.length) continue; + for (let i = 0; i <= transcriptTokens.length - windowLen; i++) { + const windowPhrase = transcriptTokens.slice(i, i + windowLen).join(" "); + const score = normalizedEditSimilarity(windowPhrase, phrase); + if (score > best) best = score; + } + } + + return best; +} + +function tokenAlignmentSimilarity(transcript, phrase) { + const transcriptTokens = normalizeForMatching(transcript) + .split(" ") + .filter(Boolean); + const phraseTokens = normalizeForMatching(phrase).split(" ").filter(Boolean); + + if (!transcriptTokens.length || !phraseTokens.length) return 0; + + let total = 0; + for (const phraseToken of phraseTokens) { + let best = 0; + for (const transcriptToken of transcriptTokens) { + const sim = normalizedEditSimilarity(transcriptToken, phraseToken); + if (sim > best) best = sim; + } + total += best; + } + + return total / phraseTokens.length; +} + +function scoreTranscriptToKeyword(transcript, keyword) { + const normalizedTranscript = normalizeForMatching(transcript); + const normalizedKeyword = normalizeForMatching(keyword); + if (!normalizedTranscript || !normalizedKeyword) return 0; + + if (normalizedTranscript.includes(normalizedKeyword)) { + return 1; + } + + const globalEdit = normalizedEditSimilarity( + normalizedTranscript, + normalizedKeyword, + ); + const windowEdit = bestWindowEditSimilarity( + normalizedTranscript, + normalizedKeyword, + ); + const tokenScore = jaccardSimilarity(normalizedTranscript, normalizedKeyword); + const tokenEdit = tokenAlignmentSimilarity( + normalizedTranscript, + normalizedKeyword, + ); + + // Weighted combination favors local phrase and token-level edit fit. + return Math.max( + windowEdit * 0.5 + tokenEdit * 0.35 + tokenScore * 0.15, + globalEdit * 0.5 + tokenEdit * 0.35 + tokenScore * 0.15, + ); +} + +function getMinimumScoreThreshold(keyword) { + const normalized = normalizeForMatching(keyword); + if (normalized.length <= 4) return 0.94; + if (normalized.split(" ").length === 1) return 0.83; + return 0.65; +} + +function findBestInternalCommand(cleanedTranscript, commands) { + let best = null; for (const command of commands) { - if ( - !command.keywords.some((keyword) => cleanedTranscript.includes(keyword)) - ) { - continue; + const keywords = Array.isArray(command.keywords) ? command.keywords : []; + let commandBestScore = 0; + let commandBestKeyword = null; + + for (const keyword of keywords) { + const score = scoreTranscriptToKeyword(cleanedTranscript, keyword); + if (score > commandBestScore) { + commandBestScore = score; + commandBestKeyword = keyword; + } } - const currentMode = getMode(); - if (currentMode !== "dev" && STUDENT_LOCKED_COMMANDS.has(command.id)) { - await speakMessage("That command is disabled in student mode."); - return { handled: true }; + if (!commandBestKeyword) continue; + + const threshold = getMinimumScoreThreshold(commandBestKeyword); + if (commandBestScore < threshold) continue; + + if (!best || commandBestScore > best.score) { + best = { + command, + score: commandBestScore, + matchedKeyword: commandBestKeyword, + }; } + } - await vscode.commands.executeCommand(command.id); - outputChannel.appendLine(`[Voice Command] Matched: ${command.id}`); - return { handled: true, command: command.id }; + return best; +} + +async function tryExecuteInternalCommand(cleanedTranscript, outputChannel) { + const commands = loadVoiceCommands(); + + const bestMatch = findBestInternalCommand(cleanedTranscript, commands); + if (!bestMatch) { + return null; } - return null; + const { command, score, matchedKeyword } = bestMatch; + + const currentMode = getMode(); + if (currentMode !== "dev" && STUDENT_LOCKED_COMMANDS.has(command.id)) { + await speakMessage("That command is disabled in student mode."); + return { handled: true }; + } + + await vscode.commands.executeCommand(command.id); + outputChannel.appendLine( + `[Voice Command] Matched: ${command.id} (keyword: "${matchedKeyword}", score: ${score.toFixed(2)})`, + ); + return { handled: true, command: command.id }; } async function tryExecuteExternalCommand(cleanedTranscript, outputChannel) { @@ -200,9 +383,10 @@ async function tryGenerateCode(transcript, outputChannel) { } } -async function tryExecuteVoiceCommand(transcript, outputChannel) { +async function tryExecuteVoiceCommand(transcript, outputChannel, options = {}) { try { - const cleanedTranscript = transcript.toLowerCase().trim(); + const allowCodeGeneration = options.allowCodeGeneration !== false; + const cleanedTranscript = normalizeForMatching(transcript); if ( !cleanedTranscript || cleanedTranscript.includes("no speech detected") @@ -236,6 +420,13 @@ async function tryExecuteVoiceCommand(transcript, outputChannel) { return { handled: false }; } + if (!allowCodeGeneration) { + outputChannel.appendLine( + `[Voice Intent] No command match for: ${transcript}. Command-only mode prevents code generation fallback.`, + ); + return { handled: false }; + } + outputChannel.appendLine( `[Voice Intent] No strict command match for: ${transcript}. Attempting Code Generation...`, ); diff --git a/test/aiProviderSetup.test.ts b/test/aiProviderSetup.test.ts new file mode 100644 index 0000000..81506bb --- /dev/null +++ b/test/aiProviderSetup.test.ts @@ -0,0 +1,550 @@ +import "./helpers/vscodeMock.js"; +import { strict as assert } from "assert"; +import { suite, test } from "mocha"; +import * as path from "path"; + +import { createRequire } from "module"; + +// Seeded from an explicit path rather than `import.meta.url`, because these files must +// load under two different runtimes: +// - Node 20/22 (CI): ts-node compiles them to CommonJS, where `import.meta` is a hard +// compile error (TS1470) but a bare `require` exists. +// - Node 24 (local): Node strips types natively and detects ESM from the `import` +// syntax, where `import.meta` is fine but `require` is not defined. +// createRequire with a plain path uses no meta-property, so it compiles and runs on +// both, and still yields the real CJS require the suite needs for require.cache mocking. +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); +const repoRoot = process.cwd(); +const modulePath = nodeRequire.resolve( + path.join( + repoRoot, + "Core/program_settings/program_settings/aiProviderSetup.js", + ), +); +const aiRequestModulePath = nodeRequire.resolve( + path.join(repoRoot, "Core/program_settings/program_settings/AIrequest.js"), +); +const secretStorePath = nodeRequire.resolve( + path.join(repoRoot, "Core/program_settings/program_settings/secretStore.js"), +); + +const speechHandlerPath = nodeRequire.resolve( + path.join(repoRoot, "Core/program_settings/speech_settings/speechHandler.js"), +); + +/** + * The setup flow speaks at each step. The real handler drives the OS speech engine and + * only resolves once the sentence has finished being read aloud, which makes these + * tests both audible and many seconds long, so stub it out for the whole suite. + */ +function silenceSpeech() { + nodeRequire.cache[speechHandlerPath] = { + id: speechHandlerPath, + filename: speechHandlerPath, + loaded: true, + exports: { speakMessage: async () => {} }, + } as any; +} + +function loadAiProviderSetupModule() { + silenceSpeech(); + delete nodeRequire.cache[modulePath]; + delete nodeRequire.cache[aiRequestModulePath]; + return nodeRequire(modulePath); +} + +function mockContext(initialState: Record = {}) { + const state = { ...initialState }; + const secrets = new Map(); + return { + globalState: { + get: (key: string, fallback: unknown) => + Object.prototype.hasOwnProperty.call(state, key) + ? state[key] + : fallback, + update: async (key: string, value: unknown) => { + state[key] = value; + }, + }, + secrets: { + get: async (key: string) => secrets.get(key), + store: async (key: string, value: string) => { + secrets.set(key, value); + }, + delete: async (key: string) => { + secrets.delete(key); + }, + }, + __state: state, + __secrets: secrets, + }; +} + +function mockEchoCodeConfig() { + const vscodeGlobal: any = (globalThis as any).vscode; + const original = vscodeGlobal.workspace.getConfiguration; + const original2 = vscodeGlobal.ConfigurationTarget; + const values: Record = {}; + + vscodeGlobal.ConfigurationTarget = { Global: "global" }; + vscodeGlobal.workspace.getConfiguration = () => ({ + get: (key: string, fallback: unknown) => + Object.prototype.hasOwnProperty.call(values, key) + ? values[key] + : fallback, + update: async (key: string, value: unknown) => { + values[key] = value; + }, + }); + + return { + values, + restore: () => { + vscodeGlobal.workspace.getConfiguration = original; + vscodeGlobal.ConfigurationTarget = original2; + }, + }; +} + +function mockWindow(overrides: Record) { + const vscodeGlobal: any = (globalThis as any).vscode; + const original = vscodeGlobal.window; + const originalProgressLocation = vscodeGlobal.ProgressLocation; + + vscodeGlobal.ProgressLocation = { Notification: 15 }; + vscodeGlobal.window = { + ...original, + // Every long-running step in setup runs inside withProgress; run the task inline. + withProgress: async (_options: any, task: any) => + task({ report: () => {} }, { onCancellationRequested: () => {} }), + ...overrides, + }; + + return () => { + vscodeGlobal.window = original; + vscodeGlobal.ProgressLocation = originalProgressLocation; + }; +} + +/** + * Installs a fake http.request that dispatches on request path, so one test can serve + * /api/tags, /api/pull and /models without caring about call order. + */ +function mockHttp(routes: Record) { + const http = nodeRequire("http"); + const original = http.request; + const { EventEmitter } = nodeRequire("events"); + const seen: { path: string; body: string }[] = []; + + http.request = (options: any, callback: (res: any) => void) => { + const req = new EventEmitter() as any; + let body = ""; + + req.write = (chunk: any) => { + body += chunk.toString(); + }; + req.setTimeout = () => {}; + req.destroy = () => {}; + req.end = () => { + seen.push({ path: options.path, body }); + + const route = Object.entries(routes).find(([p]) => + String(options.path).startsWith(p), + )?.[1]; + + const res = new EventEmitter() as any; + res.statusCode = route ? 200 : 404; + res.setEncoding = () => {}; + callback(res); + + if (!route) { + res.emit("data", JSON.stringify({ error: "not found" })); + } else if (route.ndjson) { + for (const line of route.ndjson) res.emit("data", `${line}\n`); + } else { + res.emit("data", JSON.stringify(route.json)); + } + res.emit("end"); + }; + + return req; + }; + + return { + seen, + restore: () => { + http.request = original; + }, + }; +} + +function noCopilot() { + const vscodeGlobal: any = (globalThis as any).vscode; + const originalLm = vscodeGlobal.lm; + vscodeGlobal.lm = { selectChatModels: async () => [] }; + return () => { + vscodeGlobal.lm = originalLm; + }; +} + +suite("aiProviderSetup", () => { + test("pickProviderAndModel persists chosen Copilot model", async () => { + const vscodeGlobal: any = (globalThis as any).vscode; + const config = mockEchoCodeConfig(); + + const originalLm = vscodeGlobal.lm; + vscodeGlobal.lm = { + selectChatModels: async () => [ + { id: "gpt-4o", name: "GPT-4o", family: "gpt-4o" }, + { id: "gpt-4o-mini", name: "GPT-4o Mini", family: "gpt-4o-mini" }, + ], + }; + + const restoreWindow = mockWindow({ + // Three picks now: API-or-Local, then Copilot-or-other-API, then the model. + showQuickPick: async (items: any[]) => { + if (items.some((i) => i.value === "api" && i.label.includes("API model"))) { + return items.find((i: any) => i.value === "api"); + } + if (items.some((i) => i.value === "copilot")) { + return items.find((i: any) => i.value === "copilot"); + } + return items.find((i: any) => i.value === "gpt-4o-mini"); + }, + showInformationMessage: () => {}, + showWarningMessage: async () => undefined, + }); + + const context = mockContext(); + + try { + const mod = loadAiProviderSetupModule(); + const saved = await mod.pickProviderAndModel(context, undefined); + + assert.equal(saved, true); + assert.equal(config.values.aiProvider, "copilot"); + assert.equal(config.values.useLocalOllama, false); + assert.equal(config.values.copilotModel, "gpt-4o-mini"); + assert.equal(context.__state["echocode.aiProviderConfigured"], true); + } finally { + vscodeGlobal.lm = originalLm; + restoreWindow(); + config.restore(); + } + }); + + test("pickProviderAndModel persists chosen Ollama model from live /api/tags list", async () => { + const config = mockEchoCodeConfig(); + const restoreLm = noCopilot(); + const http = mockHttp({ + "/api/tags": { + json: { models: [{ name: "llama3.2" }, { name: "qwen2.5:7b" }] }, + }, + }); + + const restoreWindow = mockWindow({ + showQuickPick: async (items: any[]) => { + if (items.some((i) => i.value === "local")) { + return items.find((i: any) => i.value === "local"); + } + return items.find((i: any) => i.value === "qwen2.5:7b"); + }, + showInformationMessage: () => {}, + showWarningMessage: async () => undefined, + }); + + const context = mockContext(); + + try { + const mod = loadAiProviderSetupModule(); + const saved = await mod.pickProviderAndModel(context, undefined); + + assert.equal(saved, true); + assert.equal(config.values.aiProvider, "ollama"); + assert.equal(config.values.useLocalOllama, true); + assert.equal(config.values.ollamaModel, "qwen2.5:7b"); + } finally { + http.restore(); + restoreLm(); + restoreWindow(); + config.restore(); + } + }); + + test("unreachable Ollama warns and hands the user back to the provider choice", async () => { + const config = mockEchoCodeConfig(); + const restoreLm = noCopilot(); + // No routes registered, so /api/tags 404s and the scan reports Ollama as down. + const http = mockHttp({}); + + const providerPicks: string[] = []; + let warning = ""; + + const restoreWindow = mockWindow({ + showQuickPick: async (items: any[]) => { + if (items.some((i) => i.value === "local")) { + // First time through pick Local (which fails); second time cancel out. + providerPicks.push("top"); + return providerPicks.length === 1 + ? items.find((i: any) => i.value === "local") + : undefined; + } + return undefined; + }, + showWarningMessage: async (message: string) => { + warning = message; + return "Use an API Instead"; + }, + showInformationMessage: () => {}, + }); + + try { + const mod = loadAiProviderSetupModule(); + const saved = await mod.pickProviderAndModel(mockContext(), undefined); + + assert.equal(saved, false); + assert.ok( + warning.includes("Ollama is not responding"), + `expected an Ollama-not-detected warning, got: ${warning}`, + ); + assert.equal( + providerPicks.length, + 2, + "expected the provider choice to be shown again after the failed local branch", + ); + assert.equal(config.values.aiProvider, undefined); + } finally { + http.restore(); + restoreLm(); + restoreWindow(); + config.restore(); + } + }); + + test("no installed models triggers a recommendation, pulls it, and selects it", async () => { + const config = mockEchoCodeConfig(); + const restoreLm = noCopilot(); + const http = mockHttp({ + "/api/tags": { json: { models: [] } }, + "/api/pull": { + ndjson: [ + JSON.stringify({ status: "pulling manifest" }), + JSON.stringify({ status: "downloading", completed: 50, total: 100 }), + JSON.stringify({ status: "success" }), + ], + }, + }); + + let recommendedLabel = ""; + + const restoreWindow = mockWindow({ + showQuickPick: async (items: any[]) => { + if (items.some((i) => i.value === "local")) { + return items.find((i: any) => i.value === "local"); + } + // The model-install pick: the recommendation is always the first entry. + recommendedLabel = items[0].label; + return items[0]; + }, + showWarningMessage: async () => "Recommend a Model", + showInformationMessage: async (_msg: string, options: any) => + // Only the modal install confirmation passes an options object. + options && options.modal ? "Install" : undefined, + }); + + const context = mockContext(); + + try { + const mod = loadAiProviderSetupModule(); + const saved = await mod.pickProviderAndModel(context, undefined); + + assert.equal(saved, true); + assert.equal(config.values.aiProvider, "ollama"); + + const pulled = http.seen.find((r) => r.path === "/api/pull"); + assert.ok(pulled, "expected a pull request to Ollama"); + + const pullBody = JSON.parse(pulled!.body); + assert.equal( + pullBody.name, + config.values.ollamaModel, + "the pulled model should be the one saved as the active model", + ); + assert.ok( + recommendedLabel.includes(String(config.values.ollamaModel)), + `recommendation "${recommendedLabel}" should match saved model ${config.values.ollamaModel}`, + ); + } finally { + http.restore(); + restoreLm(); + restoreWindow(); + config.restore(); + } + }); + + test("a failed pull is reported and does not select the model", async () => { + const config = mockEchoCodeConfig(); + const restoreLm = noCopilot(); + // Ollama answers 200 and reports the failure inside the NDJSON stream. + const http = mockHttp({ + "/api/tags": { json: { models: [] } }, + "/api/pull": { + ndjson: [ + JSON.stringify({ status: "pulling manifest" }), + JSON.stringify({ error: "model 'nope' not found" }), + ], + }, + }); + + let errorShown = ""; + let warningCount = 0; + + const restoreWindow = mockWindow({ + showQuickPick: async (items: any[]) => { + if (items.some((i) => i.value === "local")) { + // Pick Local the first time; cancel when we land back at the top choice. + return warningCount <= 1 + ? items.find((i: any) => i.value === "local") + : undefined; + } + return items[0]; + }, + showWarningMessage: async () => { + warningCount += 1; + // Offer to install the first time; back out to the API branch afterwards. + return warningCount === 1 ? "Recommend a Model" : "Use an API Instead"; + }, + showInformationMessage: async (_msg: string, options: any) => + options && options.modal ? "Install" : undefined, + showErrorMessage: async (message: string) => { + errorShown = message; + return undefined; + }, + }); + + try { + const mod = loadAiProviderSetupModule(); + const saved = await mod.pickProviderAndModel(mockContext(), undefined); + + assert.equal(saved, false); + assert.ok( + errorShown.includes("not found"), + `expected the pull failure to surface, got: ${errorShown}`, + ); + assert.equal( + config.values.ollamaModel, + undefined, + "a model that failed to download must not be saved as the active model", + ); + } finally { + http.restore(); + restoreLm(); + restoreWindow(); + config.restore(); + } + }); + + test("hosted API branch verifies the endpoint, saves settings, and keeps the key in secret storage", async () => { + const config = mockEchoCodeConfig(); + const restoreLm = noCopilot(); + const http = mockHttp({ + "/v1/models": { + json: { data: [{ id: "some-model" }, { id: "other-model" }] }, + }, + }); + + const restoreWindow = mockWindow({ + showQuickPick: async (items: any[]) => { + if (items.some((i) => i.value === "local")) { + return items.find((i: any) => i.value === "api"); + } + if (items.some((i) => i.value === "copilot")) { + // "Another model API", not Copilot. + return items.find((i: any) => i.value === "api"); + } + if (items.some((i) => i.value === "custom")) { + return items.find((i: any) => i.value === "custom"); + } + return items.find((i: any) => i.value === "other-model"); + }, + showInputBox: async (options: any) => { + if (String(options.prompt).includes("base URL")) { + return "http://localhost:9999/v1"; + } + return "test-key-123"; + }, + showInformationMessage: () => {}, + showWarningMessage: async () => undefined, + showErrorMessage: async () => undefined, + }); + + const context = mockContext(); + const { initSecretStorage } = nodeRequire(secretStorePath); + initSecretStorage(context); + + try { + const mod = loadAiProviderSetupModule(); + const saved = await mod.pickProviderAndModel(context, undefined); + + assert.equal(saved, true); + assert.equal(config.values.aiProvider, "api"); + assert.equal(config.values.apiProvider, "custom"); + assert.equal(config.values.apiBaseUrl, "http://localhost:9999/v1"); + assert.equal(config.values.apiModel, "other-model"); + assert.equal(config.values.useLocalOllama, false); + + // The key belongs in the keychain, never in settings.json. + assert.equal(context.__secrets.get("echocode.apiKey.custom"), "test-key-123"); + assert.ok( + !Object.keys(config.values).some((k) => k.toLowerCase().includes("key")), + "no API key should be written to settings", + ); + + const verify = http.seen.find((r) => r.path === "/v1/models"); + assert.ok(verify, "expected a model-listing call to verify reachability"); + } finally { + http.restore(); + restoreLm(); + restoreWindow(); + config.restore(); + } + }); + + test("checkForProviderUpdates warns when the configured Ollama model vanished", async () => { + const config = mockEchoCodeConfig(); + config.values.useLocalOllama = true; + config.values.ollamaModel = "removed-model"; + + const restoreLm = noCopilot(); + const http = mockHttp({ + "/api/tags": { json: { models: [{ name: "llama3.2" }] } }, + }); + + let warned = false; + const restoreWindow = mockWindow({ + showWarningMessage: async () => { + warned = true; + return "Dismiss"; + }, + }); + + const outputLines: string[] = []; + const outputChannel = { appendLine: (s: string) => outputLines.push(s) }; + + try { + const mod = loadAiProviderSetupModule(); + await mod.checkForProviderUpdates(mockContext(), outputChannel); + + assert.equal(warned, true); + assert.ok( + outputLines.some((l) => l.includes("removed-model")), + "expected a log line mentioning the missing model", + ); + } finally { + http.restore(); + restoreLm(); + restoreWindow(); + config.restore(); + } + }); +}); diff --git a/test/aiRequestRouting.test.ts b/test/aiRequestRouting.test.ts new file mode 100644 index 0000000..40458bf --- /dev/null +++ b/test/aiRequestRouting.test.ts @@ -0,0 +1,234 @@ +import "./helpers/vscodeMock.js"; +import { strict as assert } from "assert"; +import { suite, test } from "mocha"; +import * as path from "path"; +import { EventEmitter } from "events"; + +import { createRequire } from "module"; + +// Seeded from an explicit path rather than `import.meta.url`, because these files must +// load under two different runtimes: +// - Node 20/22 (CI): ts-node compiles them to CommonJS, where `import.meta` is a hard +// compile error (TS1470) but a bare `require` exists. +// - Node 24 (local): Node strips types natively and detects ESM from the `import` +// syntax, where `import.meta` is fine but `require` is not defined. +// createRequire with a plain path uses no meta-property, so it compiles and runs on +// both, and still yields the real CJS require the suite needs for require.cache mocking. +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); +const repoRoot = process.cwd(); +const aiRequestModulePath = nodeRequire.resolve( + path.join(repoRoot, "Core/program_settings/program_settings/AIrequest.js"), +); + +function loadAiRequestModule() { + delete nodeRequire.cache[aiRequestModulePath]; + return nodeRequire(aiRequestModulePath); +} + +function mockEchoCodeConfig(values: Record) { + const vscodeGlobal: any = (globalThis as any).vscode; + const original = vscodeGlobal.workspace.getConfiguration; + + vscodeGlobal.workspace.getConfiguration = () => ({ + get: (key: string, fallback: unknown) => + Object.prototype.hasOwnProperty.call(values, key) + ? values[key] + : fallback, + }); + + return () => { + vscodeGlobal.workspace.getConfiguration = original; + }; +} + +function ensureLanguageModelStubs() { + const vscodeGlobal: any = (globalThis as any).vscode; + + if (!vscodeGlobal.LanguageModelChatMessage) { + vscodeGlobal.LanguageModelChatMessage = { + User: (content: string) => ({ role: "user", content }), + Assistant: (content: string) => ({ role: "assistant", content }), + }; + } + + if (!vscodeGlobal.CancellationTokenSource) { + vscodeGlobal.CancellationTokenSource = class { + token = {}; + dispose() {} + }; + } +} + +suite("AIrequest backend routing", () => { + test("uses Copilot path when local Ollama toggle is off", async () => { + ensureLanguageModelStubs(); + const vscodeGlobal: any = (globalThis as any).vscode; + + const restoreConfig = mockEchoCodeConfig({ + useLocalOllama: false, + ollamaBaseUrl: "http://127.0.0.1:11434", + ollamaModel: "llama3.2", + }); + + const originalLm = vscodeGlobal.lm; + let selectCalled = 0; + let sendRequestCalled = 0; + + vscodeGlobal.lm = { + selectChatModels: async () => { + selectCalled += 1; + return [ + { + family: "gpt-4o", + sendRequest: async (messages: any[]) => { + sendRequestCalled += 1; + assert.equal(messages.length, 2); + return { + text: (async function* () { + yield "copilot answer"; + })(), + }; + }, + }, + ]; + }, + }; + + try { + const aiRequest = loadAiRequestModule(); + const text = await aiRequest.requestTextFromMessages([ + { role: "system", content: "You are a tutor." }, + { role: "user", content: "Explain this code." }, + ]); + + assert.equal(text, "copilot answer"); + assert.equal(selectCalled, 1); + assert.equal(sendRequestCalled, 1); + } finally { + vscodeGlobal.lm = originalLm; + restoreConfig(); + } + }); + + test("uses Ollama path when local Ollama toggle is on", async () => { + ensureLanguageModelStubs(); + const vscodeGlobal: any = (globalThis as any).vscode; + + const restoreConfig = mockEchoCodeConfig({ + useLocalOllama: true, + ollamaBaseUrl: "http://127.0.0.1:11434", + ollamaModel: "llama3.2", + }); + + const originalLm = vscodeGlobal.lm; + vscodeGlobal.lm = { + selectChatModels: async () => { + throw new Error( + "Copilot path should not be used when Ollama is enabled", + ); + }, + }; + + const http = nodeRequire("http"); + const originalHttpRequest = http.request; + let capturedPayload = ""; + let capturedPath = ""; + + http.request = (options: any, callback: (res: any) => void) => { + capturedPath = options.path; + const req = new EventEmitter() as any; + + req.write = (chunk: string | Buffer) => { + capturedPayload += chunk.toString(); + }; + + req.end = () => { + const res = new EventEmitter() as any; + res.statusCode = 200; + res.setEncoding = () => {}; + callback(res); + res.emit("data", JSON.stringify({ response: "ollama answer" })); + res.emit("end"); + }; + + return req; + }; + + try { + const aiRequest = loadAiRequestModule(); + const text = await aiRequest.requestTextFromMessages([ + { role: "system", content: "System instructions" }, + { role: "user", content: "Hello" }, + ]); + + assert.equal(text, "ollama answer"); + assert.equal(capturedPath, "/api/generate"); + + const body = JSON.parse(capturedPayload); + assert.equal(body.model, "llama3.2"); + assert.equal(body.stream, false); + assert.ok( + typeof body.prompt === "string" && body.prompt.includes("USER:"), + ); + } finally { + http.request = originalHttpRequest; + vscodeGlobal.lm = originalLm; + restoreConfig(); + } + }); + + test("analyzeAI routes through Ollama when enabled", async () => { + ensureLanguageModelStubs(); + const vscodeGlobal: any = (globalThis as any).vscode; + + const restoreConfig = mockEchoCodeConfig({ + useLocalOllama: true, + ollamaBaseUrl: "http://127.0.0.1:11434", + ollamaModel: "qwen2.5:7b", + }); + + const originalLm = vscodeGlobal.lm; + vscodeGlobal.lm = { + selectChatModels: async () => { + throw new Error("Copilot path should not be used in this test"); + }, + }; + + const http = nodeRequire("http"); + const originalHttpRequest = http.request; + let capturedPayload = ""; + + http.request = (_options: any, callback: (res: any) => void) => { + const req = new EventEmitter() as any; + + req.write = (chunk: string | Buffer) => { + capturedPayload += chunk.toString(); + }; + + req.end = () => { + const res = new EventEmitter() as any; + res.statusCode = 200; + res.setEncoding = () => {}; + callback(res); + res.emit("data", JSON.stringify({ response: "analysis result" })); + res.emit("end"); + }; + + return req; + }; + + try { + const aiRequest = loadAiRequestModule(); + const output = await aiRequest.analyzeAI("print('x')", "Summarize code"); + assert.equal(output, "analysis result"); + + const body = JSON.parse(capturedPayload); + assert.equal(body.model, "qwen2.5:7b"); + assert.ok(body.prompt.includes("Code to analyze:")); + } finally { + http.request = originalHttpRequest; + vscodeGlobal.lm = originalLm; + restoreConfig(); + } + }); +}); diff --git a/test/externalIntentRouter.test.ts b/test/externalIntentRouter.test.ts new file mode 100644 index 0000000..54fd175 --- /dev/null +++ b/test/externalIntentRouter.test.ts @@ -0,0 +1,113 @@ +import "./helpers/vscodeMock.js"; +import { strict as assert } from "assert"; +import { suite, test } from "mocha"; +import * as path from "path"; + +import { createRequire } from "module"; + +// Seeded from an explicit path rather than `import.meta.url`, because these files must +// load under two different runtimes: +// - Node 20/22 (CI): ts-node compiles them to CommonJS, where `import.meta` is a hard +// compile error (TS1470) but a bare `require` exists. +// - Node 24 (local): Node strips types natively and detects ESM from the `import` +// syntax, where `import.meta` is fine but `require` is not defined. +// createRequire with a plain path uses no meta-property, so it compiles and runs on +// both, and still yields the real CJS require the suite needs for require.cache mocking. +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); +const fs = nodeRequire("fs") as typeof import("fs"); +const repoRoot = process.cwd(); +const modulePath = nodeRequire.resolve( + path.join( + repoRoot, + "Core/program_settings/program_settings/ExternalIntentRouter.js", + ), +); + +function loadRouterWithCommands(commands: any[]) { + delete nodeRequire.cache[modulePath]; + const rawRouter = nodeRequire(modulePath); + const router = rawRouter?.default || rawRouter; + + const originalReadFileSync = fs.readFileSync; + const originalWatch = fs.watch; + + fs.readFileSync = (() => + JSON.stringify(commands)) as unknown as typeof fs.readFileSync; + // `on` is part of the FSWatcher surface the router uses to drop a broken watch. + fs.watch = (() => ({ + close: () => {}, + on: () => {}, + })) as unknown as typeof fs.watch; + + // The registry resolves its path from the extension context, so the router reports no + // commands until it is initialised. readFileSync is stubbed above, so the directory + // never has to exist. + router.initExternalCommandRegistry({ + globalStorageUri: { fsPath: path.join(repoRoot, ".test-global-storage") }, + subscriptions: [], + }); + + return { + router, + restore: () => { + fs.readFileSync = originalReadFileSync; + fs.watch = originalWatch; + delete nodeRequire.cache[modulePath]; + }, + }; +} + +suite("ExternalIntentRouter fuzzy matching", () => { + test("matches reversed phrase order like 'new terminal'", () => { + const { router, restore } = loadRouterWithCommands([ + { + id: "workbench.action.terminal.new", + title: "New Terminal", + keywords: ["terminal new", "terminal"], + }, + ]); + + try { + const result = router.matchExternalCommand("new terminal"); + assert.ok(result); + assert.equal(result.id, "workbench.action.terminal.new"); + } finally { + restore(); + } + }); + + test("matches minor STT typo for terminal command", () => { + const { router, restore } = loadRouterWithCommands([ + { + id: "workbench.action.terminal.new", + title: "New Terminal", + keywords: ["new terminal"], + }, + ]); + + try { + const result = router.matchExternalCommand("new termnal"); + assert.ok(result); + assert.equal(result.id, "workbench.action.terminal.new"); + } finally { + restore(); + } + }); + + test("returns null for unrelated transcript", () => { + const { router, restore } = loadRouterWithCommands([ + { + id: "workbench.action.terminal.new", + title: "New Terminal", + keywords: ["new terminal"], + }, + ]); + + try { + const result = router.matchExternalCommand("open markdown preview"); + assert.equal(result, null); + } finally { + restore(); + } + }); +}); diff --git a/test/helpers/vscodeMock.js b/test/helpers/vscodeMock.js index cf6794e..53adc69 100644 --- a/test/helpers/vscodeMock.js +++ b/test/helpers/vscodeMock.js @@ -1,3 +1,6 @@ +import path from "path"; +import os from "os"; + // ----- In-memory command registry ----- const commandRegistry = new Map(); @@ -67,11 +70,25 @@ export const env = { // Helper to create a context like VS Code provides export function __createMockContext() { + // Points at a throwaway directory rather than the repo: code under test writes + // generated per-user data here, and a test run must not leave files in the checkout. + const storageRoot = path.join(os.tmpdir(), "echocode-test-storage"); + return { subscriptions: [], globalState: { get: () => undefined, update: async () => {} }, workspaceState: { get: () => undefined, update: async () => {} }, extensionUri: { fsPath: process.cwd() }, + // Real VS Code always provides these; extension code may write to them without + // checking, so the mock has to supply them too. + globalStorageUri: { fsPath: storageRoot }, + storageUri: { fsPath: path.join(storageRoot, "workspace") }, + logUri: { fsPath: path.join(storageRoot, "logs") }, + secrets: { + get: async () => undefined, + store: async () => {}, + delete: async () => {}, + }, }; } @@ -89,6 +106,31 @@ export const copilot = { }, }; +/** + * Mirrors vscode.Disposable: a dispose callback, plus the static `from` combinator. + * Extension code registers cleanup this way (file watchers, feature commands), so the + * mock has to provide it for anything that runs through activate(). + */ +export class Disposable { + constructor(callOnDispose) { + this._callOnDispose = callOnDispose; + } + + dispose() { + if (typeof this._callOnDispose === "function") { + this._callOnDispose(); + } + } + + static from(...items) { + return new Disposable(() => { + for (const item of items) { + if (item && typeof item.dispose === "function") item.dispose(); + } + }); + } +} + // Build a global namespace for any code using require('vscode') globalThis.vscode = { commands, @@ -98,6 +140,7 @@ globalThis.vscode = { Position, Selection, Range, + Disposable, __createMockContext, }; globalThis.tts = tts; diff --git a/test/rootExtension.integration.test.ts b/test/rootExtension.integration.test.ts index a5649e9..469b044 100644 --- a/test/rootExtension.integration.test.ts +++ b/test/rootExtension.integration.test.ts @@ -7,7 +7,17 @@ import * as path from "path"; import * as VS from "./helpers/vscodeMock.js"; const vscode: any = VS; -const nodeRequire = require; +import { createRequire } from "module"; + +// Seeded from an explicit path rather than `import.meta.url`, because these files must +// load under two different runtimes: +// - Node 20/22 (CI): ts-node compiles them to CommonJS, where `import.meta` is a hard +// compile error (TS1470) but a bare `require` exists. +// - Node 24 (local): Node strips types natively and detects ESM from the `import` +// syntax, where `import.meta` is fine but `require` is not defined. +// createRequire with a plain path uses no meta-property, so it compiles and runs on +// both, and still yields the real CJS require the suite needs for require.cache mocking. +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); const repoRoot = process.cwd(); const extensionModulePath = nodeRequire.resolve( path.join(repoRoot, "extension.js"), @@ -15,6 +25,28 @@ const extensionModulePath = nodeRequire.resolve( type StubMap = Record; +/** + * A context that reports the AI provider as already chosen. + * + * These tests cover steady-state command wiring, not onboarding. With a virgin + * globalState the startup wizard fires, speaks its welcome line, and races the + * assertions on spokenMessages/infoMessages. The provider wizard has its own suite. + */ +function createConfiguredContext() { + const context: any = vscode.__createMockContext(); + const state: Record = { + "echocode.aiProviderConfigured": true, + }; + context.globalState = { + get: (key: string, fallback: unknown) => + Object.prototype.hasOwnProperty.call(state, key) ? state[key] : fallback, + update: async (key: string, value: unknown) => { + state[key] = value; + }, + }; + return context; +} + function createOutputChannelRecorder() { const lines: string[] = []; return { @@ -205,6 +237,7 @@ function createExtensionStubs(state: { const selectMicrophoneCalls: number[] = []; const refreshCalls: number[] = []; const buildRegistryCalls: number[] = []; + const initRegistryContexts: any[] = []; const dependencyEnsures: number[] = []; const announceCalls: string[] = []; const initializeFolderCalls: number[] = []; @@ -221,6 +254,10 @@ function createExtensionStubs(state: { }, "Core/program_settings/program_settings/ExternalIntentRouter.js": { matchExternalCommand: () => null, + initExternalCommandRegistry: (context: any) => { + initRegistryContexts.push(context); + return "/mock/global-storage/external_commands.json"; + }, buildExternalCommandRegistry: async () => { buildRegistryCalls.push(1); }, @@ -354,6 +391,7 @@ function createExtensionStubs(state: { selectMicrophoneCalls, refreshCalls, buildRegistryCalls, + initRegistryContexts, dependencyEnsures, announceCalls, initializeFolderCalls, @@ -368,7 +406,7 @@ suite("EchoCode – Root Extension Integration", () => { const harness = createVscodeHarness(); const { stubs, state } = createExtensionStubs({ isRecording: false }); const module = loadRootExtension(stubs); - const context = vscode.__createMockContext(); + const context = createConfiguredContext(); try { await module.extension.activate(context); @@ -390,6 +428,11 @@ suite("EchoCode – Root Extension Integration", () => { "echocode.checkPythonErrors", "echocode.setGuidanceLevel", "echocode.cycleGuidanceLevel", + // These are the entry points into the AI provider wizard. If activation + // throws before reaching them the palette entry exists but does nothing, + // which is indistinguishable from the command being broken. + "echocode.selectAIProvider", + "echocode.checkAIProviderUpdates", ]; for (const command of expected) { @@ -401,6 +444,13 @@ suite("EchoCode – Root Extension Integration", () => { assert.equal(state.dependencyEnsures.length, 1); assert.equal(state.buildRegistryCalls.length, 1); + // The registry resolves its path from the extension context, so activate() must + // hand it one before asking for a build; without this the build throws. + assert.equal(state.initRegistryContexts.length, 1); + assert.ok( + state.initRegistryContexts[0]?.globalStorageUri, + "registry should be initialised with a context carrying globalStorageUri", + ); assert.equal(state.initializeFolderCalls.length, 1); assert.deepEqual(state.announceCalls, ["student"]); assert.ok( @@ -418,7 +468,7 @@ suite("EchoCode – Root Extension Integration", () => { const harness = createVscodeHarness(); const { stubs, state } = createExtensionStubs({ isRecording: false }); const module = loadRootExtension(stubs); - const context = vscode.__createMockContext(); + const context = createConfiguredContext(); try { await module.extension.activate(context); @@ -447,15 +497,17 @@ suite("EchoCode – Root Extension Integration", () => { const harness = createVscodeHarness(); const { stubs, state } = createExtensionStubs({ isRecording: false }); const module = loadRootExtension(stubs); - const context = vscode.__createMockContext(); + const context = createConfiguredContext(); try { await module.extension.activate(context); await harness.commandRegistry.get("echocode.toggleVoice")?.(); - assert.deepEqual(state.chatProvider.recordingStates, [true]); - assert.deepEqual(state.spokenMessages.slice(-1), ["Listening"]); + assert.deepEqual(state.chatProvider.recordingStates, []); + assert.deepEqual(state.spokenMessages.slice(-1), [ + "Chat mode. Listening.", + ]); assert.equal(state.startRecordingCalls.length, 1); } finally { module.restore(); @@ -463,7 +515,7 @@ suite("EchoCode – Root Extension Integration", () => { } }); - test("toggleVoice falls back to chat when voice routing does not handle the transcript", async () => { + test("toggleVoice in chat mode sends transcript directly to chat provider", async () => { const harness = createVscodeHarness(); const { stubs, state } = createExtensionStubs({ isRecording: true, @@ -471,17 +523,17 @@ suite("EchoCode – Root Extension Integration", () => { voiceHandled: false, }); const module = loadRootExtension(stubs); - const context = vscode.__createMockContext(); + const context = createConfiguredContext(); try { await module.extension.activate(context); await harness.commandRegistry.get("echocode.toggleVoice")?.(); - assert.deepEqual(state.chatProvider.recordingStates, [false]); + assert.deepEqual(state.chatProvider.recordingStates, []); assert.ok(state.spokenMessages.includes("Processing")); assert.equal(state.stopCalls.length, 1); - assert.deepEqual(state.voiceCommands, ["Explain this code"]); + assert.deepEqual(state.voiceCommands, []); assert.deepEqual(state.chatProvider.handledMessages, [ "Explain this code", ]); diff --git a/test/speechHandler.test.ts b/test/speechHandler.test.ts index c5263d9..f22250c 100644 --- a/test/speechHandler.test.ts +++ b/test/speechHandler.test.ts @@ -2,8 +2,11 @@ import "./helpers/vscodeMock.js"; import { strict as assert } from "assert"; import { suite, test } from "mocha"; import * as path from "path"; +import { createRequire } from "module"; -const nodeRequire = require; +// createRequire rather than a bare `require`: on Node 22.18+ and 24 the test files load +// as ES modules, where `require` is not defined. See aiProviderSetup.test.ts. +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); const repoRoot = process.cwd(); const speechHandlerModulePath = nodeRequire.resolve( path.join(repoRoot, "Core/program_settings/speech_settings/speechHandler.js"), diff --git a/test/voiceCommandRouter.test.ts b/test/voiceCommandRouter.test.ts index 75ee617..c6c5f19 100644 --- a/test/voiceCommandRouter.test.ts +++ b/test/voiceCommandRouter.test.ts @@ -2,13 +2,23 @@ import "./helpers/vscodeMock.js"; import { strict as assert } from "assert"; import { suite, test } from "mocha"; import * as path from "path"; +import { createRequire } from "module"; // @ts-ignore import * as VS from "./helpers/vscodeMock.js"; const vscode: any = VS; -const fs = require("fs") as typeof import("fs"); -const nodeRequire = require; + +// Seeded from an explicit path rather than `import.meta.url`, because these files must +// load under two different runtimes: +// - Node 20/22 (CI): ts-node compiles them to CommonJS, where `import.meta` is a hard +// compile error (TS1470) but a bare `require` exists. +// - Node 24 (local): Node strips types natively and detects ESM from the `import` +// syntax, where `import.meta` is fine but `require` is not defined. +// createRequire with a plain path uses no meta-property, so it compiles and runs on +// both, and still yields the real CJS require the suite needs for require.cache mocking. +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); +const fs = nodeRequire("fs") as typeof import("fs"); const repoRoot = process.cwd(); const routerModulePath = nodeRequire.resolve( @@ -218,6 +228,67 @@ suite("EchoCode – Voice Command Router", () => { } }); + test("matches internal commands with minor STT misspellings", async () => { + const originalReadFileSync = fs.readFileSync; + const outputChannel = createOutputChannel(); + const commands = captureCommandExecution(); + const { router, restore } = loadVoiceRouter(); + + stubVoiceCommands([ + { id: "echocode.createFile", keywords: ["create file"] }, + ]); + + try { + const result = await router.tryExecuteVoiceCommand( + "pleese creat fyle", + outputChannel, + ); + + assert.deepEqual(result, { + handled: true, + command: "echocode.createFile", + }); + assert.deepEqual(commands.executed, ["echocode.createFile"]); + assert.ok(outputChannel.lines.some((line) => line.includes("score:"))); + } finally { + fs.readFileSync = originalReadFileSync; + commands.restore(); + restore(); + } + }); + + test("chooses best-fit command among similar keywords", async () => { + const originalReadFileSync = fs.readFileSync; + const outputChannel = createOutputChannel(); + const commands = captureCommandExecution(); + const { router, restore } = loadVoiceRouter(); + + stubVoiceCommands([ + { id: "echocode.createFile", keywords: ["create file", "new file"] }, + { + id: "echocode.createFolder", + keywords: ["create folder", "new folder"], + }, + ]); + + try { + const result = await router.tryExecuteVoiceCommand( + "crate new foldeer", + outputChannel, + ); + + assert.deepEqual(result, { + handled: true, + command: "echocode.createFolder", + }); + assert.deepEqual(commands.executed, ["echocode.createFolder"]); + } finally { + fs.readFileSync = originalReadFileSync; + commands.restore(); + restore(); + } + }); + test("blocks locked internal commands in student mode", async () => { const originalReadFileSync = fs.readFileSync; const spokenMessages: string[] = []; @@ -407,6 +478,57 @@ suite("EchoCode – Voice Command Router", () => { } }); + test("does not generate code in command-only mode when no command matches", async () => { + const originalReadFileSync = fs.readFileSync; + const outputChannel = createOutputChannel(); + const messages = captureWindowMessages(); + const generateCalls: any[] = []; + const originalEditor = vscode.window.activeTextEditor; + vscode.window.activeTextEditor = { + selection: { active: { line: 0, character: 0 } }, + document: { + languageId: "python", + lineCount: 1, + lineAt: () => ({ text: "pass" }), + getText: () => "pass", + }, + edit: async () => true, + }; + + const { router, restore } = loadVoiceRouter({ + aiRequest: { + generateCodeFromVoice: async (...args: any[]) => { + generateCalls.push(args); + return "print('should-not-run')"; + }, + }, + }); + + stubVoiceCommands([]); + + try { + const result = await router.tryExecuteVoiceCommand( + "make me some code", + outputChannel, + { allowCodeGeneration: false }, + ); + + assert.deepEqual(result, { handled: false }); + assert.deepEqual(generateCalls, []); + assert.deepEqual(messages.errorMessages, []); + assert.ok( + outputChannel.lines.some((line) => + line.includes("Command-only mode prevents code generation fallback"), + ), + ); + } finally { + fs.readFileSync = originalReadFileSync; + vscode.window.activeTextEditor = originalEditor; + messages.restore(); + restore(); + } + }); + test("reports generation failures without throwing", async () => { const originalReadFileSync = fs.readFileSync; const outputChannel = createOutputChannel(); diff --git a/wiki/AI-Providers-and-Models.md b/wiki/AI-Providers-and-Models.md new file mode 100644 index 0000000..0682ed5 --- /dev/null +++ b/wiki/AI-Providers-and-Models.md @@ -0,0 +1,200 @@ +# AI Providers and Models + +## For users + +Several EchoCode features need a language model: the [Chat Tutor](Chat-Tutor), [Annotations and Big-O](Annotations-and-Big-O), [Code Summaries](Code-Summaries), line explanations, and the [Assignment Tracker](Assignment-Tracker). Everything else β€” navigation, line read-out, file tools β€” works with no model at all. + +You choose the backend once. Run **EchoCode: Select AI Provider & Model** from the Command Palette at any time to change it. + +### The three options + +**GitHub Copilot** β€” no API key, no billing setup. If you already have Copilot, EchoCode borrows it through VS Code's own language-model API. Copilot has a free tier, and students get Copilot Pro free through the GitHub Student Developer Pack. The first time EchoCode sends a request, VS Code asks you to authorise EchoCode to use the model β€” one dialog, once. Requests count against your Copilot allowance. + +**Local model via Ollama** β€” runs entirely on your machine. Nothing leaves it, no account, no quota, no per-request cost. You need [Ollama](https://ollama.com) installed and running, plus disk space and RAM for a model. This is the genuinely unmetered option, and the right one if privacy matters or you are offline. + +**A hosted API** β€” OpenAI, OpenRouter, Groq, Anthropic, or any OpenAI-compatible endpoint. You supply an API key and pay the provider directly. Most flexible, and the only one that requires setting up billing. + +### Choosing a local model + +If you pick Ollama and have no models installed, EchoCode offers to recommend one. It inspects your machine β€” CPU cores, RAM, and GPU β€” and picks the largest model that will actually fit, then reads the recommendation aloud before showing the list. + +Recommendations run smallest to largest: + +| Model | Needs about | Character | +|---|---|---| +| `qwen2.5-coder:1.5b` | 2.5 GB | Runs on almost anything. Basic explanations. | +| `llama3.2:3b` | 4 GB | Small, general, very fast. | +| `qwen2.5-coder:7b` | 7 GB | Strong code understanding. Good default. | +| `qwen2.5-coder:14b` | 12 GB | Noticeably better reasoning. Needs a healthy machine. | +| `qwen2.5-coder:32b` | 24 GB | Best local quality. Workstation class. | + +Coder-tuned models are preferred at each size because EchoCode is a coding assistant. + +Sizing matters more than it looks. A model that overruns your memory does not fail loudly β€” it swaps to disk, and EchoCode just feels broken. If your machine is below the smallest entry you still get an offer, with a warning that it will be slow. + +You can override the recommendation, or type any Ollama model name. Downloads show a real progress bar and can be cancelled. + +### Setting up a hosted API + +EchoCode asks for the endpoint, then the key, then **verifies the endpoint answers before saving anything**. That means a typo'd URL or rejected key is caught immediately rather than surfacing later as a mysteriously broken feature. Once verified, you pick from the models that endpoint actually offers. + +**Your key is stored in your operating system's keychain**, never in `settings.json`. Settings files are plaintext, often committed to git, and sync between machines β€” so there is deliberately no setting for the key. Leave the key box blank when reconfiguring and EchoCode reuses the stored one. + +### What happens on later launches + +EchoCode quietly re-checks that your chosen model still exists, because model lineups change often β€” an Ollama model gets removed, a Copilot model is renamed, an API key is revoked. If your configured model has vanished it offers to pick a new one. If everything matches, it says nothing. + +The check is time-bounded so it cannot delay startup. Run **EchoCode: Check for AI Provider/Model Updates** to force it. + +### If something is wrong + +Open **View β†’ Output β†’ EchoCode**. Provider detection logs every probe and its result β€” whether Ollama answered, whether Copilot reported models, whether your API endpoint responded. + +Common causes: + +- **"Ollama is not responding"** β€” Ollama is not running, or is on a different port. The prompt offers Retry, Change Address, or switching to an API. +- **No Copilot models detected** β€” Copilot Chat is not installed, or you are not signed in. +- **API request failed** β€” the error text comes straight from the provider and usually names the real problem (bad key, wrong model, no credit). + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![AI Provider and Model Selection β€” Level 0 context diagram](diagrams/svg/11-ai-provider-selection-L0.svg) + +*Editable source: `wiki/diagrams/11-ai-provider-selection.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![AI Provider and Model Selection β€” Level 1 decomposition diagram](diagrams/svg/11-ai-provider-selection-L1.svg) + +*Editable source: `wiki/diagrams/11-ai-provider-selection.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### The files + +| File | Responsibility | +|---|---| +| `AIrequest.js` | Resolves settings, dispatches requests, the public AI surface | +| `aiProviderSetup.js` | The setup wizard, provider detection, startup re-check | +| `apiProviders.js` | Hosted-API presets and the two wire formats | +| `httpClient.js` | Dependency-free HTTP/NDJSON transport | +| `secretStore.js` | API keys via `context.secrets` | +| `systemScan.js` | Hardware probe and model recommendation | + +All under `Core/program_settings/program_settings/`. + +### The request path + +Features do not talk to backends. They call one of the functions in `AIrequest.js`: + +``` +analyzeAI(code, instructionPrompt) β€” annotations, Big-O, task scanning +requestTextFromMessages(messages, opts) β€” chat, summaries +generateCodeFromVoice(transcript, context) β€” voice-to-code +classifyVoiceIntent(transcript, commands) β€” voice command matching +``` + +Each funnels into `dispatchMessages()`, which reads the resolved provider and routes to exactly one of three paths: + +- **`copilot`** β†’ `sendCopilotMessages()` β†’ `vscode.lm` via `selectModel()` +- **`ollama`** β†’ `sendOllamaPrompt()` β†’ `POST /api/generate` through `httpClient` +- **`api`** β†’ `sendApiChat()` in `apiProviders.js` + +**Add a backend in `dispatchMessages`, not in a feature.** Every feature inherits it for free. + +### Two wire formats + +`apiProviders.js` abstracts the one real difference between hosted APIs: + +```js +buildOpenAiBody(messages, model, opts) // messages array, optional max_tokens +buildAnthropicBody(messages, model, opts) // system as a top-level field, max_tokens required +``` + +Anthropic takes the system prompt as its own field rather than a message role and requires an explicit `max_tokens`, so its body is built separately. Each preset declares `wire: "openai" | "anthropic"`. A new OpenAI-compatible provider is a preset entry and nothing more. + +### Model selection is two different problems + +```js +listCopilotModels(opts) // for menus β€” capped at 3s, throws on timeout +selectModel() // for requests β€” deliberately uncapped +``` + +`vscode.lm.selectChatModels()` does not read a cached list. It waits for Copilot Chat to activate, sign in, and fetch its catalog β€” unbounded, and routinely tens of seconds on a cold window. It is cold for exactly the users who never touch Copilot, because warming is skipped unless Copilot is the selected backend. + +So the menu path is capped and treats "too slow" as "not installed", while the request path is uncapped because there the model *is* the work. **If you unify these, the wizard hangs again.** + +`selectModel()` prefers, in order: the user's persisted `copilotModel`, then a `gpt-4` family match, then the first available. That `gpt-4` preference is why a Copilot user may get GPT-4 rather than Claude even though Copilot offers both. + +### Provider detection runs concurrently + +`detectProviders()` probes all three backends under `Promise.all`, so the caller waits for the slowest rather than the sum: + +| Probe | Cap | +|---|---| +| Copilot (`listCopilotModels`) | 3s | +| Ollama (`/api/tags`) | 2s | +| Hosted API (`/models`) | 5s | + +Each probe swallows its own failure and yields an empty list, so a missing extension or a stopped server degrades to "not available" rather than breaking setup. + +Worst case for the whole phase is ~3s. It was previously the sum of three sequential probes with an unbounded Copilot call at the front, which is what made the picker take up to a minute with nothing rendered. + +### The setup wizard is a small state machine + +`pickProviderAndModel()` runs a loop over a top-level API-or-Local fork. Each branch can return the sentinel `BACK` to hand control back, which is what makes "Ollama isn't installed" recoverable without restarting setup. `MAX_BRANCH_HOPS` (12) stops a user bouncing between branches forever. + +### Spoken quick picks + +`showQuickPick` cannot report which row is highlighted β€” it resolves only once something is chosen. So the wizard uses `createQuickPick`, whose `onDidChangeActive` fires per keystroke, wrapped in `showAnnouncedQuickPick()`: + +```js +class AnnouncedList { + moveTo(item) // returns the sentence to speak, or null if nothing changed + describe() // "qwen2.5-coder:7b. Recommended for this machine. 2 of 5." +} +``` + +Three details that are easy to get wrong: + +- **Codicons are stripped before speaking.** `$(star-full) qwen2.5-coder:7b` would otherwise be read as "dollar sign paren star full". +- **Listeners subscribe before `items` is assigned**, because assigning items makes the first row active and that is the announcement the user needs on open. +- **Hosts without `createQuickPick` fall back** to `showQuickPick` with only the intro spoken. The test harness takes this path. + +Position is counted against the full list. VS Code exposes no public view of the type-to-filter result, so while filtering the count includes rows the user cannot see. + +### Speech does not block the UI + +`speak()` in `aiProviderSetup.js` is fire-and-forget. `speakMessage` resolves only when the sentence has finished being read aloud β€” measured at ~5s for a short line β€” and every call site here speaks just before opening a quick pick. Awaiting it held the UI closed for the full utterance at every step. See [Speech Handler](Speech-Handler) for why interruption is safe. + +### Hardware scan + +`systemScan.js` never throws. Every probe resolves to `""` on failure β€” a missing tool must not block setup. + +```js +memoryBudgetGb(specs) // vram >= 4 ? max(vram, ram*0.55*0.75) : ram*0.55 +``` + +GPU detection is deliberately asymmetric. NVIDIA is the only vendor exposing a reliable number from a driver-bundled CLI (`nvidia-smi`), so it is the only one trusted for sizing. On Apple Silicon, CPU and GPU share one pool, so total RAM is the budget and VRAM is not double-counted. On Windows, `Win32_VideoController.AdapterRAM` is capped at 4GB and wrong on modern cards, so the name is taken for display only and RAM drives the recommendation. + +Each probe has a 4s timeout. On Windows the PowerShell `Get-CimInstance` call routinely uses most of it β€” PowerShell cold start alone is 1–2s. + +### HTTP transport + +`httpClient.js` is dependency-free on purpose: the extension ships as a VSIX and Node's built-ins are all it needs. + +```js +requestJson(url, body, opts) // buffered +requestNdjson(url, body, opts) // line-delimited, for Ollama's /api/pull progress +``` + +One non-obvious rule in `requestNdjson`: a throw from `onEvent` aborts the request. Ollama returns **HTTP 200 with an `{"error": …}` line** for an unknown model, so swallowing that would report success on a failed download. + +`describeError()` extracts the provider's own message from its JSON envelope β€” the difference between "invalid api key" and a wall of HTML. diff --git a/wiki/Annotations-and-Big-O.md b/wiki/Annotations-and-Big-O.md new file mode 100644 index 0000000..9ad53f6 --- /dev/null +++ b/wiki/Annotations-and-Big-O.md @@ -0,0 +1,159 @@ +# Annotations and Big-O + +## For users + +Two related review features. **Annotations** flag general problems in your code; **Big-O analysis** flags performance problems specifically. Both attach notes to individual lines and read them aloud one at a time. + +**Dev mode only.** Both are locked in Student mode. + +### Annotations + +**Ctrl+Alt+A** toggles annotations for the current file. EchoCode reviews it and attaches a note to each line worth commenting on. + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+A** | Toggle annotations | +| **Ctrl+Alt+S** | Speak the next annotation | +| **Ctrl+Alt+Q** | Read all annotations | + +The design point is stepping. Rather than dumping every issue at once, **Ctrl+Alt+S** walks you through them one line at a time so you can act on each before hearing the next. + +### Big-O analysis + +**Ctrl+Alt+N** analyses the current file's algorithmic complexity β€” nested loops, repeated scans, and similar patterns that make code slow as input grows. + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+N** | Analyse Big-O | +| **Ctrl+Alt+B** | Read next recommendation | +| **Ctrl+Alt+H** | Read all recommendations | + +Findings go into a queue. **Ctrl+Alt+B** advances through it. An audio cue plays when the queue is empty, so you know you have reached the end without being told in words. + +### Fast findings arrive without a model + +Both features check a set of built-in rules first, and those produce results immediately with no AI call. The AI pass adds deeper analysis on top. So you get useful output even with no model configured or while offline β€” just less of it. + +### How verbose the notes are + +Annotation wording follows your [guidance level](Modes-and-Guidance): + +- **guided** β€” a coaching tone with next steps +- **balanced** β€” the finding, why it matters, one step +- **concise** β€” location and one sentence + +Change it with **Ctrl+Alt+Z**. + +### What you see on screen + +Findings are also attached to lines as editor decorations, so a sighted collaborator can see the same notes you are hearing. Toggling annotations off clears them. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Annotations and Big-O β€” Level 0 context diagram](diagrams/svg/03-annotations-bigo-L0.svg) + +*Editable source: `wiki/diagrams/03-annotations-bigo.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Annotations and Big-O β€” Level 1 decomposition diagram](diagrams/svg/03-annotations-bigo-L1.svg) + +*Editable source: `wiki/diagrams/03-annotations-bigo.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Responsibility | +|---|---| +| `program_features/Annotations_BigO/annotations.js` | General annotations (~424 lines) | +| `program_features/Annotations_BigO/bigOAnalysis.js` | Complexity analysis (~439 lines) | +| `program_features/Annotations_BigO/queue_system.js` | The `Queue` class with an audio cue | + +### The shared shape + +Both follow the same pipeline: + +``` +1. Local rules pass β€” checkLocalRules / checkLocalBigORules (instant, no AI) +2. AI pass β€” analyzeAI() with a feature-specific prompt +3. Parse response β€” extract structured findings from a text stream +4. Apply decorations β€” applyDecoration(editor, line, text) +5. Enqueue β€” for stepped read-out +``` + +Step 1 existing separately is deliberate: it makes the feature useful with no model configured, and gives instant feedback before the network round trip returns. + +### Parsing a stream of JSON objects + +`annotations.js` has `extractJsonObjectsFromStream(streamText)` rather than a plain `JSON.parse`. Models emit findings as a sequence of objects, sometimes wrapped in prose or fenced code, sometimes truncated. This scans for balanced object boundaries and yields whatever parses, so a malformed tail does not discard the good findings before it. + +`bigOAnalysis.js` has `cleanResponse(rawResponse)` for the same class of problem β€” stripping fences and commentary before parsing. + +**This is the fragile seam.** If annotations stop appearing after a model or prompt change, look here first: the request probably succeeded and the parse dropped everything. + +### The queue and its audio cue + +```js +class Queue { + enqueue(el) / dequeue() / peek() / isEmpty() / size() / clear() + async playSound() +} +``` + +`playSound()` (via `sound-play`, from `audio_pings/`) signals an empty queue non-verbally. A spoken "no more annotations" every time is far more tiring than a short tone β€” worth keeping in mind before replacing it with speech. + +`iterateBigOQueue()` dequeues and speaks one item; `readEntireBigOQueue()` drains the whole queue sequentially. Because speech interrupts rather than queues, draining relies on awaiting each utterance β€” see [Speech Handler](Speech-Handler). + +### Decorations + +`applyDecoration(editor, line, suggestionText)` builds a `TextEditorDecorationType` and applies it at a line range. `clearDecorations()` disposes them on toggle-off. + +Decoration types are VS Code resources and must be disposed, not just cleared β€” leaking them accumulates render overhead across toggles. + +### Loop detection + +`bigOAnalysis.js` does structural analysis before involving a model: + +```js +detectLoops(document) // find loop constructs +analyzeLoops(editor, loops, collectedIssues) // nesting depth β†’ complexity +addBigOIssue(editor, line, suggestion, collectedIssues) +finalizeQueue(issues) +``` + +This is text/structure based, not a real parser, so it is a heuristic β€” good at nested `for` loops, weaker on complexity hidden behind function calls. + +### Prompt construction + +`annotations.js` builds its prompt from the guidance level plus the file body: + +```js +buildAnnotationPrompt() // guidance-aware instruction text +getEntireFileWithLineNumbers(textEditor) // numbered source, so the model can cite lines +loadAnnotationSettings() // reads per-feature settings from disk if present +``` + +Sending line numbers is what lets findings map back to decorations. `bigOAnalysis.js` uses a fixed `ANNOTATION_PROMPT` constant instead. + +### Registration + +```js +registerAnnotationCommands(context, outputChannel) // annotations.js +registerBigOCommand(context) // bigOAnalysis.js +``` + +`extension.js` calls both under the `annotationsBigO` feature key and combines their disposables with `vscode.Disposable.from(...)`. + +Note the command ids for Big-O use the `code-tutor.` prefix, not `echocode.` β€” `code-tutor.analyzeBigO`, `code-tutor.iterateBigOQueue`, `code-tutor.readEntireBigOQueue`. A leftover from an earlier name. `guard.js` locks both prefixes in Student mode, including some stale `code-tutor.*` ids that no longer exist. + +### Swappable + +Both live under the `annotationsBigO` feature key, so the whole subsystem can be replaced via `echocode.featureImplementation.annotationsBigO`. See [Custom Feature Implementations](Custom-Feature-Implementations). diff --git a/wiki/Architecture.md b/wiki/Architecture.md new file mode 100644 index 0000000..5522918 --- /dev/null +++ b/wiki/Architecture.md @@ -0,0 +1,166 @@ +# Architecture + +> **Developer page.** Nothing here is user-facing. + +## The shape of the thing + +EchoCode is a single-process VS Code extension, roughly 10,200 lines of JavaScript (not TypeScript β€” see below). It has three layers: + +``` +extension.js Wiring: activation, command registration, dispatch + β”œβ”€β”€ program_features/ User-facing features, swappable at runtime + β”œβ”€β”€ navigation_features/ Cursor/file/folder movement (not swappable) + └── Core/ Shared services: AI routing, speech, mode, guidance +``` + +`extension.js` is the only module that knows about all the others. Features do not import each other β€” with two documented exceptions noted below. + +## Why JavaScript with a TypeScript build + +`package.json` sets `"main": "./extension.js"` β€” the runtime entry point is plain CommonJS JavaScript. But there is a `tsconfig.json`, a `src/` directory, and an `out/` directory. + +The TypeScript build exists **only for the test suite**. `tsconfig.json` has `"rootDir": "src"` and `"exclude": ["test", …]`, and `.vscodeignore` excludes `src/`, `out/`, and `tsconfig.json` from the VSIX. Nothing at runtime requires `out/`. + +So: `npm run compile` passing tells you the TS scaffold is intact, not that the extension works. The extension is never type-checked. + +## Activation + +Activation events are declared in `package.json`. The important one is `onStartupFinished` β€” EchoCode waits until VS Code has finished loading rather than delaying startup. The rest are `onCommand:` entries, which let VS Code activate lazily if a command fires first. + +`activate(context)` in rough order: + +1. Create the `EchoCode` output channel +2. `loadFeatureImplementations(outputChannel)` β€” load every feature module through the loader +3. Register commands, grouped by feature key +4. `initSecretStorage(context)` +5. Chain the Copilot warm-up into `initializeAIProviderOnStartup` β€” **not awaited** +6. Kick off the voice dependency bootstrap β€” **not awaited** +7. `initExternalCommandRegistry(context)` then `buildExternalCommandRegistry()` β€” **not awaited** +8. `refreshModeContext()` β€” set the mode context keys +9. Register file watchers and the configuration-change listener + +### What is deliberately not awaited, and why + +Three things at activation can take a long time, and each used to hold up the whole extension: + +| Work | Why it is slow | +|---|---| +| AI provider check | A cold `vscode.lm` call waits for Copilot to activate and sign in | +| Voice dependency bootstrap | Can pip-install several hundred MB | +| External command registry | Enumerates ~3300 commands, writes 620KB | + +All three are fire-and-forget with a `.catch()` that logs. Consumers resolve what they need later β€” voice commands read the Python path from `globalState` when they actually run. + +**Do not await these.** The AI provider path in particular was measured at up to a minute with nothing rendered, which reads as a frozen extension. + +### The Copilot warm-up is conditional + +```js +const copilotWarmup = + getAiSettings().provider === "copilot" + ? tryEnsureCopilotActivated(outputChannel) + : Promise.resolve(null); + +copilotWarmup.then(() => initializeAIProviderOnStartup(context, outputChannel)) +``` + +Ollama and hosted-API users should not pay to activate an extension they never call. The chain exists because the startup check probes Copilot through `vscode.lm`, which reports no models until Copilot is live. + +The trade-off: for non-Copilot users, `detectProviders` then hits `selectChatModels` cold. That is why `listCopilotModels` has its own 3s cap β€” see [AI Providers and Models](AI-Providers-and-Models). + +`tryEnsureCopilotActivated` races activation against a 5s timeout. Warming is only ever an optimisation and must never block. + +## The request path + +Every AI-backed feature goes through one funnel: + +``` +feature + β†’ AIrequest.js (analyzeAI / requestTextFromMessages / generateCodeFromVoice / classifyVoiceIntent) + β†’ dispatchMessages() + β†’ copilot | ollama | api +``` + +Features never know which backend answered. **Add a backend in `dispatchMessages`**, not in a feature. + +## Shared services under Core/ + +| Service | Module | Notes | +|---|---|---| +| AI routing | `program_settings/AIrequest.js` | The funnel above | +| Provider setup | `program_settings/aiProviderSetup.js` | Wizard, detection, startup re-check | +| HTTP | `program_settings/httpClient.js` | Dependency-free; JSON and NDJSON | +| Secrets | `program_settings/secretStore.js` | `context.secrets`, never settings | +| Hardware scan | `program_settings/systemScan.js` | Model recommendation | +| Speech | `speech_settings/speechHandler.js` | See [Speech Handler](Speech-Handler) | +| Mode | `mode.js`, `guard.js`, `modeAudio.js` | See [Modes and Guidance](Modes-and-Guidance) | +| Guidance | `guide_settings/guidanceLevel.js` | Verbosity formatting | +| Summaries | `Summarizer/` | Not swappable | + +## Cross-feature coupling + +Two places where features reach across, both deliberate and both worth knowing: + +**`Chat_Tutor.js` requires `extension.js` back**, lazily inside a handler: + +```js +const { tryExecuteVoiceCommand } = require("../../extension"); +``` + +A circular dependency that works only because the require is deferred until after `extension.js` has finished evaluating. Hoisting it to module scope gets `undefined`. + +**File/folder creation imports the navigation cursor:** + +```js +const { getCurrentFolder } = require(".../folder_navigator"); +``` + +So creation follows the navigation cursor rather than the Explorer selection. See [File and Folder Tools](File-and-Folder-Tools). + +## Hot reload + +`extension.js` watches for changes and rebuilds features in place: + +```js +const watcher = vscode.workspace.createFileSystemWatcher( + `**/program_features/${featureFolder}/UserImplementation/**`, +); +``` + +plus a configuration listener on the eight `featureImplementation.*` keys. Both route into `scheduleFeatureReload(featureKey, reason)`, debounced 150ms, which calls `reloadFeatureImplementation` β†’ `clearFeatureRequireCache(featureFolder)` β†’ `loadFeatureImplementations()`. + +Two consequences that have caused real bugs: + +- **A reload constructs fresh feature objects.** Anything holding per-instance state gets a new one. The voice `DependencyManager` needs a *module-level* memo for exactly this reason β€” see [Voice Input](Voice-Input). +- **The watcher is workspace-relative.** If you are developing EchoCode itself, editing files in your own checkout triggers reloads. + +`setFeatureDisposable(featureKey, disposable)` disposes the previous registration before storing the new one, so a reload does not leave duplicate command handlers. + +## Resource disposal + +Everything registered goes into `context.subscriptions`. A clean activation registers ~53. + +Two things are easy to leak and have been: + +- **File watchers.** `fs.watch` handles must be `close()`d and tied to the extension lifetime. `ExternalIntentRouter` registers a `vscode.Disposable` for its watcher in `initExternalCommandRegistry`. +- **Decoration types.** `TextEditorDecorationType` must be disposed, not just cleared. See [Annotations](Annotations-and-Big-O). + +## Where generated data lives + +| Data | Location | Why | +|---|---|---| +| External command registry | `context.globalStorageUri` | Per-user, survives updates, regenerable | +| Voice venv (`echo_venv`) | `context.globalStorageUri` | Same | +| API keys | `context.secrets` (OS keychain) | Never plaintext | +| Settings | VS Code settings | User-editable | +| Python interpreter path | `context.globalState` | Cheap lookup for voice commands | + +**Never write generated data next to the source.** VS Code replaces the install directory wholesale on every extension update, so anything there is silently discarded β€” and the directory is not guaranteed writable on managed installs. + +## Reading the code in a sensible order + +1. `package.json` β€” `contributes` tells you the whole user-facing surface +2. `extension.js` `activate()` β€” the wiring +3. `program_features/featureImplementationLoader.js` β€” how modules load +4. `Core/program_settings/program_settings/AIrequest.js` β€” the AI funnel +5. Any one feature module β€” they all follow the same shape diff --git a/wiki/Assignment-Tracker.md b/wiki/Assignment-Tracker.md new file mode 100644 index 0000000..b261f4a --- /dev/null +++ b/wiki/Assignment-Tracker.md @@ -0,0 +1,125 @@ +# Assignment Tracker + +## For users + +Load an assignment document, and EchoCode turns it into a spoken task list it can track against the code you write. + +**Dev mode only.** Locked in Student mode β€” which is worth noting, since students are the obvious audience. See [Modes and Guidance](Modes-and-Guidance). + +### Loading an assignment + +**Ctrl+Alt+O** opens a file picker. EchoCode reads the document, extracts the individual tasks, and tells you how many it found. + +Word documents and PDFs work as well as plain text β€” the extension bundles readers for both. + +### Working through tasks + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+T** | Read the next task | +| **Ctrl+Alt+/** | Read the next *incomplete* task | +| **Ctrl+Alt+M** | Mark the current task complete | +| **Ctrl+Alt+Y** | Rescan your code for completed tasks | + +**Ctrl+Alt+T** steps through every task in order. **Ctrl+Alt+/** skips ones already done, which is what you want when returning to work after a break. + +### Automatic progress detection + +**Ctrl+Alt+Y** is the interesting one. EchoCode reads your current code, compares it against the task list, and marks off the tasks it believes you have completed. You hear a summary of what it found. + +Treat it as a helpful guess, not a grade. It is a language model comparing prose requirements against code, so it can miss a task you finished unusually or credit one you only partly did. **Ctrl+Alt+M** is the manual override. + +### Requirements and limits + +Needs an AI backend for task extraction and rescanning β€” see [AI Providers and Models](AI-Providers-and-Models). + +The task list lives in memory for the session. Reloading the window clears it, and you load the assignment again. Progress is not saved to disk. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Assignment Tracker β€” Level 0 context diagram](diagrams/svg/10-assignment-tracker-L0.svg) + +*Editable source: `wiki/diagrams/10-assignment-tracker.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Assignment Tracker β€” Level 1 decomposition diagram](diagrams/svg/10-assignment-tracker-L1.svg) + +*Editable source: `wiki/diagrams/10-assignment-tracker.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### File + +`program_features/Assignment_Tracker/assignmentTracker.js` β€” ~289 lines, under the `assignmentTracker` feature key. + +### State + +Module-level, and deliberately simple: + +```js +taskList // extracted task strings +currentTaskIndex // cursor for readNextTask +// completion tracked per task +function resetTasks() // called on every load +``` + +Nothing persists. A reload clears everything, which is the main limitation and the obvious improvement: `context.globalState` or `workspaceState` keyed by assignment path would survive reloads. + +### Loading and extraction + +```js +async function loadAssignmentFile() +async function parseTasksWithAI(text) +function parseTasksFromText(text) +``` + +`loadAssignmentFile` shows a file picker, extracts text, and hands it to extraction. Document readers come from bundled dependencies β€” `mammoth` for `.docx`, `pdf-parse` for PDFs β€” which is why they must not be excluded from the VSIX. + +**Two extraction paths.** `parseTasksWithAI` asks the model to identify discrete tasks; `parseTasksFromText` does rule-based splitting. The rule-based path means loading an assignment does something useful with no model configured, and covers the case where the AI call fails. + +`resetTasks()` is called first, so loading a second assignment replaces rather than appends. + +### Reading tasks + +```js +function readNextTask() // sequential, includes completed +function readNextSequentialTask() // skips completed +function markTaskComplete() +``` + +The two read functions are separately bound (**Ctrl+Alt+T** and **Ctrl+Alt+/**) because both behaviours are genuinely wanted β€” review everything, or continue where you left off. Their names are easy to confuse: `readNextTask` is the *unfiltered* one despite "sequential" appearing in the other's name. + +### Rescanning + +```js +async function rescanUserCode() +function updateCompletedTasksFromAI(responseText) +``` + +`rescanUserCode` sends the current code and the task list to `analyzeAI()`, then `updateCompletedTasksFromAI` parses the reply and flips completion flags. + +That parse is the fragile part, as with [annotations](Annotations-and-Big-O): the model returns prose or loosely-structured text and the parser has to find task identifiers in it. If rescanning stops detecting anything after a model change, the request probably succeeded and the parse found nothing. Log the raw response before assuming the AI call failed. + +Because the AI decides completion, results are **non-deterministic** β€” the same code and tasks can produce different answers across runs. `markTaskComplete()` exists as the deterministic override and should stay. + +### Registration + +```js +registerAssignmentTrackerCommands(context) +``` + +Registers all five commands, each wrapped in `guard()`. All five are in `STUDENT_LOCKED_COMMANDS`. + +### Worthwhile improvements + +- **Persist progress** in `workspaceState` keyed by assignment path. The single highest-value change. +- **Make rescan incremental** β€” it currently re-evaluates every task on every scan, which costs a full AI round trip over the whole task list. +- **Report confidence** so the user can distinguish a confident match from a guess. diff --git a/wiki/Chat-Tutor.md b/wiki/Chat-Tutor.md new file mode 100644 index 0000000..27df919 --- /dev/null +++ b/wiki/Chat-Tutor.md @@ -0,0 +1,125 @@ +# Chat Tutor + +## For users + +A conversational coding tutor that lives in the VS Code sidebar. Ask it a question about your code and it answers in speech and text. + +**Dev mode only.** Locked in Student mode. + +### Opening it + +- **Ctrl+Alt+C** with focus in the editor, or +- Click the EchoCode icon in the Activity Bar, or +- Command Palette β†’ **Open EchoCode Tutor Chat** + +### Using it + +Type a question and press Enter. The reply appears in the panel and is read aloud. + +You can also talk to it. Either use the panel's own voice button, or set the voice mode to **Chat Tutor** (**Ctrl+Alt+'**) and press **Ctrl+Alt+Space** β€” your speech becomes the message. See [Voice Input](Voice-Input). + +### What it is tuned for + +The tutor is deliberately brief. It is instructed to keep answers under four sentences, use plain text with no markdown, explain simply, give small examples, and offer a follow-up tip when useful. + +That is a speech-first choice. Long markdown-heavy answers are miserable to listen to β€” code fences, bullet characters, and headings all get read out literally or mangled. Four plain sentences works aloud. + +If code is open, the tutor focuses on it. Otherwise it answers generally. + +### It remembers the conversation + +Follow-up questions work β€” the tutor keeps the exchange in memory, so "why?" after an answer refers to that answer. History lasts as long as the panel does; closing the sidebar or reloading the window clears it. + +### Requirements + +Needs an AI backend. If you have not set one up, see [AI Providers and Models](AI-Providers-and-Models). Errors are reported in the panel and in **View β†’ Output β†’ EchoCode**. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Chat Tutor β€” Level 0 context diagram](diagrams/svg/02-chat-tutor-L0.svg) + +*Editable source: `wiki/diagrams/02-chat-tutor.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Chat Tutor β€” Level 1 decomposition diagram](diagrams/svg/02-chat-tutor-L1.svg) + +*Editable source: `wiki/diagrams/02-chat-tutor.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Responsibility | +|---|---| +| `program_features/ChatBot/Chat_Tutor.js` | The webview provider and message handling | +| `media/chat.js` | Webview-side script (~191 lines) | +| `media/` | Stylesheet and assets | + +Declared in `package.json` as a webview view inside EchoCode's own Activity Bar container: + +```json +"viewsContainers": { "activitybar": [{ "id": "echocode", "title": "EchoCode", "icon": "images/icon.png" }] }, +"views": { "echocode": [{ "id": "echocode.chatView", "name": "EchoCode Tutor", "type": "webview" }] } +``` + +### The provider + +`EchoCodeChatViewProvider` implements `resolveWebviewView`. State it holds: + +```js +this._view // the WebviewView +this._currentWebview // nulled on dispose, so posts after teardown are skipped +this._isListening // recording state, for the panel's voice button +this.conversationHistory // [{ user, response }] +``` + +`enableScripts: true` with `localResourceRoots: [this.context.extensionUri]` β€” scripts run, but only assets from the extension directory load. + +### Message protocol + +Extension ← webview, via `onDidReceiveMessage`: + +| `message.type` | Meaning | +|---|---| +| `userInput` | Typed text; `message.text` carries it | +| `executeVoiceCommand` | The panel's voice button was pressed | + +Extension β†’ webview, via `postMessage`: response payloads plus recording-state signals such as `voiceStopping`, which lets the panel show that transcription is in progress. + +### A circular require to be aware of + +The voice path in the provider does: + +```js +const { tryExecuteVoiceCommand } = require("../../extension"); +``` + +That is a **circular dependency** β€” `extension.js` loads `Chat_Tutor.js`, which requires `extension.js` back. It works because the require happens lazily inside the handler rather than at module load, by which point `extension.js` has finished evaluating and populated its exports. Hoisting it to the top of the file would get a partially-initialised module and `tryExecuteVoiceCommand` would be `undefined`. + +If you refactor this, the clean fix is passing the router in through the constructor rather than reaching back for it. + +### The system prompt + +`BASE_PROMPT` is a module constant. The four-sentence, no-markdown constraint is load-bearing for speech output, not a stylistic preference β€” see the user section above. Prepended to the conversation on every request. + +### Request path + +`handleUserMessage(text)` assembles `BASE_PROMPT` plus history plus the new turn and calls `requestTextFromMessages()` from `AIrequest.js`. The provider does not know which backend answers β€” see [AI Providers and Models](AI-Providers-and-Models). + +Replies are posted to the webview and spoken through `speakMessage`. Because speech interrupts rather than queues, a new reply cuts off the previous one; see [Speech Handler](Speech-Handler). + +### Webview security + +`getNonce()` generates a per-load nonce for the Content Security Policy, so only EchoCode's own inline script executes. Keep that if you edit the HTML β€” dropping the nonce opens the panel to arbitrary script injection from anything that reaches the rendered content. + +### Registration + +`registerChatCommands(context, outputChannel)` registers `echocode.openChat` and returns the provider instance, which `extension.js` keeps as `chatProvider` so voice handlers can push transcripts straight into it via `chatProvider.handleUserMessage(text)` without going through the command layer. diff --git a/wiki/Code-Navigation.md b/wiki/Code-Navigation.md new file mode 100644 index 0000000..223b41a --- /dev/null +++ b/wiki/Code-Navigation.md @@ -0,0 +1,146 @@ +# Code Navigation + +## For users + +Moving around code without scrolling. All of these work in Student mode. + +### Jump between functions + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+Down** | Jump to next function | +| **Ctrl+Alt+Up** | Jump to previous function | + +The cursor moves to the next or previous definition and its name is announced. This is the fastest way to traverse an unfamiliar file β€” jump, listen to the name, jump again. + +Pairs naturally with **Ctrl+Alt+E F** to summarise each one as you land on it. See [Code Summaries](Code-Summaries). + +Jumping covers functions, methods, classes, and similar top-level constructs β€” not every variable. + +### Where am I? β€” Ctrl+Alt+E W + +Describes your cursor's position in context: the line number, and the construct you are inside. + +This is the orientation command. After a jump, a search, or coming back from another window, it tells you where you actually are. A two-step chord: **Ctrl+Alt+E**, then **W**. + +### Move between files + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+P** | Navigate to next file | +| **Ctrl+Alt+[** | Move to next folder | +| **Ctrl+Alt+]** | Move to previous folder | + +Folder navigation walks your workspace's folder list, announcing each folder as you arrive. File navigation steps through files within the current folder. + +The folder list is built when the workspace opens and kept current as you add or remove folders, so newly created directories appear without a reload. + +### Caveats worth knowing + +Function jumping depends on VS Code understanding the language, which means the relevant language extension must be installed. In Python with Pylance it works well. In a language with no extension installed, there is nothing to jump between and EchoCode says so. + +There is a **Navigate Files in Current Folder** entry in the Command Palette that does not work β€” see [Known Gaps](Known-Gaps). Use **Ctrl+Alt+P**. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Code Navigation β€” Level 0 context diagram](diagrams/svg/07-code-navigation-L0.svg) + +*Editable source: `wiki/diagrams/07-code-navigation.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Code Navigation β€” Level 1 decomposition diagram](diagrams/svg/07-code-navigation-L1.svg) + +*Editable source: `wiki/diagrams/07-code-navigation.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Responsibility | +|---|---| +| `navigation_features/navigationHandler.js` | Function/symbol jumping (~152 lines) | +| `navigation_features/whereAmI.js` | Cursor position description (~69 lines) | +| `navigation_features/Folder_File_Navigator/folder_navigator.js` | Folder traversal (~219 lines) | +| `navigation_features/Folder_File_Navigator/file_navigator.js` | File traversal (~86 lines) | +| `navigation_features/Folder_File_Navigator/workspace_folder_watcher.js` | Keeps the folder list fresh (~33 lines) | + +These are **not** under `program_features/`, so none of them go through the feature-implementation loader. They cannot be swapped via settings, and `extension.js` calls their registrars directly: + +```js +registerWhereAmICommand(context); +registerMoveCursor(context); +registerFileNavigatorCommand(context); +registerFolderNavigatorCommands(context); +``` + +### Symbol jumping + +Like the summarizer, this delegates language understanding to VS Code rather than parsing: + +```js +async function getJumpTargets(document) // executeDocumentSymbolProvider +function flattenSymbols(symbols, parent) // DocumentSymbol tree β†’ flat list +function isJumpableKind(kind) // filter to interesting SymbolKinds +function prettyName(sym) // spoken label +function getPositionFromRange(document, range) +async function moveCursorToSymbol(direction) +``` + +The pipeline: ask the symbol provider, flatten the nested tree, filter by kind, sort by position, then find the nearest target in the requested direction. + +`flattenSymbols` carries a `parent` array as it descends, which is what lets `prettyName` produce a qualified label like `ClassName.methodName` rather than a bare `methodName`. + +`isJumpableKind` is the tuning knob. Including every `SymbolKind` would make jumping land on variables and constants, which is too fine-grained to be useful aurally. If jumping feels wrong for a language, this filter is the first place to look. + +### Where am I + +```js +async function describeCursorPosition(editor) +``` + +Combines line number with the enclosing symbol, using the same provider. It is the natural complement to `Ctrl+Alt+L`, which reads trimmed line text and therefore loses indentation and context. + +### Folder navigation state + +```js +async function initializeFolderList() // build the ordered list +async function moveToNextFolder() +async function moveToPreviousFolder() +async function watchFolderForChanges() +function getCurrentFolder() // exported for other features +``` + +The list and the current index are **module-level state**, so they reset on reload. `initializeFolderList` is called during activation; `echocode.initializeFolderList` exists as a command to rebuild it manually (its keybinding entry has an empty `key`, so it is Palette-only). + +`getCurrentFolder()` is exported because other features need the navigation cursor β€” [File and Folder Tools](File-and-Folder-Tools) creates new files relative to it. + +`workspace_folder_watcher.js` and `watchFolderForChanges` keep the list current. Both use `fs.existsSync`/`lstatSync` to confirm a path is still a directory before including it, so a deleted folder does not leave a dead entry. + +### File navigation + +```js +async function navigateToNextFile() +function watchWorkspaceForFileChanges() +``` + +Steps through files in the current folder, also with module-level index state. + +### The unregistered command + +`echocode.navigateFilesInCurrentFolder` is declared in `package.json` but **registered nowhere in the source**. It appears in the Command Palette and errors when invoked. `file_navigator.js` exports only `registerFileNavigatorCommand`, which registers `echocode.navigateToNextFile`. + +Either implement it or remove the manifest entry. See [Known Gaps](Known-Gaps). + +### Adding a navigation feature + +If it is language-aware, use `executeDocumentSymbolProvider` rather than parsing β€” you inherit every language the user has support for, and stay correct as those extensions improve. + +Keep in mind the symbol provider is **async and can be slow** on a cold language server or a large file. Do not call it on a high-frequency event such as cursor movement without debouncing. diff --git a/wiki/Code-Summaries.md b/wiki/Code-Summaries.md new file mode 100644 index 0000000..e480b6f --- /dev/null +++ b/wiki/Code-Summaries.md @@ -0,0 +1,119 @@ +# Code Summaries + +## For users + +Get a spoken plain-language summary of a function, a class, or an entire file. This is the fastest way to orient yourself in unfamiliar code without reading it line by line. + +**Dev mode only.** Locked in Student mode. + +| Shortcut | Summarises | +|---|---| +| **Ctrl+Alt+E** then **F** | The function your cursor is in | +| **Ctrl+Alt+E** then **C** | The class your cursor is in | +| **Ctrl+Alt+E** then **P** | The whole file | + +These are two-step chords: press and release **Ctrl+Alt+E**, then the letter. Pause too long and VS Code cancels it. + +### What you get + +A short spoken description of what the code does. Function and class summaries scope to the construct containing your cursor, so you do not need to select anything β€” put the cursor anywhere inside and ask. + +If the cursor is not inside a function when you ask for a function summary, EchoCode tells you so rather than guessing. + +### Suggested workflow + +Opening a file you have never seen: + +1. **Ctrl+Alt+E P** β€” what is this file for? +2. **Ctrl+Alt+Down** β€” jump to the first function ([Code Navigation](Code-Navigation)) +3. **Ctrl+Alt+E F** β€” what does this one do? +4. Repeat 2 and 3 to walk the file + +### Requirements + +Needs an AI backend β€” see [AI Providers and Models](AI-Providers-and-Models). Summary length and tone follow your [guidance level](Modes-and-Guidance). + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Code Summaries β€” Level 0 context diagram](diagrams/svg/04-code-summaries-L0.svg) + +*Editable source: `wiki/diagrams/04-code-summaries.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Code Summaries β€” Level 1 decomposition diagram](diagrams/svg/04-code-summaries-L1.svg) + +*Editable source: `wiki/diagrams/04-code-summaries.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Responsibility | +|---|---| +| `Core/Summarizer/summaryGenerator.js` | The three commands and their prompts (~145 lines) | +| `Core/Summarizer/codeParser.js` | Range/selection helpers (~77 lines) | + +### The three entry points + +```js +async function summarizeClass(editor) +async function summarizeFunction(editor) +function summarizeProgram(editor) // note: not async +``` + +`summarizeProgram` is synchronous because it needs no symbol resolution β€” the whole document is the input. The other two must locate the enclosing construct first, which is an async round trip to the language server. + +### Finding the enclosing construct + +Scope resolution uses VS Code's own symbol provider rather than parsing: + +```js +vscode.commands.executeCommand("vscode.executeDocumentSymbolProvider", document.uri) +``` + +That returns a `DocumentSymbol` tree from whichever language extension owns the file, so EchoCode inherits accurate scoping for every language the user has support installed for, with no per-language code. + +Two consequences: + +- **Quality tracks the user's installed extensions.** In Python with Pylance the tree is excellent; in a language with no extension installed it is empty and summaries fall back or fail. +- **It is async and can be slow** on a large file or a cold language server. + +`codeParser.js` supplies the geometry for picking the right symbol: + +```js +function inRange(pos, range) // does this symbol contain the cursor? +function rangeSize(range) // how large is it? +``` + +`rangeSize` is what makes nesting work. Several symbols can contain the cursor β€” a method inside a class inside a module β€” so the **smallest** containing range is the most specific, and that is the one summarised. + +### Request path + +Each function builds a prompt around the extracted source and calls `requestTextFromMessages()` from `AIrequest.js`, then speaks the result through `speakMessage`. Backend choice is invisible here β€” see [AI Providers and Models](AI-Providers-and-Models). + +Summaries are spoken, not written to a panel, so they are subject to speech interruption: asking for a second summary cuts off the first. See [Speech Handler](Speech-Handler). + +### Registration + +```js +registerSummarizerCommands(context, outputChannel) +``` + +Registers all three commands, each wrapped in `guard()` for Student-mode enforcement. Called directly from `extension.js` β€” the summarizer is **not** behind the feature-implementation loader, so unlike most features it cannot be swapped via settings. It lives under `Core/`, not `program_features/`. + +### Extending it + +To add a new scope (a spoken summary of a selected block, say): + +1. Add a function following the existing shape β€” resolve scope via the symbol provider, extract text, prompt, speak. +2. Register it in `registerSummarizerCommands`, wrapped in `guard()`. +3. Declare the command in `package.json`, and a keybinding if it warrants one. +4. Add the locked command id to `STUDENT_LOCKED_COMMANDS` in `Core/program_settings/guard.js` if it is an explanation feature β€” summaries are locked in Student mode as a cheating-prevention measure, and a new one should match. diff --git a/wiki/Custom-Feature-Implementations.md b/wiki/Custom-Feature-Implementations.md new file mode 100644 index 0000000..7be57a7 --- /dev/null +++ b/wiki/Custom-Feature-Implementations.md @@ -0,0 +1,138 @@ +# Custom Feature Implementations + +> **Developer page.** The settings are user-visible, but this is a development mechanism. + +## What it is + +Eight of EchoCode's features can be replaced with your own code **without forking the repo**, at runtime, with hot reload. Point a setting at `user`, drop a file in that feature's `UserImplementation/` folder, and EchoCode loads yours instead of the built-in one. + +This exists because EchoCode began as a course project where teams needed to reimplement a feature while keeping everything else working. + +## The swappable eight + +| Setting key | Feature folder | +|---|---| +| `annotationsBigO` | `program_features/Annotations_BigO/` | +| `assignmentTracker` | `program_features/Assignment_Tracker/` | +| `errorParser` | `program_features/C++_Error_Parser/` | +| `chatBot` | `program_features/ChatBot/` | +| `fileConnector` | `program_features/FileConnector/` | +| `folderFileCreator` | `program_features/Folder_File_Creator/` | +| `voice` | `program_features/Voice/` | +| `whatIsThis` | `program_features/WhatIsThis/` | + +Each is `echocode.featureImplementation.`, `builtin` (default) or `user`. + +**Not swappable:** anything under `Core/` (speech, AI routing, mode, guidance, summaries) or `navigation_features/`. Those are called directly by `extension.js`. + +## How to replace one + +1. Create `program_features//UserImplementation/` +2. Export **every** name the loader requires for that module (below) +3. Set `echocode.featureImplementation.` to `user` + +That is it. No restart β€” the change is picked up by the configuration watcher, and further edits to your file trigger a reload. + +### Required exports + +The loader validates exports and falls back if any are missing. Declared per module in `loadFeatureImplementations`: + +| Module file | Must export | +|---|---| +| `Voice/dependencyManager.js` | `DependencyManager` (class, or default export) | +| `WhatIsThis/WhatIsThis` | `registerReadCurrentLineCommand` | +| `WhatIsThis/DescribeThis.js` | `registerDescribeCurrentLineCommand` | +| `WhatIsThis/CharacterReadOut.js` | `registerCharacterReadOutCommand` | +| `C++_Error_Parser/Python_Error_Parser.js` | `checkCurrentPythonFile` | + +…and so on for the rest. Read `loadFeatureImplementations` in `extension.js` for the current list β€” it is the source of truth, and each call declares its own `requiredExports`. + +Note that a feature key can cover **several module files**. `whatIsThis` covers three, so setting it to `user` makes EchoCode look for all three in that folder. Any one you do not supply falls back individually. + +## It fails safe, and tells you why + +Every failure path returns the built-in module and logs to the EchoCode output channel: + +``` +[Feature Loader] User implementation not found for voice: …/UserImplementation/dependencyManager.js. Using built-in module. +[Feature Loader] User implementation for whatIsThis/DescribeThis.js is missing exports: registerDescribeCurrentLineCommand. Using built-in module. +[Feature Loader] Failed to load user implementation for chatBot/Chat_Tutor.js: Unexpected token. Using built-in module. +[Feature Loader] Loaded user implementation for voice/dependencyManager.js +``` + +**If your implementation is not being used, read those lines.** Silence means your file was never looked for β€” check the setting. + +## Implementation + +`program_features/featureImplementationLoader.js` β€” ~114 lines. + +```js +loadProgramFeatureModule({ featureKey, featureFolder, moduleFile, requiredExports, outputChannel }) +resolveClassExport(moduleExports, exportName) +clearFeatureRequireCache(featureFolder) +``` + +### The load decision + +```js +const defaultModule = require(defaultModulePath); // always loaded +if (mode !== "user") return defaultModule; +if (!fs.existsSync(userModulePath)) { log; return defaultModule; } +try { + const userModule = require(userModulePath); + if (!hasRequiredExports(userModule, requiredExports)) { log missing; return defaultModule; } + return userModule; +} catch (error) { log; return defaultModule; } +``` + +The built-in is **always required, even in user mode**. It is the fallback, so it must be loaded before the attempt. Cost is negligible β€” the whole module graph is 40ms β€” and it guarantees a working fallback exists. + +`hasRequiredExports` checks `typeof !== "undefined"`, so it accepts any defined value, not only functions. + +### Class resolution + +```js +function resolveClassExport(moduleExports, exportName) { + if (typeof moduleExports === "function") return moduleExports; // module.exports = Class + if (typeof moduleExports?.[exportName] === "function") return moduleExports[exportName]; + return null; +} +``` + +Accepts both `module.exports = DependencyManager` and `module.exports = { DependencyManager }`, so a user implementation can use either convention. + +### Hot reload + +```js +function clearRequireCacheByPrefix(prefixPath) { + Object.keys(require.cache).forEach((cacheKey) => { + if (path.resolve(cacheKey).startsWith(path.resolve(prefixPath))) delete require.cache[cacheKey]; + }); +} +``` + +Clears the **whole feature folder** by path prefix, not just the changed file β€” a feature's modules import each other, so clearing one leaves stale references to the rest. + +`extension.js` then calls `loadFeatureImplementations()` again and re-registers commands, disposing the previous registration via `setFeatureDisposable`. + +## Pitfalls when writing one + +**Module-level state does not survive a reload.** A reload clears the require cache, so your module is re-evaluated and its state resets. Anything that must persist belongs in `context.globalState` or `context.workspaceState`. + +**A reload constructs new objects.** If `extension.js` builds an instance of your class, a reload builds another. Per-instance guards cannot prevent repeated work β€” the voice `DependencyManager` needs a *module-level* memo for exactly this reason. See [Voice Input](Voice-Input). + +**Dispose everything.** Push disposables onto `context.subscriptions` and return them from your registrar. A registrar that registers a command without a disposable leaves a duplicate handler after every reload. + +**The watcher fires on your own edits.** It is workspace-relative β€” `**/program_features//UserImplementation/**` β€” so if you are developing EchoCode itself, saving your implementation triggers the reload. That is the feature working, but it means a save mid-edit can reload broken code. The fallback logging is how you notice. + +**Match the built-in's contract, not just its exports.** The loader checks names, not behaviour. A `registerX` that never pushes to `context.subscriptions`, or returns nothing where `extension.js` expects a disposable, passes validation and then misbehaves. + +## Adding a swappable feature + +1. Put it in `program_features//` with a `UserImplementation/` subfolder. +2. Add a `loadProgramFeatureModule` call in `loadFeatureImplementations` with explicit `requiredExports`. +3. Add the key to `FEATURE_FOLDER_MAP` in `extension.js` β€” that drives both the config watcher and the file watcher. +4. Declare `echocode.featureImplementation.` in `package.json` with the `builtin`/`user` enum. +5. Handle it in `registerFeatureCommands` so a reload re-registers it. + +Step 3 is the one people miss. Without it the feature loads but never hot-reloads, and the setting appears to do nothing until a window reload. diff --git a/wiki/Error-Reading.md b/wiki/Error-Reading.md new file mode 100644 index 0000000..1c055f5 --- /dev/null +++ b/wiki/Error-Reading.md @@ -0,0 +1,199 @@ +# Error Reading + +## For users + +EchoCode compiles or lints your code and reads the problems aloud in plain language rather than compiler jargon. + +**Dev mode only.** Locked in Student mode β€” error parsers can effectively solve an assignment. + +### One shortcut, two languages + +**Ctrl+Alt+G** does the right thing for the file you are in: + +| File type | What happens | +|---|---| +| `.py` | Runs pylint and reads the findings | +| `.cpp` | Compiles with `g++` and reads the compiler errors | + +The binding is gated on the file's language, so the same chord is unambiguous. + +### Python + +Requires `pylint` in the Python environment EchoCode invokes: + +```bash +python3 -m pip install pylint +``` + +On Windows use `python` instead of `python3`. + +Use `-m pip` rather than a bare `pip` β€” it installs into the *same* interpreter EchoCode shells out to. A bare `pip` can belong to a different Python, which produces the confusing case where the install succeeds and EchoCode still reports pylint missing. + +Findings are translated into beginner-friendly language. Instead of `undefined-variable`, you hear *"This variable is not defined. Did you forget to create it?"* A small set of common messages have hand-written explanations; everything else is read as pylint phrased it. + +Some findings are marked **critical** β€” undefined variables, syntax errors, indentation errors β€” and those are the ones read aloud. The rest go to the output channel so the readout stays short. + +### C++ + +Requires `g++` on your PATH. EchoCode compiles the current file, parses the compiler output, and reads the errors with their line numbers. + +### How verbose it is + +Error explanations follow your [guidance level](Modes-and-Guidance) β€” **Ctrl+Alt+Z** cycles it. `concise` gives you location plus one sentence; `guided` adds next steps. + +### Known limitation: nothing happens on save + +Automatic linting when you save a Python file is **not currently active**. The code for it exists but is not wired into startup, so only the manual **Ctrl+Alt+G** path works. See [Known Gaps](Known-Gaps). + +The `Read Python Errors Aloud` entry in the Command Palette is part of that same dormant feature and will not work β€” use **Ctrl+Alt+G** instead. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Error Reading β€” Level 0 context diagram](diagrams/svg/05-error-reading-L0.svg) + +*Editable source: `wiki/diagrams/05-error-reading.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Error Reading β€” Level 1 decomposition diagram](diagrams/svg/05-error-reading-L1.svg) + +*Editable source: `wiki/diagrams/05-error-reading.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Responsibility | Live? | +|---|---|---| +| `Language/Python/pylintHandler.js` | Spawns pylint, classifies failures, translates messages | Yes | +| `Language/Python/errorHandler.js` | Save-triggered linting and spoken readout | **No β€” never invoked** | +| `program_features/C++_Error_Parser/Python_Error_Parser.js` | `checkCurrentPythonFile`, the live manual path | Yes | +| `program_features/C++_Error_Parser/CPP_Error_Parser.js` | C++ compile and parse | Yes | +| `Language/registry.js` | Per-language adapter lookup | Yes | + +### The dormant module + +`extension.js` imports both of `errorHandler.js`'s entry points and **calls neither**: + +```js +const { initializeErrorHandling, registerErrorHandlingCommands } = require("./Language/Python/errorHandler"); +``` + +Those are the only two occurrences in the file. Consequences: + +- `registerErrorHandlingCommands` registers the `onDidSaveTextDocument` listener, so **pylint has never run on save**. +- It also registers `echocode.readErrors`, which is declared in `package.json` but therefore never registered at runtime β€” invoking it from the Palette fails. +- `initializeErrorHandling(channel)` supplies the output channel, so the module's `outputChannel` is `undefined`. + +`extension.js` also logs `"Commands registered: echocode.readErrors, …"` at startup. That log is wrong. + +Turning it on is two calls in `activate()`: + +```js +initializeErrorHandling(outputChannel); +registerErrorHandlingCommands(context); +``` + +Be deliberate about it β€” that switches on a toast or an auto-opening output panel on every Python save, plus spoken critical errors. It is a user-visible behaviour change, not just a fix. + +### The live Python path + +`echocode.checkPythonErrors` is registered directly in `extension.js`, guarded, and language-checked: + +```js +if (editor && editor.document.languageId === "python") { + featureImplementations.checkCurrentPythonFile(editor.document.uri.fsPath); +} +``` + +`checkCurrentPythonFile` comes from `program_features/C++_Error_Parser/Python_Error_Parser.js` β€” a confusing home for it, but that is where it lives, and it has a `UserImplementation/` variant. + +### pylintHandler + +```js +runPylint(filePath, outputChannel) // β†’ Promise> +isPylintAvailable() // cached probe +promptToInstallPylint() +ensurePylintInstalled() +``` + +Failures are **classified**, not generic, so callers can tell a setup problem from a real failure: + +```js +PYLINT_NOT_INSTALLED // "No module named pylint" on stderr +PYTHON_NOT_FOUND // spawn ENOENT +PYLINT_TIMED_OUT // 10s cap +PYLINT_FAILED // anything else +``` + +That distinction matters: a missing toolchain should prompt once, while a genuine pylint failure is worth reporting every time. Before it existed, a user without pylint got an error toast on **every save**. + +Interpreter choice is platform-dependent, and the same helper drives both the spawn and the message shown to the user so they always match: + +```js +function pythonCommand() { + return process.platform === "win32" ? "python" : "python3"; +} +``` + +Windows installs the interpreter as `python`; elsewhere a bare `python` may be a Python 2 stub or absent. + +`isPylintAvailable()` caches its probe β€” re-spawning an interpreter on every save is exactly the cost worth avoiding. Call `resetPylintAvailability()` after the user installs it. + +Three robustness details worth not regressing: + +- **The 10s timeout is cleared on every exit path.** Left running, it fired ten seconds after a *successful* run and killed an already-dead process. +- **`spawn` has an `error` handler.** With a missing interpreter, spawn emits `error` and never emits `close`, so without this the promise hung until that stray timeout fired. +- **Exit code 0 with empty stdout is not success.** pylint's JSON output is the source of truth. + +### Message translation + +```js +function isCriticalError(symbol) // Set of 5 pylint symbols +function simplifyError(symbol, message) // lookup table, falls through to raw message +``` + +Critical set: `undefined-variable`, `syntax-error`, `indentation-error`, `attribute-defined-outside-init`, `assignment-from-none`. Only these are spoken; the rest go to the channel. + +Adding a translation is one entry in the `explanations` object in `simplifyError`. Unknown symbols pass through unchanged, so the table can stay small. + +### errorHandler, if you enable it + +The dormant module has real work in it already: + +- **400ms debounce per file** β€” a save-all or auto-save otherwise spawns one interpreter per file per burst, and pylint's ~300ms startup dominates its runtime. +- **`document.version` check** β€” a save that changed nothing does not re-lint. +- **Missing-toolchain latch** β€” after one failure, automatic runs stop rather than re-spawning to rediscover the same thing. The explicit command clears the latch, so it doubles as the retry. +- **The lint lock is released before speaking.** Speech is several seconds per finding; holding the lock across it meant every save during the readout was silently dropped. +- **Stale readouts stop.** A generation counter means newer results cancel an in-progress readout rather than narrating findings that no longer apply. + +Measured pylint cost, for sizing decisions: ~320ms on 48 lines, ~385ms on 498, ~635ms on 1998. Roughly 300ms of that is fixed interpreter and astroid startup, independent of file size β€” which is why debouncing helps far more than optimising the analysis. + +### C++ parsing + +```js +parseCppCompilationErrors(terminalOutput) +analyzeCppCompilation(command, outputFilePath) +compileCurrentCppFile(currentFilePath) +findCppSourceFiles(dir) +``` + +Compiles, then parses `g++`'s text output with pattern matching. Tied to GCC-style diagnostics β€” clang's format is close enough to mostly work, MSVC's is not. + +### Language adapters + +`Language/registry.js` is a small indirection for per-language behaviour: + +```js +function pickAdapter(langId) // β†’ adapter or NoOpAdapter +function safeRequire(p) // missing adapter β‰  crash +``` + +`NoOpAdapter` is the fallback, so an unsupported language degrades silently. This is the seam to use for a third language β€” add an adapter rather than another `if (languageId === …)` branch in `extension.js`. diff --git a/wiki/File-Connector.md b/wiki/File-Connector.md new file mode 100644 index 0000000..23ed743 --- /dev/null +++ b/wiki/File-Connector.md @@ -0,0 +1,136 @@ +# File Connector + +## For users + +Generates the import statement needed to use a function from one file in another, and pastes it in for you. + +**Dev mode only.** Locked in Student mode. + +### The two-step flow + +| Step | Shortcut | Does | +|---|---|---| +| 1 | **Ctrl+Alt+I** | In the *source* file, with the cursor in a function β€” copy it for import | +| 2 | **Ctrl+Shift+I** | In the *destination* file β€” paste the import statement | + +You never type the import path yourself, and you never have to work out the relative path between two files by ear. + +### Python + +Copying `calculate_total` from `utils/math_helpers.py`, then pasting in `main.py`, gets you a correct `from … import …` line with the module path resolved for you. + +### C++ + +More is done on your behalf. EchoCode extracts the function's signature, and if the source file has **no header**, it creates one and adds the declaration. Then it inserts the right `#include` in the destination. + +That is the tedious part of C++ done automatically: declaration in a header, definition left in the `.cpp`, include wired up. + +Existing `#include` lines in the source are read so the generated header carries the dependencies the declaration needs. + +### If it does not work + +- **"No function at cursor"** β€” put the cursor inside a function body or on its signature. EchoCode uses the language's own symbol information, so the relevant language extension must be installed. +- **Incompatible file types** β€” connecting a Python function into a C++ file is refused. Both files must be the same language. +- **Nothing copied** β€” step 1 must succeed before step 2 has anything to paste. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![File Connector β€” Level 0 context diagram](diagrams/svg/09-file-connector-L0.svg) + +*Editable source: `wiki/diagrams/09-file-connector.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![File Connector β€” Level 1 decomposition diagram](diagrams/svg/09-file-connector-L1.svg) + +*Editable source: `wiki/diagrams/09-file-connector.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### File + +`program_features/FileConnector/File_Connector.js` β€” ~452 lines, the largest single feature module. Under the `fileConnector` feature key. + +### Public surface + +```js +connectFunction(...) // the orchestrator +findFunctionAtPosition(symbols, position) +areExtensionsCompatible(source, dest) +registerFileConnectorCommands(context, vscode) +``` + +Note the registrar takes `vscode` as a parameter rather than requiring it β€” unusual in this codebase, and a small dependency-injection seam that makes the module testable without the module-level `vscode` mock. + +### Private helpers, by language + +Underscore-prefixed by convention: + +**Python** +```js +_baseFunctionName(name) +_generatePythonImport(sourcePath, functionName) +``` + +**C++** +```js +_extractCppFunctionSignature(sourcePath, functionName) +_extractCppIncludes(sourcePath) +_ensureCppHeader(sourcePath, functionName, functionSignature) +_generateCppImport(...) +_generateCppHeaderInclude(sourcePath, destPath) +``` + +The asymmetry is inherent: Python imports are a module path, while C++ needs a declaration in a header plus an include. Roughly three-quarters of the module is the C++ path. + +### Finding the function + +```js +findFunctionAtPosition(symbols, position) +``` + +Operates on a `DocumentSymbol` tree from `executeDocumentSymbolProvider`, the same approach as [Code Summaries](Code-Summaries) and [Code Navigation](Code-Navigation) β€” language understanding is borrowed from whichever extension owns the file rather than parsed here. + +`_extractCppFunctionSignature` is the exception: it goes to the file text with `readFileSync`, because the symbol tree gives a range and a name but not a usable declaration string. + +### Header generation + +`_ensureCppHeader` is the most consequential function β€” it **writes a file the user did not ask for**. Behaviour: + +- No header exists β†’ create it with an include guard and the declaration +- Header exists without this declaration β†’ append it +- Declaration already present β†’ leave it alone + +Combined with `_extractCppIncludes`, so a generated header carries what the declaration needs to compile. + +Anything here is user-visible file modification. Treat changes as higher-risk than the rest of the module and check the idempotency: running the command twice must not duplicate a declaration. + +### Compatibility gate + +```js +function areExtensionsCompatible(source, dest) +``` + +Refuses cross-language connections. Called before generation, so an unsupported pair fails with a clear message rather than producing a syntactically valid but meaningless import. + +This is where a new language starts: add the pair, then a `_generateImport`. + +### State between the two commands + +`echocode.copyFileNameForImport` stores the pending connection; `echocode.pasteImportAtCursor` consumes it. That handoff is module-level state and does not survive a reload β€” the copy must be repeated after one. + +### Adding a language + +1. Extend `areExtensionsCompatible` with the extension pair. +2. Add `_generateImport(sourcePath, functionName)`. +3. Dispatch on extension in `connectFunction`. +4. If the language needs a declaration file, model it on `_ensureCppHeader` β€” and make it idempotent. + +Prefer the symbol provider over text parsing wherever the information is available; fall back to reading the file only for things symbols cannot give you, as the C++ signature extraction does. diff --git a/wiki/File-and-Folder-Tools.md b/wiki/File-and-Folder-Tools.md new file mode 100644 index 0000000..da3f3d3 --- /dev/null +++ b/wiki/File-and-Folder-Tools.md @@ -0,0 +1,110 @@ +# File and Folder Tools + +## For users + +Create files and folders without the mouse or the Explorer tree. Both work in Student mode. + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+;** | Create a file | +| **Ctrl+Alt+F** | Create a folder | + +### How it works + +Press the shortcut, type a name, press Enter. EchoCode creates it and confirms aloud. + +New items are created relative to **your current navigation folder** β€” the one you last moved to with **Ctrl+Alt+[** / **Ctrl+Alt+]** β€” not wherever the Explorer happens to be focused. That keeps keyboard navigation and creation consistent: move to a folder, create in it. + +If you are unsure where you are, **Ctrl+Alt+E W** tells you. See [Code Navigation](Code-Navigation). + +### If the name already exists + +EchoCode tells you rather than overwriting. Nothing is destroyed silently. + +### Creating a file opens it + +A newly created file is opened in the editor with your cursor in it, so you can start typing immediately. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![File and Folder Tools β€” Level 0 context diagram](diagrams/svg/08-file-folder-tools-L0.svg) + +*Editable source: `wiki/diagrams/08-file-folder-tools.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![File and Folder Tools β€” Level 1 decomposition diagram](diagrams/svg/08-file-folder-tools-L1.svg) + +*Editable source: `wiki/diagrams/08-file-folder-tools.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Provides | Command | +|---|---|---| +| `program_features/Folder_File_Creator/FileCreator.js` | `registerFileCreatorCommand` | `echocode.createFile` | +| `program_features/Folder_File_Creator/FolderCreator.js` | `registerFolderCreatorCommand` | `echocode.createFolder` | + +Both under the `folderFileCreator` feature key, so they swap together via `echocode.featureImplementation.folderFileCreator`. See [Custom Feature Implementations](Custom-Feature-Implementations). + +### The shape + +```js +async function createFile() { + // 1. resolve the target directory + // 2. showInputBox for the name + // 3. existsSync guard + // 4. write / mkdir + // 5. open the document (files only) + // 6. speakMessage confirmation +} +``` + +`createFolder` is the same without step 5. + +### Where "here" comes from + +Both import the navigation cursor rather than using `workspaceFolders[0]`: + +```js +const { getCurrentFolder } = require(".../folder_navigator"); +``` + +This is the coupling worth understanding: creation follows the **navigation** cursor, not the Explorer selection or the active editor's directory. It is what makes "move to folder, create file" work as one keyboard flow, and it is why a user who has never navigated gets the initial folder. + +If you add another creation command, use `getCurrentFolder()` for consistency. Using `workspaceFolders[0]` instead would create files somewhere the user did not expect. + +### The existence check + +```js +if (fs.existsSync(filePath)) { + // report and bail β€” never overwrite +} +``` + +`existsSync` here is a **TOCTOU race** in principle β€” the path could appear between the check and the write. In practice the window is microseconds of human-driven interaction, so it is not worth hardening. Worth knowing before someone files it as a bug. + +Note `existsSync` follows symlinks, so a dangling symlink reports `false` and the write then fails on a different error path. + +### Speech and confirmation + +Both speak a confirmation and show an information message β€” belt and braces, so a sighted user and a screen-reader user both get feedback. Keep both if you extend this; the visible message is what a sighted collaborator sees. + +### Adding a creation command + +A template file creator (new Python module with a docstring, say) fits naturally here: + +1. New function following the shape above. +2. Resolve the directory with `getCurrentFolder()`. +3. `showInputBox` with `validateInput` to reject invalid names before the user commits. +4. Guard with `existsSync`. +5. Register in the existing registrar, declare in `package.json`. +6. Keep the module's exports matching the `requiredExports` the loader declares, or the whole feature silently falls back to built-in. diff --git a/wiki/Getting-Started.md b/wiki/Getting-Started.md new file mode 100644 index 0000000..65f1a47 --- /dev/null +++ b/wiki/Getting-Started.md @@ -0,0 +1,111 @@ +# Getting Started + +## For users + +### Requirements + +| Thing | Why | Required? | +|---|---|---| +| VS Code 1.99.1 or newer | EchoCode's `engines.vscode` floor | Yes | +| An AI backend | Anything that explains, summarises, or annotates | For AI features only | +| Python 3.10+ with `pylint` | Reading Python errors aloud | For Python error features | +| A C++ compiler (`g++`) | Compiling and reading C++ errors | For C++ features | +| A microphone | Voice input | For voice only | + +Text-to-speech needs nothing installed β€” EchoCode uses your operating system's built-in voice. + +### First launch + +EchoCode activates on `onStartupFinished`, which means it waits until VS Code has finished loading rather than slowing down startup. On first run it asks one question: **which AI backend do you want?** + +You will hear the choice read aloud and see a picker with two options: + +- **API model** β€” GitHub Copilot, or a hosted API such as OpenAI, OpenRouter, Groq, or Anthropic +- **Local model (Ollama)** β€” runs entirely on your own machine, nothing leaves it + +Arrow through the list; each entry is announced with its position ("1 of 2"). Press Enter to choose. + +If you would rather not decide now, press Escape. Nothing breaks β€” the non-AI features (navigation, line read-out, file tools) all work without a model. You can pick one later with **EchoCode: Select AI Provider & Model** from the Command Palette. + +See [AI Providers and Models](AI-Providers-and-Models) for what each option costs and needs. + +### The one shortcut worth memorising first + +Press **F1** with focus in the editor and EchoCode reads its own hotkey guide aloud. If you forget everything else, that gets it back. + +Second most useful: **Ctrl+Alt+X** stops speech immediately. You will want it. + +### Student mode and Dev mode + +EchoCode starts in **Student mode**, which deliberately locks the features that would do a student's homework for them β€” summaries, explanations, annotations, the chat tutor, error parsers, the assignment tracker. + +If you are using EchoCode as a working developer, switch to Dev mode: + +- **Ctrl+Alt+9** toggles between the two, or +- Command Palette β†’ **EchoCode: Switch to Developer Mode** + +Many shortcuts do nothing in Student mode. That is intentional, not a bug. If a shortcut seems dead, check which mode you are in β€” EchoCode announces the mode when it changes, with a distinct audio ping. See [Modes and Guidance](Modes-and-Guidance). + +### Where to look when something goes wrong + +Open **View β†’ Output** and pick **EchoCode** from the dropdown. Almost everything EchoCode does logs there, including the exact shell commands it runs and how long they took. It is the single most useful place to look before filing a bug. + +--- + +## For developers + +### Repo layout + +``` +extension.js Activation, command wiring, voice-mode dispatch (~1050 lines) +Core/ + program_settings/ + program_settings/ AI routing, provider setup, HTTP, secrets, system scan + speech_settings/ Text-to-speech (see Speech Handler) + guide_settings/ Hotkey guide, guidance levels + guard.js Student-mode command lock + mode.js, modeAudio.js Student/Dev mode state and audio cues + Summarizer/ Class/function/program summaries + Diagnostics/ +Language/ + Python/ pylint invocation and error handling + registry.js Per-language adapter lookup +program_features/ One folder per user-facing feature + featureImplementationLoader.js +navigation_features/ Cursor, file, and folder navigation +media/ Chat webview assets +audio_pings/ Mode-change sounds +local_models/ Whisper model directory +``` + +### Running it locally + +```bash +npm install +``` + +Press **F5** to launch the Extension Development Host. That runs `npm run compile` (`tsc -p ./`) as a pre-launch task first β€” if TypeScript fails, the host will not start. + +```bash +npm test +``` + +Runs the Mocha suite. See [Testing and CI](Testing-and-CI) β€” it is worth reading before you touch the harness, because the test files must load under two incompatible Node runtimes. + +### Where activation begins + +`extension.js` exports `activate(context)`. In rough order it: + +1. Creates the `EchoCode` output channel +2. Loads every feature module through the [feature loader](Custom-Feature-Implementations) +3. Registers commands, grouped by feature +4. Initialises secret storage, then the [AI provider](AI-Providers-and-Models) check (not awaited) +5. Initialises and builds the external command registry (see [Voice Input](Voice-Input)) +6. Kicks off the voice dependency bootstrap (not awaited) +7. Registers file watchers and the configuration-change listener + +Steps 4 and 6 are deliberately **not awaited** β€” both can take a long time, and holding activation on them used to make EchoCode appear frozen on launch. See [Architecture](Architecture) for the full path and the reasoning. + +### Packaging + +`.vscodeignore` controls what ships in the VSIX. Note that `node_modules` is intentionally **not** excluded: `vsce` prunes devDependencies itself, and excluding the folder would strip runtime dependencies (`say`, `sound-play`, `ffmpeg-static`, `fluent-ffmpeg`, `mammoth`, `pdf-parse`, `dotenv`) and break the published extension. diff --git a/wiki/Home.md b/wiki/Home.md new file mode 100644 index 0000000..a737c39 --- /dev/null +++ b/wiki/Home.md @@ -0,0 +1,63 @@ +# EchoCode Wiki + +EchoCode is a VS Code extension that makes writing code workable without looking at the screen. It reads your errors aloud, explains lines and functions, takes voice commands, tracks assignment tasks, and narrates where your cursor is β€” all driven by keyboard shortcuts and speech rather than the mouse and the minimap. + +It is built for blind and low-vision programmers, and for students learning to program who benefit from hearing what their code does. + +## How this wiki is organised + +Every feature page has the same shape: + +1. **For users** β€” what the feature does, how to trigger it, what you will hear, and what can go wrong. +2. **For developers** β€” which files implement it, how the pieces connect, and where to change things. + +Read only the first half if you are using EchoCode. Read both if you are working on it. + +A few pages are **developer-only**, because they document machinery a user never interacts with directly β€” the speech engine, the feature loader, the test harness. Those are marked as such. + +## Start here + +| If you want to… | Read | +|---|---| +| Install it and hear something work | [Getting Started](Getting-Started) | +| Learn the keyboard shortcuts | [Keyboard Shortcuts](Keyboard-Shortcuts) | +| Point EchoCode at an AI model | [AI Providers and Models](AI-Providers-and-Models) | +| Understand every setting | [Settings Reference](Settings-Reference) | +| Understand the codebase | [Architecture](Architecture) | + +## Features + +**AI-backed** +- [AI Providers and Models](AI-Providers-and-Models) β€” Copilot, local Ollama, or a hosted API +- [Chat Tutor](Chat-Tutor) β€” a conversational tutor in the sidebar +- [Annotations and Big-O](Annotations-and-Big-O) β€” inline review of your code, read aloud +- [Code Summaries](Code-Summaries) β€” summarise a function, class, or whole file +- [Assignment Tracker](Assignment-Tracker) β€” load a homework file, track tasks against your code + +**Reading your code aloud** +- [Line and Character Read-Out](Line-and-Character-Read-Out) β€” read or explain the current line; hear each keypress +- [Error Reading](Error-Reading) β€” Python and C++ errors, spoken in plain language + +**Getting around** +- [Code Navigation](Code-Navigation) β€” jump between functions, ask where you are +- [File and Folder Tools](File-and-Folder-Tools) β€” create and move between files and folders +- [File Connector](File-Connector) β€” generate import statements between files + +**Voice** +- [Voice Input](Voice-Input) β€” speak commands, dictate code, talk to the tutor + +**Behaviour and presentation** +- [Modes and Guidance](Modes-and-Guidance) β€” Student vs Dev mode, and how verbose EchoCode is +- [Hotkey Guide](Hotkey-Guide) β€” the built-in spoken shortcut list + +## Developer-only pages + +- [Architecture](Architecture) β€” activation, module layout, the request path +- [Speech Handler](Speech-Handler) β€” how speech is queued, interrupted, and kept from overlapping +- [Custom Feature Implementations](Custom-Feature-Implementations) β€” swap any feature for your own code without forking +- [Testing and CI](Testing-and-CI) β€” the test harness, and why it runs on three Node versions +- [Known Gaps](Known-Gaps) β€” verified dead code, unreachable commands, and rough edges + +## A note on accuracy + +This wiki documents what the code **actually does**, verified by reading it β€” not what it is supposed to do. Where a feature is declared but not wired up, or works differently than its name suggests, that is stated plainly on the feature's page and collected in [Known Gaps](Known-Gaps). If you find a discrepancy, the code is the truth and this wiki is the bug. diff --git a/wiki/Hotkey-Guide.md b/wiki/Hotkey-Guide.md new file mode 100644 index 0000000..23cca57 --- /dev/null +++ b/wiki/Hotkey-Guide.md @@ -0,0 +1,77 @@ +# Hotkey Guide + +## For users + +**F1** with focus in the editor reads EchoCode's shortcuts aloud. + +This is the recovery command. If you forget everything else in this wiki, F1 gets it back without needing to read a screen. + +It works in both Student and Dev mode. + +### What you hear + +The shortcuts and what they do, spoken in order. **Ctrl+Alt+X** stops it if you have heard what you needed. + +### Why F1 and not something else + +F1 is the conventional help key, and it is a single keypress β€” no chord to remember, reachable one-handed. VS Code's own "Help" is bound elsewhere, so there is no conflict inside the editor. + +### If you prefer to read it + +The same content is in [Keyboard Shortcuts](Keyboard-Shortcuts), organised by task and including which shortcuts are Dev-mode-only. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Hotkey Guide β€” Level 0 context diagram](diagrams/svg/13-hotkey-guide-L0.svg) + +*Editable source: `wiki/diagrams/13-hotkey-guide.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Hotkey Guide β€” Level 1 decomposition diagram](diagrams/svg/13-hotkey-guide-L1.svg) + +*Editable source: `wiki/diagrams/13-hotkey-guide.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### File + +`Core/program_settings/guide_settings/hotkeyGuide.js` β€” ~104 lines. + +```js +async function showHotkeyGuide() +function registerHotkeyGuideCommand(context) +``` + +Registers `echocode.readHotkeyGuide`. Called directly from `extension.js` β€” not behind the feature loader, and **not** wrapped in `guard()`, since help must work in every mode. + +### The maintenance problem + +The guide's text is **a hand-maintained list inside the module**. It is not generated from `package.json`, which means: + +> Every time you add, change, or remove a keybinding, this file must be edited too β€” or EchoCode will read out shortcuts that do not exist and omit ones that do. + +There is no test catching the drift. For a blind user this is a worse failure than a stale README: they have no way to discover the real binding except trial and error. + +### Generating it instead + +The manifest already holds everything needed β€” `contributes.keybindings` pairs `key`/`mac` with `command`, and `contributes.commands` supplies human titles. A generated guide would stay correct automatically. + +Three things to handle: + +1. **Speak the chord, not the string.** `ctrl+alt+e c` read literally is "control alt e c". It wants expanding to "Control Alt E, then C", and `ctrl+alt+'` to "Control Alt apostrophe". +2. **Respect the platform.** Entries carry an optional `mac` field; reading the Windows chord to a Mac user is wrong. +3. **Filter by mode.** A `when` clause containing `echocode:isDev` means the shortcut is inert in Student mode, so reading it to a student is misleading. The clause is machine-readable β€” check it against the current mode. + +A middle option, if full generation is too much: a test that asserts every `contributes.keybindings` command appears in the guide text. That turns silent drift into a failing build. + +### Ordering matters for speech + +A spoken list is consumed linearly β€” no skimming, no scanning back. Order by task frequency, most-used first, rather than alphabetically or in manifest order. A user who needs "stop speech" should not have to sit through thirty other entries. diff --git a/wiki/Keyboard-Shortcuts.md b/wiki/Keyboard-Shortcuts.md new file mode 100644 index 0000000..56e860b --- /dev/null +++ b/wiki/Keyboard-Shortcuts.md @@ -0,0 +1,150 @@ +# Keyboard Shortcuts + +## For users + +EchoCode is keyboard-first. Almost every shortcut uses the **Ctrl+Alt** prefix so it does not collide with VS Code's own bindings. + +**Dev-only** shortcuts do nothing in Student mode β€” see [Modes and Guidance](Modes-and-Guidance). + +### The essentials + +| Shortcut | Does | +|---|---| +| **F1** | Read the hotkey guide aloud | +| **Ctrl+Alt+X** | Stop speech immediately | +| **Ctrl+Alt+9** | Toggle Student / Dev mode | +| **Ctrl+Alt+U** / **Ctrl+Alt+D** | Speech faster / slower | + +### Reading code + +| Shortcut | Does | Dev only | +|---|---|---| +| **Ctrl+Alt+L** | Read the current line verbatim | | +| **Ctrl+Alt+K** | Explain the current line in plain language | Yes | +| **Ctrl+Alt+R** | Toggle character read-out (speaks each character you type) | | +| **Ctrl+Alt+E W** | Where am I? β€” describe cursor position in context | | + +### Navigation + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+Down** | Jump to next function | +| **Ctrl+Alt+Up** | Jump to previous function | +| **Ctrl+Alt+P** | Navigate to next file | +| **Ctrl+Alt+[** | Move to next folder | +| **Ctrl+Alt+]** | Move to previous folder | + +### Understanding code (Dev mode) + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+E F** | Summarise the current function | +| **Ctrl+Alt+E C** | Summarise the current class | +| **Ctrl+Alt+E P** | Summarise the whole file | +| **Ctrl+Alt+A** | Toggle inline annotations | +| **Ctrl+Alt+S** | Speak the next annotation | +| **Ctrl+Alt+Q** | Read all annotations | +| **Ctrl+Alt+N** | Analyse Big-O complexity | +| **Ctrl+Alt+B** | Read next Big-O recommendation | +| **Ctrl+Alt+H** | Read all Big-O recommendations | +| **Ctrl+Alt+C** | Open the Chat Tutor | + +### Errors (Dev mode) + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+G** | Compile and read errors β€” C++ in a `.cpp` file, Python in a `.py` file | + +The same chord does different things depending on the file's language, because the binding is gated on `editorLangId`. + +### Voice + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+Space** | Start / stop recording in the current voice mode | +| **Ctrl+Alt+'** | Cycle voice mode (Chat β†’ Code β†’ Command) | +| **Ctrl+Alt+Shift+C** | Record once, insert as code | +| **Ctrl+Alt+Shift+V** | Record once, run as a command | +| **Ctrl+Alt+Shift+T** | Record once, send to the tutor | +| **Ctrl+Alt+V** | Start voice input (Dev mode) | + +### Files and folders + +| Shortcut | Does | Dev only | +|---|---|---| +| **Ctrl+Alt+;** | Create a file | | +| **Ctrl+Alt+F** | Create a folder | | +| **Ctrl+Alt+I** | Copy the current function name for import | Yes | +| **Ctrl+Shift+I** | Paste the generated import at the cursor | Yes | + +### Assignment tracker + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+O** | Load an assignment file | +| **Ctrl+Alt+T** | Read next task | +| **Ctrl+Alt+/** | Read next *incomplete* task | +| **Ctrl+Alt+M** | Mark current task complete | +| **Ctrl+Alt+Y** | Rescan your code for completed tasks | + +### Output verbosity + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+Z** | Cycle guidance level (guided β†’ balanced β†’ concise) | +| **Ctrl+Alt+Shift+Z** | Pick a guidance level from a list | + +### Multi-key chords + +Four bindings are two-step chords: **Ctrl+Alt+E** then a letter (`C`, `F`, `P`, `W`). Press and release `Ctrl+Alt+E`, then press the letter. If you pause too long VS Code cancels the chord and nothing happens. + +--- + +## For developers + +### Where bindings live + +All 45 are declared in `package.json` under `contributes.keybindings`. VS Code owns dispatch entirely β€” EchoCode never reads raw keystrokes, with one exception noted below. + +### The `when` clause is the gate + +Most bindings carry a `when` clause combining two conditions: + +```json +"when": "editorTextFocus && echocode:isDev" +``` + +- `editorTextFocus` β€” VS Code built-in; the binding is inert outside a text editor +- `echocode:isDev` β€” a **custom context key** EchoCode sets + +That context key is set in `Core/program_settings/mode.js`: + +```js +await vscode.commands.executeCommand("setContext", "echocode:isDev", isDev); +await vscode.commands.executeCommand("setContext", "echocode:isStudent", !isDev); +``` + +`refreshModeContext()` is called during activation and again whenever the mode changes. **This is the first layer of Student-mode enforcement** β€” a locked shortcut does not fire at all, so nothing needs to reject it. The second layer is the `guard()` wrapper, which catches Command Palette invocations that bypass keybindings entirely. See [Modes and Guidance](Modes-and-Guidance). + +### Things worth knowing before you edit + +**Adding a shortcut is two edits, not one.** A `contributes.commands` entry makes it appear in the Command Palette; a `contributes.keybindings` entry binds the key. Declaring a command without registering it at runtime produces a Palette entry that errors when invoked β€” this has already happened, see [Known Gaps](Known-Gaps). + +**`Ctrl+Alt+G` is bound twice on purpose**, once per language: + +```json +"when": "editorTextFocus && editorLangId == 'cpp' && echocode:isDev" +"when": "editorTextFocus && editorLangId == 'python' && echocode:isDev" +``` + +Mutually exclusive `when` clauses are the idiomatic way to make one chord language-sensitive. + +**`Ctrl+Alt+Space` is currently declared twice with identical `when` clauses** for `echocode.toggleVoice`. Harmless β€” VS Code resolves to the last match β€” but it is redundant. + +**`echocode.initializeFolderList` has a keybinding entry with an empty `key`.** It is effectively Palette-only. + +**Character read-out is the one exception to "VS Code owns dispatch."** `CharacterReadOut.js` subscribes to document-change events to speak each typed character, rather than binding keys. See [Line and Character Read-Out](Line-and-Character-Read-Out). + +### Suggested bindings, not enforced ones + +`contributes.keybindings` is a *default*. Users remap freely in their own `keybindings.json`, so never assume a chord is the only route to a command β€” always keep the Command Palette path working. diff --git a/wiki/Known-Gaps.md b/wiki/Known-Gaps.md new file mode 100644 index 0000000..4767584 --- /dev/null +++ b/wiki/Known-Gaps.md @@ -0,0 +1,159 @@ +# Known Gaps + +> **Developer page.** Verified by reading the code, not inferred from behaviour. Each entry says how it was confirmed. + +This page exists so nobody spends an afternoon debugging something already known. If you fix one, delete the entry. + +## Dead code and unreachable commands + +### Python error reading on save never runs + +`extension.js` imports both of `errorHandler.js`'s entry points and **calls neither**: + +```js +const { initializeErrorHandling, registerErrorHandlingCommands } = require("./Language/Python/errorHandler"); +``` + +Those are the only two occurrences in the file β€” confirmed with a case-insensitive grep and a check for dynamic dispatch through the feature loader. + +Consequences: + +- `registerErrorHandlingCommands` registers the `onDidSaveTextDocument` listener, so **pylint has never run on save** +- It also registers `echocode.readErrors`, declared in `package.json` β€” so that Palette entry fails when invoked +- `initializeErrorHandling(channel)` supplies the output channel, so the module's `outputChannel` is `undefined` + +**Fix:** two calls in `activate()`. Be deliberate β€” it switches on a toast or auto-opening panel on every Python save plus spoken errors, which is a user-visible behaviour change. The manual **Ctrl+Alt+G** path (`echocode.checkPythonErrors`) is wired separately and does work. + +### `echocode.navigateFilesInCurrentFolder` is declared but never registered + +In `contributes.commands`, registered nowhere in the source. Appears in the Command Palette and errors when invoked. `file_navigator.js` exports only `registerFileNavigatorCommand`, which registers `echocode.navigateToNextFile`. + +Verified by extracting every `registerCommand`/`registerTextEditorCommand` string literal across all source files (including the extensionless `WhatIsThis`) and diffing against the manifest. It is the only one missing. + +**Fix:** implement it, or drop the manifest entry. + +### The startup log lies + +[extension.js:929](../extension.js#L929) prints: + +``` +Commands registered: echocode.readErrors, echocode.annotate, … +``` + +`echocode.readErrors` is not registered (above). The line is a hardcoded string, not generated from what was actually registered, so it will drift again. + +**Fix:** build it from the real registrations, or delete it. + +## Inconsistencies + +### `echocode.rate` is declared but never read + +`package.json` declares `echocode.rate` ("Speech rate for text-to-speech"). `speechHandler.js` reads **`echocode.speechSpeed`**, which is what **Ctrl+Alt+U** / **Ctrl+Alt+D** persist. + +So a user setting `rate` sees no effect. `rate` is documented and inert; `speechSpeed` is live and undocumented in the manifest. + +**Fix:** pick one. Reading `rate` as the initial value and persisting speed changes to it would honour both. + +### Stale ids in the student-mode denylist + +`STUDENT_LOCKED_COMMANDS` in `guard.js` contains ids that no longer exist: + +``` +code-tutor.Annotate +code-tutor.speakNextAnnotation +code-tutor.readAllAnnotation +echocode.voiceInput +``` + +Harmless β€” a `Set` miss costs nothing β€” but misleading when reading the list to work out what is locked. + +### Big-O commands use the `code-tutor.` prefix + +`code-tutor.analyzeBigO`, `code-tutor.iterateBigOQueue`, `code-tutor.readEntireBigOQueue` β€” every other command is `echocode.*`. A leftover from an earlier name. Renaming is a breaking change for anyone who has remapped them. + +### `Ctrl+Alt+Space` is declared twice + +Two identical `contributes.keybindings` entries for `echocode.toggleVoice` with the same `when`. VS Code resolves to the last; the duplicate is redundant. + +### `echocode.initializeFolderList` has an empty `key` + +It has a keybindings entry with no key, making it Palette-only. Either give it a chord or drop the entry. + +## Fragile by construction + +### A source file with no extension + +`program_features/WhatIsThis/WhatIsThis` has no `.js` suffix, and the loader references it as `moduleFile: "WhatIsThis"`. `require()` tolerates it because Node tries the literal filename first. + +It works, but linters, bundlers, coverage tools, and any `find -name '*.js'` skip it silently. It is genuinely easy to conclude `echocode.readCurrentLine` is unregistered. + +**Fix:** rename to `WhatIsThis.js` and update `moduleFile`. + +### `npm run lint` does not run + +ESLint 9 requires `eslint.config.js`; the repo has none. `.gitignore` also ignores `**/eslint.config.mjs`, which would block the obvious fix. + +### `ci.yml` is a check that cannot fail + +```yaml + - name: Say Hello + run: echo "Hello World!" + - name: Install Dependencies + run: npm install say +``` + +The whole job. `main.yml` calls it on every push to `main`/`dev` and every PR, presenting a green check that verifies nothing. See [Testing and CI](Testing-and-CI). + +### Hot reload fires on your own edits + +The `UserImplementation/**` watcher is workspace-relative, so developing EchoCode in its own checkout triggers feature reloads while you type. Working as designed, surprising in practice. + +### A breakpoint looks identical to a broken extension + +A breakpoint in an extension-host file pauses the whole extension. It presents as EchoCode failing to start, with no error anywhere. Check breakpoints before debugging an activation failure. + +## Platform gaps + +### Voice capture has no Linux path + +`whisperService.js` branches on Windows (`dshow`) and macOS (`avfoundation`). There is no Linux branch, so voice input does not work there. Everything else does. + +### The venv can bind to an unsuitable Python + +`findSystemPython()` prefers 3.12 β†’ 3.11 β†’ 3.10 β†’ `python3`. On macOS the last resort is Xcode's bundled **Python 3.9**, which current `faster-whisper` wheels may not support. A venv created against it can fail at install time in a way that looks like a network problem. + +Also: a venv's interpreter is a **symlink**. `fs.existsSync` follows symlinks, so if Xcode is updated or the Command Line Tools are reinstalled, the link dangles and EchoCode rebuilds the whole venv. That is one way "it was fine, then it got slow" happens on a Mac. + +### Windows GPU detection is best-effort + +`Win32_VideoController.AdapterRAM` is capped at 4GB and wrong on modern cards, so the name is used for display only and RAM drives model sizing. Also the slowest probe in the hardware scan β€” PowerShell cold start alone is 1–2s. + +## Behavioural things that surprise people + +### Reading a line drops indentation + +`readCurrentLine` speaks `line.text.trim()`. For Python, where indentation is semantic, that is real information loss. **Ctrl+Alt+E W** partly compensates by describing position in context. + +### "Describe this line" is Python-only + +`describePythonLine` is rule-based pattern matching, ~140 lines. Other languages get much less. Instant and offline, but not general. + +### Rescanning an assignment is non-deterministic + +Completion detection is a model comparing prose requirements against code, so the same inputs can give different answers across runs. `markTaskComplete` is the deterministic override. + +### Assignment progress does not persist + +Task list and completion are module-level state. A window reload clears them. `workspaceState` keyed by assignment path would be the fix. + +### The hotkey guide is hand-maintained + +The spoken list in `hotkeyGuide.js` is not generated from `package.json`. Change a keybinding and the guide silently drifts β€” and for a blind user a wrong shortcut is a worse failure than a stale README. No test catches it. + +### Voice mode resets on reload + +`currentVoiceMode` is module state in `extension.js`, so it returns to Chat after any reload. Not persisted. + +## Leftover files + +A stale ~620KB `external_commands.json` may sit in `Core/program_settings/program_settings/`. It was tracked before the registry moved to global storage, is now ignored, and nothing reads it. Safe to delete. diff --git a/wiki/Line-and-Character-Read-Out.md b/wiki/Line-and-Character-Read-Out.md new file mode 100644 index 0000000..72c2d54 --- /dev/null +++ b/wiki/Line-and-Character-Read-Out.md @@ -0,0 +1,135 @@ +# Line and Character Read-Out + +## For users + +Three ways to hear what is under or around your cursor. These are the features you will use most often, and two of them work in Student mode. + +### Read the current line β€” Ctrl+Alt+L + +Speaks the current line exactly as written. Available in both modes. + +If the line is empty you hear "The current line is empty" rather than silence, so you can tell the difference between a blank line and speech failing. + +### Explain the current line β€” Ctrl+Alt+K + +Describes what the line *does* in plain language instead of reading it verbatim. **Dev mode only.** + +For a Python line like: + +```python +for i in range(len(items)): +``` + +you hear a description of a loop over the indices of `items`, rather than the punctuation read aloud. + +Useful when a line is syntactically dense β€” a comprehension, a chained call, a decorator β€” and hearing the characters would not tell you much. + +### Character read-out β€” Ctrl+Alt+R + +Toggles a mode where **every character you type is spoken back**. Available in both modes. + +This is proofreading support. Typing an identifier or a string literal, the read-back catches a wrong character immediately rather than at compile time. + +It is loud by design. Toggle it off with the same shortcut when you are done. **Ctrl+Alt+X** stops speech if it gets ahead of you. + +### Which to use when + +| Situation | Use | +|---|---| +| Where am I in this line? | **Ctrl+Alt+L** | +| What does this line mean? | **Ctrl+Alt+K** | +| Did I type that correctly? | **Ctrl+Alt+R** | +| Where am I in the file? | **Ctrl+Alt+E W** β€” see [Code Navigation](Code-Navigation) | + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Line and Character Read-Out β€” Level 0 context diagram](diagrams/svg/06-line-character-readout-L0.svg) + +*Editable source: `wiki/diagrams/06-line-character-readout.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Line and Character Read-Out β€” Level 1 decomposition diagram](diagrams/svg/06-line-character-readout-L1.svg) + +*Editable source: `wiki/diagrams/06-line-character-readout.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Provides | Command | +|---|---|---| +| `program_features/WhatIsThis/WhatIsThis` | `registerReadCurrentLineCommand` | `echocode.readCurrentLine` | +| `program_features/WhatIsThis/DescribeThis.js` | `registerDescribeCurrentLineCommand` | `echocode.describeCurrentLine` | +| `program_features/WhatIsThis/CharacterReadOut.js` | `registerCharacterReadOutCommand` | `echocode.toggleCharacterReadOut` | + +### A file with no extension + +`program_features/WhatIsThis/WhatIsThis` has **no `.js` suffix**, and the loader references it that way: + +```js +const whatIsThisModule = loadProgramFeatureModule({ + featureKey: "whatIsThis", + featureFolder: "WhatIsThis", + moduleFile: "WhatIsThis", // no extension + requiredExports: ["registerReadCurrentLineCommand"], +}); +``` + +`require()` tolerates this because Node tries the literal filename before appending extensions. It works, but it is fragile β€” most tooling (linters, bundlers, `find -name '*.js'`, coverage) skips it silently. It is easy to believe `echocode.readCurrentLine` is unregistered because a `*.js` search finds nothing. + +Renaming it to `WhatIsThis.js` and updating `moduleFile` would be a safe cleanup. + +### Read current line + +The simplest feature in the codebase, and a good template: + +```js +const cursorPosition = editor.selection.active; +const currentLine = editor.document.lineAt(cursorPosition.line); +const lineText = currentLine.text.trim(); +``` + +Note it speaks the **trimmed** text, so indentation is not conveyed. For a language where indentation is semantic β€” Python β€” that is a real information loss. `Ctrl+Alt+E W` partly covers it by describing position in context. + +### Describe current line + +`describePythonLine(lineText, lineNumber, document)` is a **rule-based Python describer**, not an AI call β€” roughly 140 lines of pattern matching that recognises loops, conditionals, assignments, definitions, imports, and so on. + +Two consequences: + +- **It is instant and works offline**, with no model configured. +- **It is Python-specific.** Other languages get much less. The function name says so. + +This is the natural place to add another language: a `describeJavaScriptLine` alongside it and a dispatch on `document.languageId`. Routing to `AIrequest` for unrecognised lines would also work, at the cost of the instant response. + +### Character read-out + +The one feature that does not work through commands. It subscribes to document changes: + +```js +function startListeningForChanges() // subscribe to onDidChangeTextDocument +function stopListeningForChanges() // dispose the subscription +function readCharacterToLeft() // speak the character just typed +function toggleCharacterReadOut() // flip state, start/stop listening +``` + +`readCharacterToLeft` reads the character to the left of the cursor after a change, which is the one just inserted. + +Two things to preserve: + +- **The listener is disposed when toggled off**, not merely flagged inactive. An always-subscribed handler runs on every keystroke in every document for the whole session. +- **Speech interrupts.** Fast typing means each character cuts off the previous one, so you effectively hear the most recent character. That is correct for this feature β€” a queue would fall seconds behind. See [Speech Handler](Speech-Handler). + +### Registration and swapping + +All three modules are loaded under the `whatIsThis` feature key, so `echocode.featureImplementation.whatIsThis` swaps all of them together β€” they share one `UserImplementation/` folder. You cannot replace only the describer. See [Custom Feature Implementations](Custom-Feature-Implementations). + +Each declares its `requiredExports` to the loader, so a user implementation missing an export falls back to built-in with a logged reason rather than failing at call time. diff --git a/wiki/Modes-and-Guidance.md b/wiki/Modes-and-Guidance.md new file mode 100644 index 0000000..c14d707 --- /dev/null +++ b/wiki/Modes-and-Guidance.md @@ -0,0 +1,199 @@ +# Modes and Guidance + +## For users + +Two independent dials. **Mode** controls *which* features you can use. **Guidance level** controls *how much* EchoCode says. + +## Student mode and Dev mode + +EchoCode starts in **Student mode**. + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+9** | Toggle between modes | + +Also available as **EchoCode: Switch to Student Mode** / **Switch to Developer Mode** in the Command Palette. + +The mode change is announced aloud, with a distinct audio ping for each β€” so you can tell which mode you are in without waiting for words. + +### What Student mode locks + +Student mode disables the features that would do a student's work for them: + +- The [Chat Tutor](Chat-Tutor) and voice input to it +- All [Code Summaries](Code-Summaries) +- [Annotations and Big-O](Annotations-and-Big-O) +- Line explanations (**Ctrl+Alt+K**) β€” but **not** line reading (**Ctrl+Alt+L**) +- [Error Reading](Error-Reading) for Python and C++ +- The [File Connector](File-Connector) +- The [Assignment Tracker](Assignment-Tracker) + +### What always works + +The accessibility core is never locked, because it is not a shortcut to an answer β€” it is how you read code at all: + +- Read current line (**Ctrl+Alt+L**) +- Character read-out (**Ctrl+Alt+R**) +- All [navigation](Code-Navigation) β€” functions, files, folders, where-am-I +- File and folder creation +- Speech speed, stop speech, the hotkey guide + +### If a shortcut seems dead + +Check your mode first. A locked shortcut does nothing at all β€” no error, no speech β€” because VS Code does not even dispatch it. Press **Ctrl+Alt+9** and try again. + +If you invoke a locked command from the Command Palette instead, EchoCode tells you it is unavailable in Student mode. + +### Which mode should you use? + +Using EchoCode as a working developer: **Dev mode**. Nothing in Student mode benefits you. + +Using it as a student: whichever your course expects. Student mode is a self-discipline tool, not a security boundary β€” you can switch it off in one keystroke. + +## Guidance level + +Controls the verbosity of every explanation β€” errors, annotations, summaries. + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+Z** | Cycle: guided β†’ balanced β†’ concise | +| **Ctrl+Alt+Shift+Z** | Pick from a list | + +| Level | You hear | +|---|---| +| **guided** | The finding, then "Next, …" and "Then, …" β€” a coaching checklist | +| **balanced** *(default)* | The finding, why it matters, and one step | +| **concise** | Location and one sentence. Nothing else. | + +`concise` is for when you know the codebase and want the location, fast. `guided` is for learning something unfamiliar. `balanced` is a reasonable default. + +This is independent of mode β€” you can be in Dev mode with `guided`, or Student mode with `concise`. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Modes and Guidance β€” Level 0 context diagram](diagrams/svg/12-modes-guidance-L0.svg) + +*Editable source: `wiki/diagrams/12-modes-guidance.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Modes and Guidance β€” Level 1 decomposition diagram](diagrams/svg/12-modes-guidance-L1.svg) + +*Editable source: `wiki/diagrams/12-modes-guidance.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### Files + +| File | Responsibility | +|---|---| +| `Core/program_settings/mode.js` | Mode state and context keys (~31 lines) | +| `Core/program_settings/guard.js` | The locked-command list and wrapper (~90 lines) | +| `Core/program_settings/modeAudio.js` | Mode-change audio cues (~63 lines) | +| `Core/program_settings/guide_settings/guidanceLevel.js` | Verbosity formatting (~97 lines) | + +### Mode is enforced twice, on purpose + +**Layer 1 β€” context keys, so locked shortcuts never dispatch:** + +```js +async function refreshModeContext() { + const isDev = getMode() === "dev"; + await vscode.commands.executeCommand("setContext", "echocode:isDev", isDev); + await vscode.commands.executeCommand("setContext", "echocode:isStudent", !isDev); + return mode; +} +``` + +Keybindings carry `when: "… && echocode:isDev"`, so VS Code filters them out entirely. Nothing needs to reject them. + +**Layer 2 β€” the `guard()` wrapper, for everything else:** + +```js +function guard(commandId, handler) { + return (...args) => { + if (!isAllowed(commandId)) { + // inform the user, speak it, and do not run the handler + return; + } + return handler(...args); + }; +} +``` + +Both layers are needed. Context keys only gate **keybindings** β€” the Command Palette, voice commands, and `executeCommand` calls from other extensions all bypass them. `guard()` is what covers those. + +When registering a lockable command, wrap it: + +```js +vscode.commands.registerCommand("echocode.foo", guard("echocode.foo", handler)) +``` + +The command id is passed twice because `guard` needs it for the lookup, independent of registration. + +### The locked list + +`STUDENT_LOCKED_COMMANDS` is a `Set` of command ids in `guard.js`, grouped by category with comments explaining the rationale β€” "explanations = cheat risk", "can effectively solve". + +Two things to know: + +- **It is a denylist, so new commands are unlocked by default.** Adding an explanation feature means adding its id here; forgetting is a silent policy hole. +- **It contains stale ids** β€” `code-tutor.Annotate`, `code-tutor.speakNextAnnotation`, `code-tutor.readAllAnnotation`, `echocode.voiceInput` β€” that no longer exist. Harmless (a `Set` miss costs nothing) but misleading when reading the list. + +`isStudentMode()` is `getMode() !== "dev"`, so any unexpected value fails **closed** into Student mode. That is the right default for a policy check. + +### Mode state + +```js +function getMode() // reads echocode.mode, default "student" +function onModeChange(handler) // subscribe +``` + +Backed by the `echocode.mode` setting, so it persists and syncs. `onModeChange` lets features react; `extension.js` uses it to refresh context keys and announce. + +### Audio cues + +`modeAudio.js` plays a distinct ping per mode via `sound-play`, from `audio_pings/`: + +```js +function enqueue(fn) // serialise playback +function pingOnce() +function announceMode(mode, outputChannel) +``` + +`enqueue` exists because overlapping playback on a rapid double-toggle sounds like a glitch. The ping precedes the spoken announcement so the mode is identifiable before the sentence finishes β€” the same reasoning as the empty-queue tone in [Annotations](Annotations-and-Big-O). + +### Guidance formatting + +`formatHelpByGuidance(input)` is the single formatter every explanation feature routes through. It accepts a structured input and renders per level: + +```js +{ + where, // location, e.g. "Line 42" + summary, + detail (or legacy: raw), + why (or legacy: ruleHint), + steps (or legacy: suggestions) +} +``` + +Both field sets are accepted β€” `detail ?? raw`, `why ?? ruleHint`, `steps ?? suggestions` β€” because older call sites use the legacy names. Keep the aliases if you refactor, or those callers silently lose content. + +Rendering: + +| Level | Composition | +|---|---| +| `guided` | where + summary(180) + "Next, " step1 + "Then, " step2 | +| `balanced` | where + summary(200) + why(160) + step1(200) | +| `concise` | where + first sentence of detail (220) | + +Character caps per field, via `trim(t, max)`. Those are **speech-duration budgets**, not layout constraints β€” a 400-character sentence is exhausting to listen to and cannot be skimmed. `firstSentence(t)` is how `concise` gets exactly one sentence. + +A new explanation feature should build this structured object and call `formatHelpByGuidance`, rather than formatting its own strings. That is what keeps verbosity consistent across features and makes `Ctrl+Alt+Z` affect everything at once. diff --git a/wiki/Settings-Reference.md b/wiki/Settings-Reference.md new file mode 100644 index 0000000..ca5b584 --- /dev/null +++ b/wiki/Settings-Reference.md @@ -0,0 +1,122 @@ +# Settings Reference + +## For users + +All settings live under the `echocode.` prefix. Open **Settings** and search for "echocode", or edit `settings.json` directly. + +Several are written for you by **EchoCode: Select AI Provider & Model** β€” you rarely need to set those by hand. + +### Speech and presentation + +| Setting | Default | What it does | +|---|---|---| +| `echocode.voice` | `""` | Voice name for text-to-speech. Blank uses your system default. On Windows EchoCode tries `Microsoft Zira Desktop` when blank. | +| `echocode.rate` | `1` | Speech rate. `0.5` is slow, `1.5` is fast. | +| `echocode.speechSpeed` | `1` | Live speed, changed by **Ctrl+Alt+U** / **Ctrl+Alt+D**. Written automatically. | +| `echocode.guidanceLevel` | `balanced` | How much detail explanations carry: `guided`, `balanced`, or `concise`. See [Modes and Guidance](Modes-and-Guidance). | + +`voice` and `rate` are the declared settings; `speechSpeed` is what the speed shortcuts actually persist. If you set a rate and it seems ignored, check `speechSpeed`. + +### Mode + +| Setting | Default | What it does | +|---|---|---| +| `echocode.mode` | `student` | `student` locks AI and explanation features. `dev` enables everything. | + +### AI backend + +| Setting | Default | What it does | +|---|---|---| +| `echocode.aiProvider` | `""` | Which backend to use: `copilot`, `ollama`, or `api`. Blank falls back to the legacy toggle below. | +| `echocode.useLocalOllama` | `false` | Legacy. Only consulted when `aiProvider` is blank. Prefer `aiProvider`. | +| `echocode.ollamaBaseUrl` | `http://127.0.0.1:11434` | Your Ollama server. Can point at another machine. | +| `echocode.ollamaModel` | `llama3.2` | Model name for Ollama. | +| `echocode.copilotModel` | `""` | Preferred Copilot model id. Blank auto-selects. | +| `echocode.apiProvider` | `""` | Hosted API preset: `openai`, `openrouter`, `groq`, `anthropic`, or `custom`. | +| `echocode.apiBaseUrl` | `""` | Full base URL including version path, e.g. `https://api.openai.com/v1`. Blank uses the preset default. | +| `echocode.apiModel` | `""` | Model id to send to the hosted API. | + +**Your API key is never stored in settings.** It goes into VS Code's secret storage, which is backed by your OS keychain. There is deliberately no setting for it β€” see [AI Providers and Models](AI-Providers-and-Models). + +### Feature implementation swapping + +Eight settings of the form `echocode.featureImplementation.`, each `builtin` (default) or `user`: + +`annotationsBigO`, `assignmentTracker`, `errorParser`, `chatBot`, `fileConnector`, `folderFileCreator`, `voice`, `whatIsThis` + +Set one to `user` and EchoCode loads your own code from that feature's `UserImplementation/` folder instead of the built-in version. This is a development feature β€” see [Custom Feature Implementations](Custom-Feature-Implementations). If your replacement is missing or broken, EchoCode silently falls back to the built-in one and logs why. + +--- + +## For developers + +### Where settings are declared and read + +Declared in `package.json` under `contributes.configuration.properties`. Read through the standard API: + +```js +const config = vscode.workspace.getConfiguration("echocode"); +config.get("ollamaModel", "llama3.2"); +``` + +Written with an explicit global target: + +```js +await config.update(key, value, vscode.ConfigurationTarget.Global); +``` + +`aiProviderSetup.js` wraps that as `saveGlobal()`. Global is deliberate β€” an AI backend choice is a property of the machine, not the folder you happen to have open. + +### The provider resolution chain + +`getAiSettings()` in `Core/program_settings/program_settings/AIrequest.js` is the single place backend choice is resolved: + +```js +const useLocalOllama = config.get("useLocalOllama", false); +const explicitProvider = config.get("aiProvider", ""); +const provider = explicitProvider || (useLocalOllama ? "ollama" : "copilot"); +``` + +`aiProvider` is canonical. `useLocalOllama` predates it and is honoured only when `aiProvider` is blank, so installs that predate the newer setting keep working across an update without the user touching anything. **Keep that fallback** if you refactor β€” removing it silently reverts those users to Copilot. + +Note the final default: with both unset, the provider is `copilot`. + +### Secrets are not settings + +API keys go through `Core/program_settings/program_settings/secretStore.js`, which wraps `context.secrets` (the OS keychain): + +```js +function keyName(providerId) { + return `echocode.apiKey.${providerId || "custom"}`; +} +``` + +`initSecretStorage(context)` must be called during activation before any provider code reads a key. `isAvailable()` reports whether storage exists at all, and the hosted-API flow refuses to proceed without it rather than falling back to plaintext. + +Settings files are plaintext, frequently committed, and sync across machines β€” which is exactly why there is no key setting. Do not add one. + +### Reacting to changes + +`extension.js` subscribes to configuration changes, but only watches the feature-implementation keys: + +```js +vscode.workspace.onDidChangeConfiguration((event) => { + for (const featureKey of RELOADABLE_FEATURE_KEYS) { + const settingKey = `echocode.featureImplementation.${featureKey}`; + if (event.affectsConfiguration(settingKey)) { + scheduleFeatureReload(featureKey, "configuration changed"); + } + } +}); +``` + +Reloads are debounced 150ms per feature, because a settings edit can fire several events in quick succession. + +Other settings are read on demand rather than cached, so they take effect on next use without a reload. The exception is `speechSpeed`, held in a module-level variable in the speech handler and refreshed by `loadSavedSpeechSpeed()`. + +### Adding a setting + +1. Declare it in `package.json` with a `type`, `default`, and a `description` a screen reader can read usefully. +2. Read it with `config.get(key, default)` β€” pass the default at every call site rather than relying on the manifest, so the code still behaves if the manifest drifts. +3. If it needs to take effect immediately, extend the `onDidChangeConfiguration` handler; otherwise on-demand reads are fine. +4. If it is machine-scoped, write it with `ConfigurationTarget.Global`. diff --git a/wiki/Speech-Handler.md b/wiki/Speech-Handler.md new file mode 100644 index 0000000..7ce8d41 --- /dev/null +++ b/wiki/Speech-Handler.md @@ -0,0 +1,172 @@ +# Speech Handler + +> **Developer page.** Users never interact with this directly β€” they just hear the result. The only user-visible controls are **Ctrl+Alt+U** / **Ctrl+Alt+D** (speed) and **Ctrl+Alt+X** (stop), documented in [Keyboard Shortcuts](Keyboard-Shortcuts). + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Speech Output / TTS β€” Level 0 context diagram](diagrams/svg/14-speech-output-L0.svg) + +*Editable source: `wiki/diagrams/14-speech-output.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Speech Output / TTS β€” Level 1 decomposition diagram](diagrams/svg/14-speech-output-L1.svg) + +*Editable source: `wiki/diagrams/14-speech-output.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +## What it is + +`Core/program_settings/speech_settings/speechHandler.js` β€” ~327 lines. Every spoken word in EchoCode goes through it, from **116 call sites across 26 files**. + +That call-site count is the important fact about this module: any behaviour change here affects the entire extension at once, and there is no per-feature override. It is also why fixes belong here rather than at call sites. + +## Public surface + +```js +speakMessage(message) // speak, interrupting anything in progress +stopSpeaking() // stop current + discard queued; returns whether anything stopped +isCurrentlySpeaking() // for tests +increaseSpeechSpeed() // +0.1, capped at 2.0 +decreaseSpeechSpeed() // -0.1, floored at 0.5 +getSpeechSpeed() +loadSavedSpeechSpeed() // read echocode.speechSpeed into module state +registerSpeechCommands(context, outputChannel) +``` + +## The central design decision: interrupt, never overlap + +A new message **interrupts** the one in progress. It does not queue behind it. + +That is the right semantics for this extension. Arrowing down a list emits one announcement per row; a queue would fall seconds behind the cursor and narrate rows the user already passed. A stale announcement is worse than a truncated one. + +But interrupting `say.js` naively produces **two voices talking at once**, which is the failure this module exists to prevent. + +## Why say.js cannot be interrupted naively + +The `say` package exports a **singleton holding a single child-process handle**, and `speak()` overwrites it unconditionally: + +```js +// node_modules/say/platform/base.js +this.child = childProcess.spawn(command, args, options) +``` + +So calling `speak()` while audio is playing **orphans the previous process** β€” the handle is gone, nothing can ever kill it, and it talks to completion alongside the new one. say.js flags this in its own source: + +```js +// TODO: If two messages are being spoken simultaneously, childD points to +// new instance, no way to kill previous +``` + +And `stop()` makes it worse β€” it nulls the handle and calls back *before the process has exited*: + +```js +stop (callback) { + this.runStopCommand() + this.child = null + callback(null) // returns before the process is dead +} +``` + +On Windows `runStopCommand` fires `taskkill` through an **async** `exec`, so `stop()` returns before the kill has even started while SAPI keeps talking. + +The old implementation did exactly `if (isSpeaking) stopSpeaking()` then immediately `say.speak(...)`. That is the overlap recipe verbatim. Measured with a faithful mock: **30 messages in quick succession produced 29 overlaps and 30 simultaneous processes.** + +## The invariant that fixes it + +> **Never spawn while a child is alive.** + +An interrupt kills the current utterance and *parks* the new one until the dying process reports `exit`. say.js surfaces that as the speak callback, which is the only trustworthy signal that audio has actually ceased. + +Same measurement after: **0 overlaps, 2 processes** (first and last). + +## State + +```js +let activeUtterance = null; // { id, resolve, interrupted? } β€” null when silent +let pendingUtterance = null; // at most one; each new message supersedes the last +let utteranceId = 0; // distinguishes an utterance from its successor +let handoffTimer = null; // set while waiting on a dying process +``` + +`pendingUtterance` holds **at most one** entry. Several messages arriving during a handoff each supersede the previous, because either would interrupt the other anyway β€” only the newest is worth speaking. + +## Flow + +``` +speakMessage(text) + β”œβ”€β”€ pendingUtterance already set? resolve it (stale) and replace + β”œβ”€β”€ something speaking? β†’ interruptActive() [kill, then wait for exit] + └── silent? β†’ startPending() [spawn immediately] + +say.speak callback fires (normal end OR kill-induced exit) + β†’ retireActive() β†’ resolve promise β†’ queueMicrotask(startPending) +``` + +`retireActive` yields with `queueMicrotask` rather than recursing, so a backend failing synchronously does not run the whole backlog in one unbroken stack. + +## Four details that are easy to break + +**Stale callbacks are identified and ignored.** + +```js +if (activeUtterance !== utterance) return; +``` + +Identity comparison, not a flag. Without it, a late exit from a killed process clears the speaking flag and starts draining state that now belongs to a newer utterance β€” two utterances at once. + +**Every promise settles.** `stopSpeaking` resolves the in-flight entry as well as the pending one, so nothing awaiting `speakMessage` hangs forever. Features that await speech in a loop β€” the [annotations](Annotations-and-Big-O) read-all, the [error](Error-Reading) readout β€” would deadlock otherwise. + +**Interrupted utterances do not log an error.** say.js reports a kill as a failure, but that is the expected outcome of an interrupt: + +```js +if (err && !utterance.interrupted) console.warn("[EchoCode] say.speak error", err); +``` + +The old code spammed the console on every interrupt. + +**There is a 2s handoff timeout.** If a killed process never reports exit, `forceHandoff` proceeds anyway and logs it. Staying permanently mute is the worse failure for an accessibility tool β€” a small overlap risk is the right trade. + +## Voice resolution + +```js +function resolveVoice() { + const configured = config.get("voice") || null; + if (configured) return configured; + if (process.platform === "darwin") return null; // system default is good + if (process.platform === "win32") return "Microsoft Zira Desktop"; + return null; +} +``` + +Windows gets an explicit voice because the default SAPI voice is noticeably robotic. If Zira is not installed, say.js errors and the callback fires β€” degraded, not fatal. + +## Speed + +`speechSpeed` is a **module-level variable**, not read per call. `loadSavedSpeechSpeed()` pulls it from `echocode.speechSpeed` at activation; the increase/decrease commands mutate it and persist with `config.update`. + +Note the manifest also declares `echocode.rate`, which this module does not read. `speechSpeed` is the live value. That is a real inconsistency β€” see [Known Gaps](Known-Gaps). + +## Cost, and why nothing should await speech before UI + +Measured: **`say.speak`'s callback fires after ~5.2 seconds for one short sentence.** It resolves on *completion of playback*, not on start. + +That is why `speak()` in `aiProviderSetup.js` is fire-and-forget. Awaiting it held the quick pick closed for the full utterance at every step of setup β€” several seconds per step, for speech the user could interrupt anyway. + +**Rule: never `await speakMessage()` before rendering UI.** Await it only when sequencing matters, as in a multi-item read-out. + +## Testing it + +Do not call the real backend in tests β€” it is audible and adds seconds per case. Every suite replaces the module in `require.cache`: + +```js +nodeRequire.cache[speechHandlerPath] = { + id: speechHandlerPath, filename: speechHandlerPath, loaded: true, + exports: { speakMessage: async () => {} }, +}; +``` + +To test the handler itself, mock `say` instead β€” and model it faithfully: one overwritable child handle, and a `stop()` that returns before the process dies. A naive mock where `stop()` is synchronous and instant will not reproduce the overlap bug, so it will not catch a regression. diff --git a/wiki/Testing-and-CI.md b/wiki/Testing-and-CI.md new file mode 100644 index 0000000..46ede53 --- /dev/null +++ b/wiki/Testing-and-CI.md @@ -0,0 +1,163 @@ +# Testing and CI + +> **Developer page.** + +## Running the tests + +```bash +npm test +``` + +51 tests, ~60ms. `pretest` runs `npm run compile` (`tsc -p ./`) first. + +```bash +npm run compile # tsc -p ./ β€” the TS scaffold, not the extension +npm run lint # BROKEN β€” see below +``` + +## What is covered + +| Suite | Covers | +|---|---| +| `aiProviderSetup.test.ts` | The provider wizard end to end β€” Copilot, Ollama, hosted API, failure paths | +| `aiRequestRouting.test.ts` | Backend selection in `AIrequest.js` | +| `voiceCommandRouter.test.ts` | Fuzzy matching, student-mode locking, routing decisions | +| `externalIntentRouter.test.ts` | External command matching | +| `rootExtension.integration.test.ts` | `activate()` with every dependency stubbed | +| `annotationsCommands.test.ts`, `extension.test.ts` | Command registration | + +Not covered: speech internals, the venv bootstrap, the C++ parser, the File Connector, anything needing a real editor. + +## The harness + +``` +node --loader ./test/alias-loader.mjs mocha + --require ts-node/register + --require test/setup.mjs + --recursive test/**/*.test.ts +``` + +Three pieces: + +**`test/helpers/vscodeMock.js`** β€” a hand-written `vscode` API mock. Exports ESM-shaped members *and* attaches `globalThis.vscode`, because some code paths take each route. Provides `commands`, `window`, `workspace`, `env`, `Disposable`, `Position`/`Range`/`Selection`, and `__createMockContext()`. + +**`test/alias-loader.mjs`** β€” an ESM resolve hook mapping `import "vscode"` to the mock. + +**`test/setup.mjs`** β€” patches `Module.prototype.require` so `require("vscode")` also resolves to the mock. + +Both interception routes are needed: the mock must be reachable whether a module is loaded as ESM or CommonJS. + +### Mocking pattern + +Tests inject dependencies by writing into `require.cache` before loading the module under test: + +```js +nodeRequire.cache[modulePath] = { + id: modulePath, filename: modulePath, loaded: true, + exports: { speakMessage: async () => {} }, +}; +delete nodeRequire.cache[targetModulePath]; +const target = nodeRequire(targetModulePath); +``` + +That requires a **real CommonJS `require`**, because only the CJS loader consults `require.cache`. Which leads to the harness's one genuinely tricky constraint. + +## The dual-runtime constraint + +Read this before touching any test file. + +The test files must load under two **incompatible** Node behaviours: + +| | Node 20/22 | Node 24 | +|---|---|---| +| Who loads `.ts` | `ts-node` β†’ **CommonJS** | native type-strip β†’ **ESM** | +| `require` | exists | **undefined** | +| `import.meta` | **TS1470 compile error** | fine | + +`tsconfig.json` has `module: Node16` and `package.json` has no `"type": "module"`, so ts-node emits CommonJS β€” where `import.meta` is a hard compile error. Node 24 strips types natively and infers ESM from the `import` statements β€” where `require` does not exist. + +So both of the obvious fixes are wrong: + +- `const nodeRequire = require` β†’ works on 20/22, **fails on 24** +- `createRequire(import.meta.url)` β†’ works on 24, **fails on 20/22** with TS1470 + +The working form uses no meta-property at all: + +```js +import { createRequire } from "module"; +const nodeRequire = createRequire(path.join(process.cwd(), "package.json")); +``` + +`createRequire` only needs a resolution base. A plain path compiles and runs on both. + +Every test file carries a comment explaining this. **Keep it** β€” the next person to "clean up" `createRequire(import.meta.url)` breaks CI, which has already happened once. + +### Verifying across versions locally + +```bash +npx --yes node@20 -e "console.log(process.execPath)" # get a Node 20 binary +``` + +Then run mocha with it directly. A compile check alone is not enough β€” reproduce the CI error with: + +```bash +npx tsc --noEmit --module Node16 --moduleResolution Node16 --target ES2022 --skipLibCheck test/*.test.ts +``` + +## CI + +Four workflows in `.github/workflows/`: + +| Workflow | Triggers | Does | +|---|---|---| +| `echocode-tests.yml` | push + PR, all branches | **The real test job.** Matrix: Node 20, 22, 24 | +| `release.yml` | release | Tests on Node 20, then publishes | +| `main.yml` | push to main/dev, PR | Calls `ci.yml` | +| `ci.yml` | `workflow_call` | **Runs no tests** β€” see below | + +Tests run under `xvfb-run -a` because some paths touch a display. + +### Why the matrix + +```yaml +strategy: + fail-fast: false + matrix: + node-version: [20, 22, 24] +``` + +Because of the dual-runtime constraint above β€” code can be correct on one major and a hard failure on another. Node 20 is closest to what ships (VS Code 1.99's extension host bundles Node 20 via Electron), while contributors on a current install hit the Node 24 behaviour. + +`fail-fast: false` matters: cancelling siblings on the first failure hides which majors are affected, which is the whole point. + +`release.yml` stays pinned to a single version deliberately β€” a release should build on one known runtime. + +### ci.yml verifies nothing + +```yaml + - name: Say Hello + run: echo "Hello World!" + - name: Install Dependencies + run: npm install say +``` + +That is the entire job. It echoes a string and installs one package. `main.yml` calls it on every push to `main`/`dev` and every PR, so it presents as a green check that **cannot fail**. + +A green check that can't fail is worse than no check β€” it reads as coverage. Either delete it and `main.yml` (since `echocode-tests.yml` already runs on all branches), or make it a real Windows test job. Windows is where the speech and voice paths diverge most, so genuine Windows coverage has real value. + +## Known harness issues + +**`npm run lint` is broken.** ESLint 9 requires `eslint.config.js` and the repo has none β€” it still has the old `.eslintrc` style, and `.gitignore` even ignores `**/eslint.config.mjs`. The `lint` script fails immediately. + +**A `MODULE_TYPELESS_PACKAGE_JSON` warning** appears on every run for `vscodeMock.js` β€” Node cannot tell its module type and re-parses it as ESM. Harmless, but it is the same ambiguity that caused the dual-runtime problem. Adding `"type": "commonjs"` to `package.json` would silence it, but needs checking against the extension's own loading first. + +**`__createMockContext()` was missing `globalStorageUri`** until recently β€” real VS Code always provides it. If you add a feature that writes to a context path, check the mock supplies it; a missing field surfaces as a confusing `undefined` deep in a path join. + +## Writing a test + +1. `import "./helpers/vscodeMock.js"` first β€” it must load before anything requiring `vscode`. +2. Use the `createRequire(path.join(process.cwd(), "package.json"))` form. +3. Stub the speech handler. The real one is audible and adds seconds per case. +4. Inject dependencies through `require.cache`, and restore them in a `finally`. +5. Save and restore any `globalThis.vscode` members you override β€” suites share one mock instance. +6. Verify on Node 20 as well as your local version. diff --git a/wiki/Voice-Input.md b/wiki/Voice-Input.md new file mode 100644 index 0000000..35abbf6 --- /dev/null +++ b/wiki/Voice-Input.md @@ -0,0 +1,218 @@ +# Voice Input + +## For users + +EchoCode can listen to you. Speech is transcribed **locally** β€” the audio never leaves your machine β€” and then routed one of three ways depending on the current voice mode. + +### The three modes + +| Mode | What your speech becomes | +|---|---| +| **Chat Tutor** | A message to the [Chat Tutor](Chat-Tutor) | +| **Code Generation** | Code inserted at your cursor | +| **Command** | A VS Code command to run | + +Press **Ctrl+Alt+'** to cycle modes. The new mode is announced aloud. + +Press **Ctrl+Alt+Space** to start recording, and again to stop. On stop, EchoCode transcribes and routes according to the mode. + +You can also skip the mode system and use a one-shot shortcut: + +| Shortcut | Does | +|---|---| +| **Ctrl+Alt+Shift+T** | Record once β†’ tutor | +| **Ctrl+Alt+Shift+C** | Record once β†’ code at cursor | +| **Ctrl+Alt+Shift+V** | Record once β†’ run as command | + +### One-time setup + +Voice needs a local speech-to-text engine. The first time it is used, EchoCode offers to set up an isolated Python environment and install `faster-whisper`. Say yes and it handles the rest. + +This is a real download β€” several hundred megabytes of dependencies β€” and takes a few minutes. It happens once. The progress notification stays on screen; you can keep working. + +You need Python 3.10 or newer on your PATH. EchoCode prefers newer versions (3.12, then 3.11, then 3.10, then plain `python3`). On macOS, the bundled Xcode `python3` is 3.9 and is only the last resort, because current `faster-whisper` wheels may not support it. + +If setup fails twice, EchoCode stops retrying and asks instead β€” a repeat failure is almost always something retrying will not fix (no compiler, no network, unsupported Python). The prompt offers **Retry Setup** once you have addressed it. + +You can decline permanently with **Don't ask again**. Every non-voice feature keeps working. + +### Choosing a microphone + +**EchoCode: Select Microphone** lists your input devices. On Windows this matters β€” EchoCode must name your device explicitly, and will report an error if it cannot find one. On macOS the default input from System Settings is used unless you pick otherwise. + +### What the Command mode understands + +Command mode matches what you said against two sets of commands: + +1. **EchoCode's own commands**, from a curated list of 28 entries with spoken phrasings β€” "toggle annotations", "summarize function", "read annotations", and so on. +2. **Every command VS Code knows about**, with keywords generated from the command id. "toggle sidebar visibility" finds `workbench.action.toggleSidebarVisibility`. + +Matching is fuzzy and tolerant of transcription errors. "pleese creat fyle" still finds **Create File**. Short phrases need a closer match than long ones, so a single mumbled word does not fire something unexpected. + +If what you said looks like a question β€” it starts with "what", "why", "how", "explain", and so on β€” it is routed to the tutor instead of being matched as a command, even in Command mode. Asking "how do I open a terminal?" gets you an answer rather than a terminal. + +If nothing matches and it is not a question, Code Generation mode will try to write code from it. + +### If voice does not work + +Check **View β†’ Output β†’ EchoCode**: + +- `[Voice] Error: No microphone found` β€” pick a device with **Select Microphone** +- `[ffmpeg] Spawning with args: …` β€” recording started; if nothing follows, the capture device is wrong +- `[Python-Whisper] …` β€” the transcriber's own log +- `[DependencyManager] …` β€” environment setup, with the duration of every command + +Empty or noise-only recordings are discarded rather than sent anywhere. + +### Data flow β€” Level 0 (context) + +What goes in, what comes out, and who it talks to. + +![Voice Input β€” Level 0 context diagram](diagrams/svg/01-voice-input-L0.svg) + +*Editable source: `wiki/diagrams/01-voice-input.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + +--- + +## For developers + +### Data flow β€” Level 1 (decomposition) + +The same feature opened up into its numbered sub-processes. + +![Voice Input β€” Level 1 decomposition diagram](diagrams/svg/01-voice-input-L1.svg) + +*Editable source: `wiki/diagrams/01-voice-input.drawio` β€” generated locally, see `wiki/diagrams/README.md`* + + +### The pipeline + +``` +Ctrl+Alt+Space + β†’ extension.js dispatches by currentVoiceMode + β†’ whisperService.startRecording() ffmpeg β†’ 16kHz mono WAV in tmp + β†’ (second press) stopAndTranscribe() + β†’ runLocalWhisper() spawn python local_whisper_stt.py + β†’ transcript string + β†’ voiceCommandRouter.tryExecuteVoiceCommand() + β”œβ”€β”€ internal command match β†’ executeCommand + β”œβ”€β”€ external command match β†’ executeCommand + β”œβ”€β”€ looks like a question β†’ chat provider + └── otherwise β†’ generateCodeFromVoice β†’ insert at cursor +``` + +### Files + +| File | Responsibility | +|---|---| +| `program_features/Voice/whisperService.js` | Device enumeration, ffmpeg capture, Whisper invocation | +| `program_features/Voice/local_whisper_stt.py` | Transcription; prints transcript on stdout, logs on stderr | +| `program_features/Voice/voiceCommandRouter.js` | Fuzzy matching and routing | +| `program_features/Voice/dependencyManager.js` | Python venv and `faster-whisper` bootstrap | +| `Core/program_settings/program_settings/ExternalIntentRouter.js` | The all-of-VS-Code command registry | +| `Core/program_settings/program_settings/voice_commands.json` | 28 curated EchoCode commands with spoken keywords | + +### Audio capture + +`ffmpeg-static` supplies the binary, so there is no system ffmpeg dependency. Platform arguments differ: + +```js +// Windows β€” device must be named explicitly +["-f", "dshow", "-i", `audio=${micName}`, "-ac", "1", "-ar", "16000", "-y", tmpWav] + +// macOS β€” ":0" is the default input from System Settings +["-f", "avfoundation", "-i", devInput, "-ac", "1", "-ar", "16000", "-y", tmpWav] +``` + +Both produce 16kHz mono, which is what Whisper wants. On Windows a `micName` of `"default"` is a hard error β€” dshow cannot resolve it β€” whereas on macOS `":0"` is a valid default. + +There is no Linux branch. Voice capture does not currently work on Linux. + +### Transcription + +`local_whisper_stt.py` keeps a strict stream contract: + +```python +sys.stdout.reconfigure(encoding='utf-8') # Windows fix +def log(msg): sys.stderr.write(f"[Python-Whisper] {msg}\n") +``` + +**stdout is the transcript, stderr is diagnostics.** `runLocalWhisper` accumulates stdout as the result and forwards stderr to the output channel. Anything printed to stdout for debugging corrupts the transcript. + +The model is `WhisperModel("base.en", device="cpu", compute_type="int8")` β€” English-only, CPU, quantised. That trades accuracy for running acceptably on a student laptop with no GPU. `local_models/whisper-tiny/` exists but `base.en` is what the script requests. + +The temp WAV is unlinked in the `close` handler regardless of exit code. + +### Fuzzy matching + +`voiceCommandRouter.js` scores a transcript against each keyword with several measures combined: + +```js +scoreTranscriptToKeyword(transcript, keyword) + β†’ 1.0 if either contains the other + β†’ else max(jaccard, tokenOrderAgnosticEdit * 0.65 + tokenEdit * 0.35) +``` + +Built from `levenshteinDistance`, `jaccardSimilarity` (token overlap), `bestWindowEditSimilarity` (sliding window), and `tokenAlignmentSimilarity` (best per-token pairing). Combining them is what makes it survive word reordering *and* per-word transcription noise. + +Thresholds scale with phrase length: + +```js +function getMinimumScoreThreshold(keyword) { + return tokenList(keyword).length <= 1 ? 0.85 : 0.62; +} +``` + +Single-word keywords demand a near-exact match. Without this, one noisy syllable fires arbitrary commands. + +`ExternalIntentRouter.js` applies the same technique to every VS Code command, with its own `getMinimumExternalScore` β€” stricter, because that pool is thousands of entries wide and a loose match there is far more likely to be wrong. + +### The external command registry + +`buildExternalCommandRegistry()` runs once per activation: + +```js +const allCommandIds = await vscode.commands.getCommands(true); +// filter out echocode.* and _internal, then generateKeywords(id) +``` + +`generateKeywords()` strips prefixes (`workbench.action.`, `editor.action.`), splits camelCase and dots into words, and adds a two-word short form. `workbench.action.toggleSidebarVisibility` becomes `["toggle sidebar visibility", "toggle sidebar"]`. + +The result β€” roughly 3300 entries, about 620KB β€” is written to the extension's **global storage** directory, not next to the source. That location is per-user, survives extension updates, and is cleaned up on uninstall. It is also regenerated if absent, so it must never be committed; it is in `.gitignore`. + +Three implementation details worth preserving: + +- **`initExternalCommandRegistry(context)` must be called before the build.** It resolves the path from the extension context and ties the file watcher's disposal to the extension lifetime. +- **Writes are atomic** β€” temp file then `rename`. A crash mid-write would otherwise leave a truncated 620KB file that fails to parse on next activation. +- **The watcher drops itself on `rename`.** `fs.watch` follows the inode, so after a rebuild swaps the file the old watch points at a dead one. + +`getCommands()` caches in memory and returns `[]` on any read failure, so a missing registry degrades to "no external commands" rather than an error. + +### Intent classification via AI + +`localIntentRouter.js` and `local_intent_matcher.py` provide an AI-assisted alternative to fuzzy matching, and `AIrequest.classifyVoiceIntent()` can ask the model to pick a command. Fuzzy matching is the primary path β€” it is instant and needs no model. + +### Voice mode state + +`currentVoiceMode` is a module-level integer in `extension.js`, cycled modulo `VOICE_MODES.length`. `echocode.toggleVoice` reads it and delegates to one of three real commands: + +```js +const modeCommandMap = { chat: "echocode.voiceChat", code: "echocode.voiceCode", command: "echocode.voiceCommand" }; +await vscode.commands.executeCommand(modeCommandMap[activeMode] || "echocode.voiceChat"); +``` + +Because it is module state, the mode resets to Chat on every reload. It is not persisted. + +### Dependency bootstrap + +`dependencyManager.js` guards a genuinely hostile operation. See its own detailed notes in [Known Gaps](Known-Gaps) for history, but the invariants are: + +- **One attempt per extension host.** A module-level memo, because `extension.js` constructs a new `DependencyManager` on activation *and* on every feature reload. Instance state cannot prevent repeats. +- **Cross-window lock.** An `O_EXCL` lock file, stale after 20 minutes. Every VS Code window runs its own extension host against the same venv directory. +- **Attempt counter gates the whole slow path**, venv creation included β€” not just `pip install`. +- **Creation is verified.** `python -m venv` can exit 0 without producing the expected interpreter; that state previously recorded no failure and retried forever. +- **Every shell-out is bounded.** 15s probes, 3min venv, 20min pip. `exec` has no default timeout, and on Windows `python --version` can hang indefinitely on the Microsoft Store App Execution Alias. +- **`maxBuffer` is 32MB.** The default 1MB can be overrun by pip, which kills the child with ENOBUFS and surfaces as a failed install that then retries on every launch. +- **Install success is confirmed against site-packages**, not pip's exit code β€” a partially-installed wheel can still exit 0. + +The warm path is two `existsSync` calls and no process launches. Checking by spawning `python -c "import faster_whisper"` costs over a second cold, because that import pulls in ctranslate2 and onnxruntime. diff --git a/wiki/_Sidebar.md b/wiki/_Sidebar.md new file mode 100644 index 0000000..c7649d9 --- /dev/null +++ b/wiki/_Sidebar.md @@ -0,0 +1,30 @@ +### EchoCode + +- [Home](Home) +- [Getting Started](Getting-Started) +- [Keyboard Shortcuts](Keyboard-Shortcuts) +- [Settings Reference](Settings-Reference) + +**Features** + +- [AI Providers and Models](AI-Providers-and-Models) +- [Chat Tutor](Chat-Tutor) +- [Annotations and Big-O](Annotations-and-Big-O) +- [Code Summaries](Code-Summaries) +- [Assignment Tracker](Assignment-Tracker) +- [Line and Character Read-Out](Line-and-Character-Read-Out) +- [Error Reading](Error-Reading) +- [Code Navigation](Code-Navigation) +- [File and Folder Tools](File-and-Folder-Tools) +- [File Connector](File-Connector) +- [Voice Input](Voice-Input) +- [Modes and Guidance](Modes-and-Guidance) +- [Hotkey Guide](Hotkey-Guide) + +**For developers** + +- [Architecture](Architecture) +- [Speech Handler](Speech-Handler) +- [Custom Feature Implementations](Custom-Feature-Implementations) +- [Testing and CI](Testing-and-CI) +- [Known Gaps](Known-Gaps) diff --git a/wiki/diagrams/svg/01-voice-input-L0.svg b/wiki/diagrams/svg/01-voice-input-L0.svg new file mode 100644 index 0000000..ab264cc --- /dev/null +++ b/wiki/diagrams/svg/01-voice-input-L0.svg @@ -0,0 +1,65 @@ + + + + + + Ctrl+Alt+Space + + + audio stream + + + language, selection, + surrounding code + + + candidate commands + + + executeCommand() + + + generated code + + + question text + + + confirmation + Voice Input β€” Level 0 (Context) + Spoken language becomes a command, generated code, or a tutor question β€” transcribed on-device. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Microphone + (Input Layer) + + Editor Context + (VS Code Environment) + + + Command Registry + (voice_commands.json + VS Code) + + 1.0 + Voice Input + + External Commands / + Extensions + + Editor + (inserted code) + + Chat Tutor + + Speech Output / TTS + + + Transcription is local (faster-whisper). Audio never leaves + the machine; only the resulting text may reach the AI layer. + diff --git a/wiki/diagrams/svg/01-voice-input-L1.svg b/wiki/diagrams/svg/01-voice-input-L1.svg new file mode 100644 index 0000000..2d47b9b --- /dev/null +++ b/wiki/diagrams/svg/01-voice-input-L1.svg @@ -0,0 +1,105 @@ + + + + + ECHOCODE CORE + + + hotkey + + + audio + + + code context + + + keywords + + + wav path + + + transcript + + + clean transcript + + + no match + + + match β‰₯ threshold + + + dictation + + + question keywords + + + what was heard / done + + + write / unlink + + + read interpreter + Voice Input β€” Level 1 (Decomposition) + Spoken language becomes a command, generated code, or a tutor question β€” transcribed on-device. + INPUTS + SERVICES & SINKS + + 1.1 + Capture audio + (ffmpeg β†’ 16kHz mono WAV) + + 1.2 + Transcribe locally + (faster-whisper, base.en) + + 1.3 + Normalise + reject + empty / no-speech + + 1.4 + Score against commands + (Levenshtein + Jaccard) + + 1.5 + Route by intent + (question? code? command?) + + + Users + + Microphone + + Editor Context + + + Command Registry + + External Commands / + Extensions + + AI Services Layer + generateCodeFromVoice() + + Chat Tutor + + Speech Output / TTS + + + Temp WAV + (deleted after use) + + + Local Storage & State + (python path) + + + Transcription is local (faster-whisper). Audio never leaves + the machine; only the resulting text may reach the AI + layer. + diff --git a/wiki/diagrams/svg/02-chat-tutor-L0.svg b/wiki/diagrams/svg/02-chat-tutor-L0.svg new file mode 100644 index 0000000..de3ca9a --- /dev/null +++ b/wiki/diagrams/svg/02-chat-tutor-L0.svg @@ -0,0 +1,55 @@ + + + + + + Ctrl+Alt+C + + + message text + + + transcript + + + open code + + + prompt + history + + + reply text + + + reply, spoken + Chat Tutor β€” Level 0 (Context) + A conversational tutor in the sidebar; answers are constrained for listening, not reading. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Typed question + (webview) + + Voice Input + + Editor Context + + 2.0 + Chat Tutor + + AI Services Layer + Tutor Services + + Webview panel + + Speech Output / TTS + + + Replies are capped at four plain-text sentences. Markdown and + long answers are hostile to TTS. + diff --git a/wiki/diagrams/svg/02-chat-tutor-L1.svg b/wiki/diagrams/svg/02-chat-tutor-L1.svg new file mode 100644 index 0000000..0114668 --- /dev/null +++ b/wiki/diagrams/svg/02-chat-tutor-L1.svg @@ -0,0 +1,83 @@ + + + + + ECHOCODE CORE + + + open panel + + + text + + + text + + + code + + + raw text + + + messages[] + + + reply + + + request + + + reply + + + spoken reply + + + append / read + Chat Tutor β€” Level 1 (Decomposition) + A conversational tutor in the sidebar; answers are constrained for listening, not reading. + INPUTS + SERVICES & SINKS + + 2.1 + Receive message + (onDidReceiveMessage) + + 2.2 + Assemble prompt + BASE_PROMPT + history + turn + + 2.3 + Dispatch to provider + requestTextFromMessages() + + 2.4 + Render + speak reply + + + Users + + Webview message + (userInput) + + Voice transcript + + Editor Context + + AI Services Layer + + Webview panel + (postMessage) + + Speech Output / TTS + + + Conversation history + (in memory, session only) + + + Replies are capped at four plain-text sentences. Markdown + and long answers are hostile to TTS. + diff --git a/wiki/diagrams/svg/03-annotations-bigo-L0.svg b/wiki/diagrams/svg/03-annotations-bigo-L0.svg new file mode 100644 index 0000000..a6f01f0 --- /dev/null +++ b/wiki/diagrams/svg/03-annotations-bigo-L0.svg @@ -0,0 +1,51 @@ + + + + + + Ctrl+Alt+A / Ctrl+Alt+N + + + source + line numbers + + + verbosity + + + numbered source + + + per-line notes + + + one finding at a time + Annotations and Big-O β€” Level 0 (Context) + Line-level review of the open file, stepped through one finding at a time. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Open document + (VS Code Environment) + + + Guidance level + + 3.0 + Annotations and Big-O + + AI Services Layer + Analysis Services + + Editor decorations + + Speech Output / TTS + + + A local rules pass runs first, so findings appear with no + model configured. The AI pass adds depth on top. + diff --git a/wiki/diagrams/svg/03-annotations-bigo-L1.svg b/wiki/diagrams/svg/03-annotations-bigo-L1.svg new file mode 100644 index 0000000..fe2591f --- /dev/null +++ b/wiki/diagrams/svg/03-annotations-bigo-L1.svg @@ -0,0 +1,90 @@ + + + + + ECHOCODE CORE + + + hotkey + + + source + + + format + + + numbered source + + + early findings + + + response stream + + + findings[] + + + prompt + + + applyDecoration() + + + dequeue on Ctrl+Alt+S + + + non-verbal cue + + + enqueue / dequeue + Annotations and Big-O β€” Level 1 (Decomposition) + Line-level review of the open file, stepped through one finding at a time. + INPUTS + SERVICES & SINKS + + 3.1 + Read file with line numbers + + 3.2 + Local rules pass + (instant, no AI) + + 3.3 + AI analysis pass + analyzeAI() + + 3.4 + Extract findings + extractJsonObjectsFromStream() + + 3.5 + Decorate + enqueue + (guidance-formatted) + + + Users + + Open document + + + Guidance level + + AI Services Layer + + Editor decorations + + Speech Output / TTS + + Audio ping + (queue empty) + + + Finding queue + (Queue class) + + + A local rules pass runs first, so findings appear with no + model configured. The AI pass adds depth on top. + diff --git a/wiki/diagrams/svg/04-code-summaries-L0.svg b/wiki/diagrams/svg/04-code-summaries-L0.svg new file mode 100644 index 0000000..eee8644 --- /dev/null +++ b/wiki/diagrams/svg/04-code-summaries-L0.svg @@ -0,0 +1,46 @@ + + + + + + Ctrl+Alt+E then F / C / P + + + position + + + DocumentSymbol tree + + + scoped source + + + spoken summary + Code Summaries β€” Level 0 (Context) + Plain-language summary of the function, class, or file containing the cursor. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Cursor position + (VS Code Environment) + + Document symbols + (language extension) + + 4.0 + Code Summaries + + AI Services Layer + Tutor Services + + Speech Output / TTS + + + Scope comes from VS Code's own symbol provider, so accuracy + tracks whichever language extension the user has installed. + diff --git a/wiki/diagrams/svg/04-code-summaries-L1.svg b/wiki/diagrams/svg/04-code-summaries-L1.svg new file mode 100644 index 0000000..232b9b9 --- /dev/null +++ b/wiki/diagrams/svg/04-code-summaries-L1.svg @@ -0,0 +1,76 @@ + + + + + ECHOCODE CORE + + + hotkey + + + position + + + symbol tree + + + tree + + + range + + + code + + + summary + + + prompt + + + summary + + + read-only + Code Summaries β€” Level 1 (Decomposition) + Plain-language summary of the function, class, or file containing the cursor. + INPUTS + SERVICES & SINKS + + 4.1 + Request document symbols + executeDocumentSymbolProvider + + 4.2 + Find smallest enclosing symbol + inRange() + rangeSize() + + 4.3 + Extract source for range + + 4.4 + Summarise + requestTextFromMessages() + + 4.5 + Speak summary + + + Users + + Cursor position + + Symbol provider + + AI Services Layer + + Speech Output / TTS + + Editor + (no modification) + + + Scope comes from VS Code's own symbol provider, so accuracy + tracks whichever language extension the user has installed. + diff --git a/wiki/diagrams/svg/05-error-reading-L0.svg b/wiki/diagrams/svg/05-error-reading-L0.svg new file mode 100644 index 0000000..aaf096f --- /dev/null +++ b/wiki/diagrams/svg/05-error-reading-L0.svg @@ -0,0 +1,57 @@ + + + + + + Ctrl+Alt+G + + + file path + + + verbosity + + + spawn + parse + + + simplified findings + + + critical errors + + + full findings + Error Reading β€” Level 0 (Context) + Python and C++ problems compiled or linted, then read aloud in beginner language. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Source file on disk + (.py / .cpp) + + + Guidance level + + 5.0 + Error Reading + + Validation Tools + (pylint / g++) + + Validation & Error + Interpretation + + Speech Output / TTS + + Output channel + + + Save-triggered linting exists but is not wired into activation + β€” only the manual Ctrl+Alt+G path runs today. + diff --git a/wiki/diagrams/svg/05-error-reading-L1.svg b/wiki/diagrams/svg/05-error-reading-L1.svg new file mode 100644 index 0000000..243c0b7 --- /dev/null +++ b/wiki/diagrams/svg/05-error-reading-L1.svg @@ -0,0 +1,92 @@ + + + + + ECHOCODE CORE + + + hotkey + + + path + + + format + + + command + + + available? + + + raw output + + + findings[] + + + spawn + + + findings + + + critical only + + + all findings + + + read / set + Error Reading β€” Level 1 (Decomposition) + Python and C++ problems compiled or linted, then read aloud in beginner language. + INPUTS + SERVICES & SINKS + + 5.1 + Select toolchain + by languageId + + 5.2 + Probe availability + (cached) + + 5.3 + Run analyser + pylint --output-format=json / g++ + + 5.4 + Parse + classify + critical vs informational + + 5.5 + Simplify wording + simplifyError() + + + Users + + Source file path + + + Guidance level + + Validation Tools + (External Services) + + Validation & Error + Interpretation + + Speech Output / TTS + + Output channel + + + Availability cache + + attempt latch + + + Save-triggered linting exists but is not wired into + activation β€” only the manual Ctrl+Alt+G path runs today. + diff --git a/wiki/diagrams/svg/06-line-character-readout-L0.svg b/wiki/diagrams/svg/06-line-character-readout-L0.svg new file mode 100644 index 0000000..8a52228 --- /dev/null +++ b/wiki/diagrams/svg/06-line-character-readout-L0.svg @@ -0,0 +1,40 @@ + + + + + + Ctrl+Alt+L / K / R + + + line, position + + + typed character + + + line, description, or + character + Line and Character Read-Out β€” Level 0 (Context) + Read the current line, explain what it does, or hear every character as it is typed. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Cursor + line text + (VS Code Environment) + + Document change events + + 6.0 + Line and Character Read-Out + + Speech Output / TTS + + + Line reading speaks trimmed text, so indentation is lost β€” + Ctrl+Alt+E W compensates by describing position in context. + diff --git a/wiki/diagrams/svg/06-line-character-readout-L1.svg b/wiki/diagrams/svg/06-line-character-readout-L1.svg new file mode 100644 index 0000000..2d25249 --- /dev/null +++ b/wiki/diagrams/svg/06-line-character-readout-L1.svg @@ -0,0 +1,80 @@ + + + + + ECHOCODE CORE + + + hotkey + + + line + + + change event + + + text + + + description + + + listening state + + + Ctrl+Alt+L + + + Ctrl+Alt+K + + + while enabled + + + set / clear + Line and Character Read-Out β€” Level 1 (Decomposition) + Read the current line, explain what it does, or hear every character as it is typed. + INPUTS + SERVICES & SINKS + + 6.1 + Read current line + (trimmed) + + 6.2 + Describe line + describePythonLine() β€” rule-based + + 6.3 + Toggle character read-out + (subscribe / dispose) + + 6.4 + Speak character to left + of cursor + + + Users + + Cursor + line text + + onDidChangeTextDocument + + Speech Output / TTS + (verbatim line) + + Speech Output / TTS + (explanation) + + Speech Output / TTS + (each character) + + + Listening flag + + disposable + + + Line reading speaks trimmed text, so indentation is lost β€” + Ctrl+Alt+E W compensates by describing position in context. + diff --git a/wiki/diagrams/svg/07-code-navigation-L0.svg b/wiki/diagrams/svg/07-code-navigation-L0.svg new file mode 100644 index 0000000..0a4aae8 --- /dev/null +++ b/wiki/diagrams/svg/07-code-navigation-L0.svg @@ -0,0 +1,51 @@ + + + + + + Ctrl+Alt+Up/Down, [ ], P, E W + + + symbol tree + + + folder list + + + reveal + select + + + destination name + + + navigation index + Code Navigation β€” Level 0 (Context) + Move between functions, files, and folders by keyboard, with each destination announced. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Document symbols + (language extension) + + Workspace folders + + 7.0 + Code Navigation + + Editor selection + (cursor moved) + + Speech Output / TTS + + + Local Storage & State + + + Symbol jumping delegates to the language extension, so an + unsupported language has nothing to jump between. + diff --git a/wiki/diagrams/svg/07-code-navigation-L1.svg b/wiki/diagrams/svg/07-code-navigation-L1.svg new file mode 100644 index 0000000..906df75 --- /dev/null +++ b/wiki/diagrams/svg/07-code-navigation-L1.svg @@ -0,0 +1,84 @@ + + + + + ECHOCODE CORE + + + hotkey + + + tree + + + paths + + + targets[] + + + target + + + ordered folders + + + new position + + + reveal + + + spoken position + + + read / advance + + + read / advance + Code Navigation β€” Level 1 (Decomposition) + Move between functions, files, and folders by keyboard, with each destination announced. + INPUTS + SERVICES & SINKS + + 7.1 + Collect jump targets + flatten + isJumpableKind() + + 7.2 + Pick nearest in direction + + 7.3 + Build / refresh folder list + (watch for changes) + + 7.4 + Move cursor or folder cursor + + 7.5 + Announce destination + (name, line, enclosing symbol) + + + Users + + Symbol provider + + Workspace folders + + Editor selection + + Speech Output / TTS + + + Folder index + (module state) + + + File index + (module state) + + + Symbol jumping delegates to the language extension, so an + unsupported language has nothing to jump between. + diff --git a/wiki/diagrams/svg/08-file-folder-tools-L0.svg b/wiki/diagrams/svg/08-file-folder-tools-L0.svg new file mode 100644 index 0000000..6dc5a8e --- /dev/null +++ b/wiki/diagrams/svg/08-file-folder-tools-L0.svg @@ -0,0 +1,52 @@ + + + + + + Ctrl+Alt+; / Ctrl+Alt+F + + + name + + + target directory + + + write / mkdir + + + open document + + + confirmation + File and Folder Tools β€” Level 0 (Context) + Create files and folders relative to the current navigation folder, without the Explorer tree. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Typed name + (input box) + + Current navigation folder + + 8.0 + File and Folder Tools + + File system + (External Services) + + Editor + (new file opened) + + Speech Output / TTS + + + Creation follows the navigation cursor, not the Explorer + selection β€” that is what makes move-then-create one keyboard + flow. + diff --git a/wiki/diagrams/svg/08-file-folder-tools-L1.svg b/wiki/diagrams/svg/08-file-folder-tools-L1.svg new file mode 100644 index 0000000..b5878f0 --- /dev/null +++ b/wiki/diagrams/svg/08-file-folder-tools-L1.svg @@ -0,0 +1,82 @@ + + + + + ECHOCODE CORE + + + hotkey + + + name + + + directory + + + path + + + name + + + safe path + + + created + + + create + + + open new file + + + confirmation + + + refusal + File and Folder Tools β€” Level 1 (Decomposition) + Create files and folders relative to the current navigation folder, without the Explorer tree. + INPUTS + SERVICES & SINKS + + 8.1 + Resolve target directory + + 8.2 + Prompt for name + showInputBox + + 8.3 + Guard against collision + existsSync + + 8.4 + Create on disk + writeFile / mkdir + + 8.5 + Open + confirm + + + Users + + Input box value + + getCurrentFolder() + + File system + + Editor + + Speech Output / TTS + + Speech Output / TTS + (already exists) + + + Creation follows the navigation cursor, not the Explorer + selection β€” that is what makes move-then-create one + keyboard flow. + diff --git a/wiki/diagrams/svg/09-file-connector-L0.svg b/wiki/diagrams/svg/09-file-connector-L0.svg new file mode 100644 index 0000000..ed452e7 --- /dev/null +++ b/wiki/diagrams/svg/09-file-connector-L0.svg @@ -0,0 +1,51 @@ + + + + + + Ctrl+Alt+I then Ctrl+Shift+I + + + symbol, path + + + insertion point + + + write .h + + + edit + + + confirmation + File Connector β€” Level 0 (Context) + Generate the import needed to use a function from one file in another, and paste it in. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Source file + cursor + (function to export) + + Destination file + cursor + + 9.0 + File Connector + + File system + (header generated) + + Editor + (import inserted) + + Speech Output / TTS + + + For C++ this writes a header the user did not ask for. Any + change here must stay idempotent. + diff --git a/wiki/diagrams/svg/09-file-connector-L1.svg b/wiki/diagrams/svg/09-file-connector-L1.svg new file mode 100644 index 0000000..7983151 --- /dev/null +++ b/wiki/diagrams/svg/09-file-connector-L1.svg @@ -0,0 +1,91 @@ + + + + + ECHOCODE CORE + + + copy step + + + function + + + path + + + symbol + + + ok + + + signature + + + header path + + + write + + + insert + + + confirmation + + + refusal + + + store / consume + File Connector β€” Level 1 (Decomposition) + Generate the import needed to use a function from one file in another, and paste it in. + INPUTS + SERVICES & SINKS + + 9.1 + Find function at cursor + findFunctionAtPosition() + + 9.2 + Check language compatibility + areExtensionsCompatible() + + 9.3 + Extract signature + includes + (C++ only, from file text) + + 9.4 + Ensure header declaration + _ensureCppHeader() + + 9.5 + Generate + paste import + + + Users + + Source file + symbols + + Destination path + + File system + (.h created / appended) + + Editor + (import at cursor) + + Speech Output / TTS + + Speech Output / TTS + (incompatible) + + + Pending connection + (module state) + + + For C++ this writes a header the user did not ask for. Any + change here must stay idempotent. + diff --git a/wiki/diagrams/svg/10-assignment-tracker-L0.svg b/wiki/diagrams/svg/10-assignment-tracker-L0.svg new file mode 100644 index 0000000..b1f1cb5 --- /dev/null +++ b/wiki/diagrams/svg/10-assignment-tracker-L0.svg @@ -0,0 +1,53 @@ + + + + + + Ctrl+Alt+O + + + file + + + source for rescan + + + extract text + + + tasks vs code + + + next task / progress + Assignment Tracker β€” Level 0 (Context) + Turn an assignment document into a spoken task list, tracked against the code the user writes. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Assignment document + (.docx / .pdf / text) + + User's code + (VS Code Environment) + + 10.0 + Assignment Tracker + + Doc Parser + (mammoth / pdf-parse) + + AI Services Layer + Analysis Services + + Speech Output / TTS + + + Completion detection is a model comparing prose to code, so it + is non-deterministic. Ctrl+Alt+M is the deterministic + override. + diff --git a/wiki/diagrams/svg/10-assignment-tracker-L1.svg b/wiki/diagrams/svg/10-assignment-tracker-L1.svg new file mode 100644 index 0000000..5a6a682 --- /dev/null +++ b/wiki/diagrams/svg/10-assignment-tracker-L1.svg @@ -0,0 +1,92 @@ + + + + + ECHOCODE CORE + + + load file + + + file + + + source + + + plain text + + + tasks[] + + + task + + + response + + + parse + + + extract / compare + + + task text, progress + + + read / write + + + set + Assignment Tracker β€” Level 1 (Decomposition) + Turn an assignment document into a spoken task list, tracked against the code the user writes. + INPUTS + SERVICES & SINKS + + 10.1 + Extract document text + mammoth / pdf-parse + + 10.2 + Identify tasks + AI pass, or rule-based split + + 10.3 + Read next task + (all, or incomplete only) + + 10.4 + Rescan code against tasks + analyzeAI() + + 10.5 + Update completion flags + updateCompletedTasksFromAI() + + + Users + + Assignment document + + User's code + + Doc Parser + (External Services) + + AI Services Layer + + Speech Output / TTS + + + Task list + index + (in memory, lost on reload) + + + Completion flags + + + Completion detection is a model comparing prose to code, so + it is non-deterministic. Ctrl+Alt+M is the deterministic + override. + diff --git a/wiki/diagrams/svg/11-ai-provider-selection-L0.svg b/wiki/diagrams/svg/11-ai-provider-selection-L0.svg new file mode 100644 index 0000000..c160222 --- /dev/null +++ b/wiki/diagrams/svg/11-ai-provider-selection-L0.svg @@ -0,0 +1,60 @@ + + + + + + first run, or Select AI + Provider + + + capability scan + + + current config + + + probe + verify + + + persist choice + + + store API key + + + announced options + AI Provider and Model Selection β€” Level 0 (Context) + Choose and verify the backend every AI feature routes through: Copilot, local Ollama, or a hosted API. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + Machine specs + (CPU, RAM, GPU) + + + Stored settings + key + + 11.0 + AI Provider and Model Selection + + AI Services + (Copilot / Ollama / API) + + + Local Storage & State + (settings) + + OS keychain + (context.secrets) + + Speech Output / TTS + + + Provider probes run concurrently and are individually capped, + so the picker opens in ~3s rather than the sum of three waits. + diff --git a/wiki/diagrams/svg/11-ai-provider-selection-L1.svg b/wiki/diagrams/svg/11-ai-provider-selection-L1.svg new file mode 100644 index 0000000..5e6a6b3 --- /dev/null +++ b/wiki/diagrams/svg/11-ai-provider-selection-L1.svg @@ -0,0 +1,93 @@ + + + + + ECHOCODE CORE + + + invoke wizard + + + scan + + + current + + + availability + + + branch + + + model list + + + recommendation + + + list models + + + NDJSON progress + + + store key + + + row + position + + + write global + AI Provider and Model Selection β€” Level 1 (Decomposition) + Choose and verify the backend every AI feature routes through: Copilot, local Ollama, or a hosted API. + INPUTS + SERVICES & SINKS + + 11.1 + Probe providers concurrently + capped 3s / 2s / 5s + + 11.2 + Announce + present fork + API or local Ollama + + 11.3 + Verify endpoint answers + before saving anything + + 11.4 + Recommend a local model + from memory budget + + 11.5 + Persist choice + (settings + keychain) + + + Users + + Machine specs + + + Stored settings + + AI Services + (probe / verify) + + Ollama + (pull model) + + OS keychain + + Speech Output / TTS + + + Local Storage & State + (aiProvider, model) + + + Provider probes run concurrently and are individually + capped, so the picker opens in ~3s rather than the sum of + three waits. + diff --git a/wiki/diagrams/svg/12-modes-guidance-L0.svg b/wiki/diagrams/svg/12-modes-guidance-L0.svg new file mode 100644 index 0000000..c14e5d7 --- /dev/null +++ b/wiki/diagrams/svg/12-modes-guidance-L0.svg @@ -0,0 +1,53 @@ + + + + + + Ctrl+Alt+9 / Ctrl+Alt+Z + + + current values + + + command id + + + setContext + + + formatted verbosity + + + mode announced + Modes and Guidance β€” Level 0 (Context) + Student/Dev mode decides which features are reachable; guidance level decides how much is said. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + + Stored mode + guidance + (settings) + + Command invocation + (any source) + + 12.0 + Modes and Guidance + + VS Code context keys + echocode:isDev / isStudent + + Every explanation feature + + Audio ping + TTS + + + Mode is enforced twice β€” context keys stop keybindings + dispatching, and guard() catches Palette and voice + invocations. + diff --git a/wiki/diagrams/svg/12-modes-guidance-L1.svg b/wiki/diagrams/svg/12-modes-guidance-L1.svg new file mode 100644 index 0000000..90a1c89 --- /dev/null +++ b/wiki/diagrams/svg/12-modes-guidance-L1.svg @@ -0,0 +1,92 @@ + + + + + ECHOCODE CORE + + + toggle + + + mode, guidance + + + invocation + + + mode + + + keys set + + + allowed? + + + text + + + when: echocode:isDev + + + not allowed + + + guided / balanced / concise + + + mode name + + + read / write + Modes and Guidance β€” Level 1 (Decomposition) + Student/Dev mode decides which features are reachable; guidance level decides how much is said. + INPUTS + SERVICES & SINKS + + 12.1 + Read mode + getMode() β€” fails closed to student + + 12.2 + Publish context keys + setContext Γ—2 + + 12.3 + Check denylist + STUDENT_LOCKED_COMMANDS + + 12.4 + Format by guidance level + formatHelpByGuidance() + + 12.5 + Announce mode change + ping, then speech + + + Users + + + Stored settings + + Command id + + VS Code keybinding filter + + Blocked command + (refusal spoken) + + Explanation features + + Audio ping + TTS + + + Local Storage & State + (echocode.mode) + + + Mode is enforced twice β€” context keys stop keybindings + dispatching, and guard() catches Palette and voice + invocations. + diff --git a/wiki/diagrams/svg/13-hotkey-guide-L0.svg b/wiki/diagrams/svg/13-hotkey-guide-L0.svg new file mode 100644 index 0000000..17663a4 --- /dev/null +++ b/wiki/diagrams/svg/13-hotkey-guide-L0.svg @@ -0,0 +1,35 @@ + + + + + + F1 + + + shortcut list + + + shortcuts, in task order + Hotkey Guide β€” Level 0 (Context) + F1 reads EchoCode's own shortcuts aloud β€” the recovery path when the user has forgotten everything else. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + + Hardcoded guide text + (hotkeyGuide.js) + + 13.0 + Hotkey Guide + + Speech Output / TTS + + + The spoken list is hand-maintained, not generated from + package.json, so it drifts silently when a binding changes. + diff --git a/wiki/diagrams/svg/13-hotkey-guide-L1.svg b/wiki/diagrams/svg/13-hotkey-guide-L1.svg new file mode 100644 index 0000000..3965c6b --- /dev/null +++ b/wiki/diagrams/svg/13-hotkey-guide-L1.svg @@ -0,0 +1,55 @@ + + + + + ECHOCODE CORE + + + F1 + + + list + + + entries + + + ordered + + + spoken guide + + + stop + Hotkey Guide β€” Level 1 (Decomposition) + F1 reads EchoCode's own shortcuts aloud β€” the recovery path when the user has forgotten everything else. + INPUTS + SERVICES & SINKS + + 13.1 + Load guide text + + 13.2 + Order by task frequency + (spoken lists are linear) + + 13.3 + Speak sequentially + (interruptible) + + + Users + + + Guide text + (module constant) + + Speech Output / TTS + + Ctrl+Alt+X + (user interrupt) + + + The spoken list is hand-maintained, not generated from + package.json, so it drifts silently when a binding changes. + diff --git a/wiki/diagrams/svg/14-speech-output-L0.svg b/wiki/diagrams/svg/14-speech-output-L0.svg new file mode 100644 index 0000000..2161f9a --- /dev/null +++ b/wiki/diagrams/svg/14-speech-output-L0.svg @@ -0,0 +1,53 @@ + + + + + + any feature calls + speakMessage() + + + text to speak + + + rate + + + stop request + + + spawn one utterance + + + spoken audio + Speech Output / TTS β€” Level 0 (Context) + The single sink every other feature speaks through. Interrupts rather than queues, and never overlaps. + SOURCES + PROCESS + SINKS + + + Users + (BLV programmer) + + 116 call sites + (26 modules) + + + Speech speed setting + + Ctrl+Alt+X + + 14.0 + Speech Output / TTS + + Speech Services + (OS TTS via say.js) + + Audio output + + + say.js holds one overwritable child handle, so spawning while + audio plays orphans the old process. An interrupt must wait + for exit. + diff --git a/wiki/diagrams/svg/14-speech-output-L1.svg b/wiki/diagrams/svg/14-speech-output-L1.svg new file mode 100644 index 0000000..02b2ec1 --- /dev/null +++ b/wiki/diagrams/svg/14-speech-output-L1.svg @@ -0,0 +1,99 @@ + + + + + ECHOCODE CORE + + + speakMessage() + + + message + + + rate + + + Ctrl+Alt+X + + + pending set + + + yes β†’ interrupt + + + exit reported + + + callback + + + child process + + + one voice at a time + + + resolve + + + read / set + + + increment + Speech Output / TTS β€” Level 1 (Decomposition) + The single sink every other feature speaks through. Interrupts rather than queues, and never overlaps. + INPUTS + SERVICES & SINKS + + 14.1 + Supersede pending utterance + (at most one waits) + + 14.2 + Something speaking? + + 14.3 + Kill current + await exit + (the no-overlap invariant) + + 14.4 + Spawn utterance + say.speak(text, voice, rate) + + 14.5 + Retire + start pending + (identity-checked) + + + Users + + Caller text + + + Speech speed + + Stop request + + Speech Services + (External Services) + + Audio output + + Caller promise + (always settles) + + + activeUtterance + + pendingUtterance + + + utteranceId + (stale-callback guard) + + + say.js holds one overwritable child handle, so spawning + while audio plays orphans the old process. An interrupt + must wait for exit. + diff --git a/wiki/diagrams/svg/S1-component-architecture.svg b/wiki/diagrams/svg/S1-component-architecture.svg new file mode 100644 index 0000000..39a7d61 --- /dev/null +++ b/wiki/diagrams/svg/S1-component-architecture.svg @@ -0,0 +1,99 @@ + + + + + VS CODE ENVIRONMENT + + INPUT LAYER + + ECHOCODE CORE + + AI SERVICES LAYER + + EXTERNAL SERVICES + + + + + + + + validation, parsing + + + + + + decorations, edits + EchoCode β€” Component Architecture + Five layers. Every feature is assembled from the same components. + + Editor Context + + Document Symbols + + Commands & Workspace + + Output & Webview Panels + + + Users + (BLV programmer) + + Keyboard / Hotkeys + 45 bindings + + Voice Pipeline + local speech-to-text + + Chat Input + sidebar webview + + Command Router & Guard + mode policy + + Feature Loader + built-in or user impl + + Guidance Formatter + guided / balanced / concise + + Speech Handler + one voice, interruptible + + State & Settings + globalState, keychain + + Request Dispatcher + single funnel + + Tutor Services + + Analysis Services + + Code Generation + + Intent Classification + + GitHub Copilot + via vscode.lm + + Ollama + local, on-device + + Hosted API + OpenAI / Anthropic / Groq + + Validation Tools + pylint, g++ + + Doc Parser + docx, pdf + + OS Speech Services + + Speech Output / TTS + + + Local Storage & State + diff --git a/wiki/diagrams/svg/S2-interaction-patterns.svg b/wiki/diagrams/svg/S2-interaction-patterns.svg new file mode 100644 index 0000000..564dc2a --- /dev/null +++ b/wiki/diagrams/svg/S2-interaction-patterns.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + Fourteen Features, Five Patterns + Each feature is one of these shapes. Only the prompt and the sink change. + + A + Direct read-out + read line Β· character read-out Β· where am I Β· + navigation Β· hotkey guide Β· mode change + no AI Β· instant Β· works offline + + Editor / cursor state + + Extract + format + + Speech Output + + B + AI code analysis + annotations Β· Big-O analysis Β· code summaries Β· + assignment rescan + local rules first, so findings appear with no model + configured + + Editor context + + Local rules pass + + AI Services Layer + + Parse + format + + Speech + decorations + + C + External validation + Python errors (pylint) Β· C++ errors (g++) + bounded timeouts Β· failures classified, not generic + + File on disk + + Spawn tool + + Parse output + + Interpret + simplify + + Speech Output + + D + Voice intake + voice command Β· voice to code Β· voice to tutor + audio never leaves the device + + Microphone + + Local transcription + + Fuzzy intent match + + Command Β· Code Β· Chat + + E + Workspace mutation + create file Β· create folder Β· file connector + never overwrites silently + + Name / selection + + Validate + guard + + Write to disk + + Speech confirm + diff --git a/wiki/diagrams/svg/S3-provider-abstraction.svg b/wiki/diagrams/svg/S3-provider-abstraction.svg new file mode 100644 index 0000000..903f7d7 --- /dev/null +++ b/wiki/diagrams/svg/S3-provider-abstraction.svg @@ -0,0 +1,47 @@ + + + + + AI-BACKED FEATURES + + + + + + One Funnel, Three Backends + Features never choose a provider. Swapping backends changes one function. + + Chat Tutor + + Annotations & Big-O + + Code Summaries + + Voice to Code + + Assignment Rescan + + Request Dispatcher + dispatchMessages() + + + Resolved provider + from settings + + GitHub Copilot + via vscode.lm + no API key Β· uses the user's entitlement + + Ollama + local HTTP + on-device Β· no quota Β· offline + + Hosted API + OpenAI-compatible or Anthropic + bring your own key Β· two wire formats + + + The research question is efficiency: Copilot costs the user nothing extra, Ollama costs nothing at all + and never leaves the machine, and a hosted key is the fallback. Provider choice is verified before it + is saved. + diff --git a/wiki/diagrams/svg/S4-voice-pipeline.svg b/wiki/diagrams/svg/S4-voice-pipeline.svg new file mode 100644 index 0000000..3c8cbc7 --- /dev/null +++ b/wiki/diagrams/svg/S4-voice-pipeline.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + Voice Pipeline β€” Transcribed On-Device + Speech becomes a command, code, or a question. The audio never leaves the machine. + + Microphone + Input Layer + + Capture + ffmpeg β†’ 16 kHz mono + local + + Transcribe + faster-whisper, CPU + local + + Intent match + fuzzy, error-tolerant + local + + + Command registries + EchoCode's 28 phrases + every VS Code command + + Run a command + External Commands / Extensions + + Generate code + AI Services Layer β†’ editor + + Ask the tutor + Chat Tutor + + + Transcription runs in an isolated Python environment EchoCode bootstraps once. A question-shaped utterance is routed + to the tutor even in command mode, so β€œhow do I open a terminal?” is answered rather than executed. + + Local speech-to-text is the privacy boundary: + no audio, and no code, leaves the device on this path. + diff --git a/wiki/diagrams/svg/S5-output-path.svg b/wiki/diagrams/svg/S5-output-path.svg new file mode 100644 index 0000000..e97336e --- /dev/null +++ b/wiki/diagrams/svg/S5-output-path.svg @@ -0,0 +1,60 @@ + + + + + EVERY FEATURE + + + + + formatted text + + + + + queue empty, mode change + + + findings + + Output Path β€” Everything Reaches the User as Speech + One sink, 116 call sites. Verbosity and interruption are handled centrally. + + Errors & annotations + + Summaries & explanations + + Navigation & position + + Tasks & confirmations + + Mode & status + + Guidance Formatter + guided Β· balanced Β· concise + Per-field character caps are + speech-duration budgets, + not layout constraints. + + Speech Handler + interrupt, never overlap + A new message kills the current + utterance and waits for its exit + before speaking. + + OS Speech Services + + Audio Pings + non-verbal cues + + Editor Decorations + for sighted collaborators + + Heard by the user + + + Interrupting rather than queueing is the right + default for an accessibility tool: arrowing down a + list emits one announcement per row, and a queue + would narrate rows the user has already passed. +