diff --git a/.vscode/launch.json b/.vscode/launch.json index 4ab79bebd7..13dc141620 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -57,7 +57,7 @@ "--extensionDevelopmentPath=${workspaceFolder}/extensions/vscode" ], "outFiles": [ - "${workspaceFolder}/extensions/vscode/out/**/*.js", + "${workspaceFolder}/extensions/vscode/out/extension.js", "/Users/natesesti/.continue/config.ts" ], "preLaunchTask": "vscode-extension:build" diff --git a/core/autocomplete/completionProvider.ts b/core/autocomplete/completionProvider.ts index 66ebf9cee2..33b73ed5b4 100644 --- a/core/autocomplete/completionProvider.ts +++ b/core/autocomplete/completionProvider.ts @@ -395,7 +395,7 @@ export async function getTabCompletion( if (llm.model.includes("codestral")) { // Codestral sometimes starts with an extra space if (completion[0] === " " && completion[1] !== " ") { - if (suffix.startsWith("\n")) { + if (prefix.endsWith(" ") && suffix.startsWith("\n")) { completion = completion.slice(1); } } diff --git a/core/autocomplete/lineStream.ts b/core/autocomplete/lineStream.ts index 134d356002..7ebd12ff54 100644 --- a/core/autocomplete/lineStream.ts +++ b/core/autocomplete/lineStream.ts @@ -109,12 +109,16 @@ export async function* skipLines(stream: LineStream): LineStream { } } +const LINES_TO_REMOVE_BEFORE_START = [ + "", + "[CODE]", + "", +]; + function shouldRemoveLineBeforeStart(line: string): boolean { return ( line.trimStart().startsWith("```") || - line.trim() === "[CODE]" || - line.trim() === "" || - line.trim() === "" + LINES_TO_REMOVE_BEFORE_START.some((l) => line.trim() === l) ); } diff --git a/core/config/default.ts b/core/config/default.ts index 2277eff43a..08d28d649b 100644 --- a/core/config/default.ts +++ b/core/config/default.ts @@ -33,7 +33,7 @@ export const FREE_TRIAL_MODELS: ModelDescription[] = [ ]; export const defaultConfig: SerializedContinueConfig = { - models: FREE_TRIAL_MODELS, + models: [], customCommands: [ { name: "test", diff --git a/core/config/load.ts b/core/config/load.ts index 0833c0eff5..a96688e6ea 100644 --- a/core/config/load.ts +++ b/core/config/load.ts @@ -150,15 +150,18 @@ async function serializedToIntermediateConfig( } const workspaceDirs = await ide.getWorkspaceDirs(); - const promptFiles = ( - await Promise.all( - workspaceDirs.map((dir) => - getPromptFiles(ide, path.join(dir, ".prompts")), - ), + const promptFolder = initial.experimental?.promptPath; + + let promptFiles: { path: string; content: string }[] = []; + if (promptFolder) { + promptFiles = ( + await Promise.all( + workspaceDirs.map((dir) => getPromptFiles(ide, promptFolder)), + ) ) - ) - .flat() - .filter(({ path }) => path.endsWith(".prompt")); + .flat() + .filter(({ path }) => path.endsWith(".prompt")); + } // Also read from ~/.continue/.prompts promptFiles.push(...readAllGlobalPromptFiles()); diff --git a/core/config/promptFile.ts b/core/config/promptFile.ts index 494220b6a8..9f8ec55c1c 100644 --- a/core/config/promptFile.ts +++ b/core/config/promptFile.ts @@ -9,7 +9,7 @@ export async function getPromptFiles( dir: string, ): Promise<{ path: string; content: string }[]> { try { - const paths = await ide.listWorkspaceContents(dir); + const paths = await ide.listWorkspaceContents(dir, false); const results = paths.map(async (path) => { const content = await ide.readFile(path); return { path, content }; @@ -55,10 +55,17 @@ export function slashCommandFromPromptFile( // Render prompt template const diff = await ide.getDiff(); + const currentFilePath = await ide.getCurrentFile(); const promptUserInput = await renderTemplatedString( prompt, ide.readFile.bind(ide), - { input: userInput, diff }, + { + input: userInput, + diff, + currentFile: currentFilePath + ? await ide.readFile(currentFilePath) + : undefined, + }, ); const messages = [...history]; diff --git a/core/config/types.ts b/core/config/types.ts index ca17d6c9c9..f7bd0fc19a 100644 --- a/core/config/types.ts +++ b/core/config/types.ts @@ -385,7 +385,7 @@ declare global { stackDepth: number, ): Promise; getAvailableThreads(): Promise; - listWorkspaceContents(directory?: string): Promise; + listWorkspaceContents(directory?: string, useGitIgnore?: boolean): Promise; listFolders(): Promise; getWorkspaceDirs(): Promise; getWorkspaceConfigs(): Promise; diff --git a/core/context/providers/CodeHighlightsContextProvider.ts b/core/context/providers/CodeHighlightsContextProvider.ts index b2522e37a4..626b3db19f 100644 --- a/core/context/providers/CodeHighlightsContextProvider.ts +++ b/core/context/providers/CodeHighlightsContextProvider.ts @@ -6,8 +6,6 @@ import { import { getBasename } from "../../util/index.js"; import { BaseContextProvider } from "../index.js"; -// import { getHighlightsThatFit, ILLMContextSizer } from "llm-code-highlighter/dist/index.continue"; - const HIGHLIGHTS_TOKEN_BUDGET = 2000; class CodeHighlightsContextProvider extends BaseContextProvider { @@ -22,6 +20,9 @@ class CodeHighlightsContextProvider extends BaseContextProvider { query: string, extras: ContextProviderExtras, ): Promise { + // const { getHighlightsThatFit } = await import( + // "llm-code-highlighter/src/index.continue.js" + // ); const ide = extras.ide; const openFiles = await ide.getOpenFiles(); const allFiles: { name: string; absPath: string; content: string }[] = @@ -34,11 +35,11 @@ class CodeHighlightsContextProvider extends BaseContextProvider { }; }), ); - // const contextSizer = { + // const contextSizer = { // fits(content: string): boolean { // return countTokens(content, "") < HIGHLIGHTS_TOKEN_BUDGET; - // } - // } as ILLMContextSizer + // }, + // }; // const repoMap = await getHighlightsThatFit( // contextSizer, // [], @@ -49,7 +50,7 @@ class CodeHighlightsContextProvider extends BaseContextProvider { // relPath: file.name, // code: file.content, // }; - // }) + // }), // ); // return [ // { diff --git a/core/context/providers/FileTreeContextProvider.ts b/core/context/providers/FileTreeContextProvider.ts index c098d61ab0..1470b67a74 100644 --- a/core/context/providers/FileTreeContextProvider.ts +++ b/core/context/providers/FileTreeContextProvider.ts @@ -4,6 +4,7 @@ import { ContextProviderExtras, } from "../../index.js"; import { BaseContextProvider } from "../index.js"; +import { splitPath } from "../../util/index.js"; interface Directory { name: string; @@ -11,15 +12,6 @@ interface Directory { directories: Directory[]; } -function splitPath(path: string, withRoot?: string): string[] { - let parts = path.includes("/") ? path.split("/") : path.split("\\"); - if (withRoot !== undefined) { - const rootParts = splitPath(withRoot); - parts = parts.slice(rootParts.length - 1); - } - return parts; -} - function formatFileTree(tree: Directory, indentation = ""): string { let result = ""; for (const file of tree.files) { diff --git a/core/context/providers/OpenFilesContextProvider.ts b/core/context/providers/OpenFilesContextProvider.ts index 77203e01df..15a6322899 100644 --- a/core/context/providers/OpenFilesContextProvider.ts +++ b/core/context/providers/OpenFilesContextProvider.ts @@ -3,7 +3,7 @@ import { ContextProviderDescription, ContextProviderExtras, } from "../../index.js"; -import { getBasename } from "../../util/index.js"; +import { getRelativePath } from "../../util/index.js"; import { BaseContextProvider } from "../index.js"; class OpenFilesContextProvider extends BaseContextProvider { @@ -22,11 +22,12 @@ class OpenFilesContextProvider extends BaseContextProvider { const openFiles = this.options?.onlyPinned ? await ide.getPinnedFiles() : await ide.getOpenFiles(); + const workspaceDirs = await extras.ide.getWorkspaceDirs(); return await Promise.all( openFiles.map(async (filepath: string) => { return { description: filepath, - content: `\`\`\`${getBasename(filepath)}\n${await ide.readFile( + content: `\`\`\`${await getRelativePath(filepath, workspaceDirs)}\n${await ide.readFile( filepath, )}\n\`\`\``, name: (filepath.split("/").pop() ?? "").split("\\").pop() ?? "", diff --git a/core/context/providers/index.ts b/core/context/providers/index.ts index 7dd960715e..19bf88f365 100644 --- a/core/context/providers/index.ts +++ b/core/context/providers/index.ts @@ -1,6 +1,7 @@ import { ContextProviderName } from "../../index.js"; import { BaseContextProvider } from "../index.js"; import CodeContextProvider from "./CodeContextProvider.js"; +// import CodeHighlightsContextProvider from "./CodeHighlightsContextProvider.js"; import CodebaseContextProvider from "./CodebaseContextProvider.js"; import CurrentFileContextProvider from "./CurrentFileContextProvider.js"; import DatabaseContextProvider from "./DatabaseContextProvider.js"; diff --git a/core/context/retrieval/retrieval.ts b/core/context/retrieval/retrieval.ts index c950e5671d..f3218685bc 100644 --- a/core/context/retrieval/retrieval.ts +++ b/core/context/retrieval/retrieval.ts @@ -6,7 +6,7 @@ import { } from "../../index.js"; import { LanceDbIndex } from "../../indexing/LanceDbIndex.js"; -import { deduplicateArray, getBasename } from "../../util/index.js"; +import { deduplicateArray, getRelativePath } from "../../util/index.js"; import { RETRIEVAL_PARAMS } from "../../util/parameters.js"; import { retrieveFts } from "./fullTextSearch.js"; @@ -149,7 +149,7 @@ export async function retrieveContextItemsFromEmbeddings( return [ ...results.map((r) => { - const name = `${getBasename(r.filepath)} (${r.startLine}-${r.endLine})`; + const name = `${getRelativePath(r.filepath, workspaceDirs)} (${r.startLine}-${r.endLine})`; const description = `${r.filepath} (${r.startLine}-${r.endLine})`; return { name, diff --git a/core/index.d.ts b/core/index.d.ts index a182794491..9987b901b8 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -404,7 +404,10 @@ export interface IDE { stackDepth: number, ): Promise; getAvailableThreads(): Promise; - listWorkspaceContents(directory?: string): Promise; + listWorkspaceContents( + directory?: string, + useGitIgnore?: boolean, + ): Promise; listFolders(): Promise; getWorkspaceDirs(): Promise; getWorkspaceConfigs(): Promise; @@ -751,6 +754,7 @@ interface ExperimentalConfig { contextMenuPrompts?: ContextMenuConfig; modelRoles?: ModelRoles; defaultContext?: "activeFile"[]; + promptPath?: string; } export interface SerializedContinueConfig { diff --git a/core/llm/index.ts b/core/llm/index.ts index 1ad5945184..fca3e946f9 100644 --- a/core/llm/index.ts +++ b/core/llm/index.ts @@ -263,6 +263,7 @@ ${prompt}`; { ...this.requestOptions }, ); + // Error mapping to be more helpful if (!resp.ok) { let text = await resp.text(); if (resp.status === 404 && !resp.url.includes("/v1")) { @@ -276,6 +277,12 @@ ${prompt}`; text = "This may mean that you forgot to add '/v1' to the end of your 'apiBase' in config.json."; } + } else if ( + resp.status === 404 && + resp.url.includes("api.openai.com") + ) { + text = + "You may need to add pre-paid credits before using the OpenAI API."; } throw new Error( `HTTP ${resp.status} ${resp.statusText} from ${resp.url}\n\n${text}`, @@ -284,6 +291,10 @@ ${prompt}`; return resp; } catch (e: any) { + console.warn( + `${e.message}\n\nCode: ${e.code}\nError number: ${e.errno}\nSyscall: ${e.erroredSysCall}\nType: ${e.type}\n\n${e.stack}`, + ); + if ( e.code === "ECONNREFUSED" && e.message.includes("http://127.0.0.1:11434") @@ -292,9 +303,7 @@ ${prompt}`; "Failed to connect to local Ollama instance. To start Ollama, first download it at https://ollama.ai.", ); } - throw new Error( - `${e.message}\n\nCode: ${e.code}\nError number: ${e.errno}\nSyscall: ${e.erroredSysCall}\nType: ${e.type}\n\n${e.stack}`, - ); + throw new Error(e.message); } }; return withExponentialBackoff( diff --git a/core/llm/llms/LlamaCpp.ts b/core/llm/llms/LlamaCpp.ts index f9fb332e01..e37b445e69 100644 --- a/core/llm/llms/LlamaCpp.ts +++ b/core/llm/llms/LlamaCpp.ts @@ -16,6 +16,9 @@ class LlamaCpp extends BaseLLM { min_p: options.minP, mirostat: options.mirostat, stop: options.stop, + top_k: options.topK, + top_p: options.topP, + temperature: options.temperature, }; return finalOptions; diff --git a/core/package-lock.json b/core/package-lock.json index 471011b1f2..77bbe9e9da 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -1,12 +1,12 @@ { "name": "@continuedev/core", - "version": "1.0.12", + "version": "1.0.13", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@continuedev/core", - "version": "1.0.12", + "version": "1.0.13", "license": "Apache-2.0", "dependencies": { "@aws-sdk/client-bedrock-runtime": "^3.574.0", diff --git a/core/protocol/ide.ts b/core/protocol/ide.ts index 83048f4ef6..1975e2ca35 100644 --- a/core/protocol/ide.ts +++ b/core/protocol/ide.ts @@ -13,7 +13,10 @@ import { IdeSettings } from "./ideWebview"; export type ToIdeFromWebviewOrCoreProtocol = { // Methods from IDE type getIdeInfo: [undefined, IdeInfo]; - listWorkspaceContents: [undefined, string[]]; + listWorkspaceContents: [ + { directory?: string; useGitIgnore?: boolean }, + string[], + ]; getWorkspaceDirs: [undefined, string[]]; listFolders: [undefined, string[]]; writeFile: [{ path: string; contents: string }, void]; diff --git a/core/util/filesystem.ts b/core/util/filesystem.ts index a0cc2b5e1a..25bdd3d701 100644 --- a/core/util/filesystem.ts +++ b/core/util/filesystem.ts @@ -110,7 +110,10 @@ class FileSystemIde implements IDE { return Promise.resolve(); } - listWorkspaceContents(): Promise { + listWorkspaceContents( + directory?: string, + useGitIgnore?: boolean, + ): Promise { return new Promise((resolve, reject) => { fs.readdir("/tmp/continue", (err, files) => { if (err) { diff --git a/core/util/index.ts b/core/util/index.ts index ad6db9af55..49a299655f 100644 --- a/core/util/index.ts +++ b/core/util/index.ts @@ -1,3 +1,7 @@ +import { + ContextProviderExtras, + } from "../index.js"; + export function removeQuotesAndEscapes(output: string): string { output = output.trim(); @@ -97,6 +101,26 @@ export function getLastNPathParts(filepath: string, n: number): string { return filepath.split(/[\\/]/).slice(-n).join("/"); } +export function getRelativePath(filepath: string, workspaceDirs: string[]): string { + for (const workspaceDir of workspaceDirs) { + const filepathParts = splitPath(filepath); + const workspaceDirParts = splitPath(workspaceDir); + if (filepathParts.slice(0, workspaceDirParts.length).join('/') === workspaceDirParts.join('/')) { + return filepathParts.slice(workspaceDirParts.length).join('/'); + } + } + return splitPath(filepath).pop() ?? ''; // If the file is not in any of the workspaces, return the plain filename +} + +export function splitPath(path: string, withRoot?: string): string[] { + let parts = path.includes("/") ? path.split("/") : path.split("\\"); + if (withRoot !== undefined) { + const rootParts = splitPath(withRoot); + parts = parts.slice(rootParts.length - 1); + } + return parts; +} + export function getMarkdownLanguageTagForFile(filepath: string): string { const ext = filepath.split(".").pop(); switch (ext) { diff --git a/core/util/messageIde.ts b/core/util/messageIde.ts index 8b40a7d55d..b810450961 100644 --- a/core/util/messageIde.ts +++ b/core/util/messageIde.ts @@ -92,8 +92,14 @@ export class MessageIde implements IDE { return await this.request("getTerminalContents", undefined); } - async listWorkspaceContents(directory?: string): Promise { - return await this.request("listWorkspaceContents", undefined); + async listWorkspaceContents( + directory?: string, + useGitIgnore?: boolean, + ): Promise { + return await this.request("listWorkspaceContents", { + directory, + useGitIgnore, + }); } async getWorkspaceDirs(): Promise { diff --git a/docs/static/schemas/config.json b/docs/static/schemas/config.json index ac61c321af..0778af54d5 100644 --- a/docs/static/schemas/config.json +++ b/docs/static/schemas/config.json @@ -2067,6 +2067,9 @@ } } }, + "promptPath": { + "type": "string" + }, "contextMenuPrompts": { "type": "object", "properties": { diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/ConfigJsonSchemaProviderFactory.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/ConfigJsonSchemaProviderFactory.kt index 8357c3cf4c..c296286367 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/ConfigJsonSchemaProviderFactory.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/ConfigJsonSchemaProviderFactory.kt @@ -22,7 +22,7 @@ class ConfigJsonSchemaProviderFactory : JsonSchemaProviderFactory { class ConfigJsonSchemaFileProvider : JsonSchemaFileProvider { override fun isAvailable(file: VirtualFile): Boolean { - return file.name == "config.json" + return file.name == "config.json" || file.name == ".continuerc.json" } override fun getName(): String { diff --git a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt index b99ad9e2a0..466ef0e76d 100644 --- a/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt +++ b/extensions/intellij/src/main/kotlin/com/github/continuedev/continueintellijextension/continue/IdeProtocolClient.kt @@ -6,6 +6,7 @@ import com.github.continuedev.continueintellijextension.services.ContinueExtensi import com.github.continuedev.continueintellijextension.services.ContinuePluginService import com.google.gson.Gson import com.google.gson.GsonBuilder +import com.google.gson.JsonObject import com.google.gson.reflect.TypeToken import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerImpl @@ -316,7 +317,26 @@ class IdeProtocolClient ( } "getWorkspaceConfigs" -> { - respond(emptyList()) + val workspaceDirs = workspaceDirectories() + + val configs: List = listOf() + for (workspaceDir in workspaceDirs) { + val workspacePath = File(workspaceDir) + val dir = VirtualFileManager.getInstance().findFileByUrl("file://$workspacePath") + if (dir != null) { + val contents = dir.children.map { it.name } + + // Find any .continuerc.json files + for (file in contents) { + if (file.endsWith(".continuerc.json")) { + val filePath = workspacePath.resolve(file) + val fileContent = File(filePath.toString()).readText() + configs.plus(fileContent) + } + } + } + } + respond(configs) } "getTerminalContents" -> { diff --git a/extensions/intellij/src/main/resources/config_schema.json b/extensions/intellij/src/main/resources/config_schema.json index ac61c321af..0778af54d5 100644 --- a/extensions/intellij/src/main/resources/config_schema.json +++ b/extensions/intellij/src/main/resources/config_schema.json @@ -2067,6 +2067,9 @@ } } }, + "promptPath": { + "type": "string" + }, "contextMenuPrompts": { "type": "object", "properties": { diff --git a/extensions/vscode/config_schema.json b/extensions/vscode/config_schema.json index ac61c321af..0778af54d5 100644 --- a/extensions/vscode/config_schema.json +++ b/extensions/vscode/config_schema.json @@ -2067,6 +2067,9 @@ } } }, + "promptPath": { + "type": "string" + }, "contextMenuPrompts": { "type": "object", "properties": { diff --git a/extensions/vscode/continue_rc_schema.json b/extensions/vscode/continue_rc_schema.json index fcbd553581..f9dd22b9a7 100644 --- a/extensions/vscode/continue_rc_schema.json +++ b/extensions/vscode/continue_rc_schema.json @@ -270,7 +270,10 @@ "apiType": { "title": "Api Type", "markdownDescription": "OpenAI API type, either `openai` or `azure`", - "enum": ["openai", "azure"] + "enum": [ + "openai", + "azure" + ] }, "apiVersion": { "title": "Api Version", @@ -283,7 +286,11 @@ "type": "string" } }, - "required": ["title", "provider", "model"], + "required": [ + "title", + "provider", + "model" + ], "allOf": [ { "if": { @@ -293,7 +300,9 @@ } }, "not": { - "required": ["provider"] + "required": [ + "provider" + ] } }, "then": { @@ -320,33 +329,48 @@ ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { - "required": ["apiKey"] + "required": [ + "apiKey" + ] } }, { "if": { "properties": { "provider": { - "enum": ["huggingface-tgi", "huggingface-inference-api"] + "enum": [ + "huggingface-tgi", + "huggingface-inference-api" + ] } } }, "then": { - "required": ["apiBase"] + "required": [ + "apiBase" + ] }, - "required": ["provider"] + "required": [ + "provider" + ] }, { "if": { "properties": { "provider": { - "enum": ["openai"] + "enum": [ + "openai" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -366,10 +390,14 @@ "if": { "properties": { "provider": { - "enum": ["cloudflare"] + "enum": [ + "cloudflare" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -424,10 +452,14 @@ "if": { "properties": { "provider": { - "enum": ["openai"] + "enum": [ + "openai" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -458,10 +490,14 @@ "if": { "properties": { "provider": { - "enum": ["replicate"] + "enum": [ + "replicate" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -491,10 +527,14 @@ "if": { "properties": { "provider": { - "enum": ["free-trial"] + "enum": [ + "free-trial" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -519,7 +559,9 @@ "if": { "properties": { "provider": { - "enum": ["openai"] + "enum": [ + "openai" + ] }, "apiType": { "not": { @@ -527,7 +569,9 @@ } } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -585,10 +629,14 @@ "if": { "properties": { "provider": { - "enum": ["anthropic"] + "enum": [ + "anthropic" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -616,15 +664,22 @@ "if": { "properties": { "provider": { - "enum": ["cohere"] + "enum": [ + "cohere" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { "model": { - "enum": ["command-r", "command-r-plus"] + "enum": [ + "command-r", + "command-r-plus" + ] } } } @@ -633,10 +688,14 @@ "if": { "properties": { "provider": { - "enum": ["bedrock"] + "enum": [ + "bedrock" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -661,10 +720,14 @@ "if": { "properties": { "provider": { - "enum": ["gemini"] + "enum": [ + "gemini" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -683,10 +746,14 @@ "if": { "properties": { "provider": { - "enum": ["together"] + "enum": [ + "together" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -720,10 +787,14 @@ "if": { "properties": { "provider": { - "enum": ["deepinfra"] + "enum": [ + "deepinfra" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -746,7 +817,9 @@ ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -792,10 +865,14 @@ "if": { "properties": { "provider": { - "enum": ["ollama"] + "enum": [ + "ollama" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -843,10 +920,14 @@ "if": { "properties": { "provider": { - "enum": ["mistral"] + "enum": [ + "mistral" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -867,10 +948,14 @@ "if": { "properties": { "provider": { - "enum": ["groq"] + "enum": [ + "groq" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -891,15 +976,21 @@ "if": { "properties": { "provider": { - "enum": ["fireworks"] + "enum": [ + "fireworks" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { "model": { - "enum": ["starcoder-7b"] + "enum": [ + "starcoder-7b" + ] } } } @@ -911,20 +1002,30 @@ "const": "azure" } }, - "required": ["apiType"] + "required": [ + "apiType" + ] }, "then": { - "required": ["engine", "apiVersion", "apiBase"] + "required": [ + "engine", + "apiVersion", + "apiBase" + ] } }, { "if": { "properties": { "provider": { - "enum": ["openai"] + "enum": [ + "openai" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -941,7 +1042,9 @@ "const": "llamafile" } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -955,10 +1058,14 @@ "if": { "properties": { "provider": { - "enum": ["text-gen-webui"] + "enum": [ + "text-gen-webui" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -972,10 +1079,14 @@ "if": { "properties": { "provider": { - "enum": ["flowise"] + "enum": [ + "flowise" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { "properties": { @@ -1002,7 +1113,10 @@ "type": "string" } }, - "required": ["key", "value"] + "required": [ + "key", + "value" + ] } }, "additionalFlowiseConfiguration": { @@ -1021,7 +1135,10 @@ "description": "Configuration Property value" } }, - "required": ["key", "value"] + "required": [ + "key", + "value" + ] } }, "model": { @@ -1111,7 +1228,9 @@ "type": "string" } }, - "required": ["default"] + "required": [ + "default" + ] }, "SlashCommand": { "title": "SlashCommand", @@ -1170,7 +1289,9 @@ "if": { "properties": { "name": { - "enum": ["issue"] + "enum": [ + "issue" + ] } } }, @@ -1183,17 +1304,23 @@ "description": "Enter the URL of the GitHub repository for which you want to generate the issue." } }, - "required": ["repositoryUrl"] + "required": [ + "repositoryUrl" + ] } }, - "required": ["params"] + "required": [ + "params" + ] } }, { "if": { "properties": { "name": { - "enum": ["edit"] + "enum": [ + "edit" + ] } } }, @@ -1218,7 +1345,9 @@ "if": { "properties": { "name": { - "enum": ["share"] + "enum": [ + "share" + ] } } }, @@ -1236,7 +1365,10 @@ } } ], - "required": ["name", "description"] + "required": [ + "name", + "description" + ] }, "CustomCommand": { "title": "CustomCommand", @@ -1255,7 +1387,11 @@ "type": "string" } }, - "required": ["name", "prompt", "description"] + "required": [ + "name", + "prompt", + "description" + ] }, "ContextProviderWithParams": { "title": "ContextProviderWithParams", @@ -1328,7 +1464,9 @@ "if": { "properties": { "name": { - "enum": ["google"] + "enum": [ + "google" + ] } } }, @@ -1341,17 +1479,23 @@ "description": "Your API key for https://serper.dev in order to get Google search results" } }, - "required": ["serperApiKey"] + "required": [ + "serperApiKey" + ] } }, - "required": ["params"] + "required": [ + "params" + ] } }, { "if": { "properties": { "name": { - "enum": ["open"] + "enum": [ + "open" + ] } } }, @@ -1373,7 +1517,9 @@ "if": { "properties": { "name": { - "enum": ["issue"] + "enum": [ + "issue" + ] } } }, @@ -1402,24 +1548,37 @@ "type": { "type": "string", "description": "The type of issues to search for", - "enum": ["open", "closed", "all"] + "enum": [ + "open", + "closed", + "all" + ] } }, - "required": ["owner", "repo"] + "required": [ + "owner", + "repo" + ] } } }, - "required": ["repos"] + "required": [ + "repos" + ] } }, - "required": ["params"] + "required": [ + "params" + ] } }, { "if": { "properties": { "name": { - "enum": ["database"] + "enum": [ + "database" + ] } } }, @@ -1437,7 +1596,11 @@ "connection_type": { "type": "string", "description": "The type of database (e.g., 'postgres', 'mysql')", - "enum": ["postgres", "mysql", "sqlite"] + "enum": [ + "postgres", + "mysql", + "sqlite" + ] }, "connection": { "type": "object", @@ -1470,17 +1633,25 @@ "required": [] } }, - "required": ["name", "type", "connection"] + "required": [ + "name", + "type", + "connection" + ] } }, - "required": ["connections"] + "required": [ + "connections" + ] } }, { "if": { "properties": { "name": { - "enum": ["gitlab-mr"] + "enum": [ + "gitlab-mr" + ] } } }, @@ -1501,17 +1672,23 @@ "description": "If you have code selected, filters out comments that aren't related to the selection." } }, - "required": ["token"] + "required": [ + "token" + ] } }, - "required": ["params"] + "required": [ + "params" + ] } }, { "if": { "properties": { "name": { - "enum": ["jira"] + "enum": [ + "jira" + ] } } }, @@ -1557,17 +1734,24 @@ ] } }, - "required": ["domain", "token"] + "required": [ + "domain", + "token" + ] } }, - "required": ["params"] + "required": [ + "params" + ] } }, { "if": { "properties": { "name": { - "enum": ["http"] + "enum": [ + "http" + ] } } }, @@ -1580,17 +1764,24 @@ "description": "The HTTP endpoint of your context provider server." } }, - "required": ["url"] + "required": [ + "url" + ] } }, - "required": ["params"] + "required": [ + "params" + ] } }, { "if": { "properties": { "name": { - "enum": ["codebase", "folder"] + "enum": [ + "codebase", + "folder" + ] } } }, @@ -1625,7 +1816,9 @@ "if": { "properties": { "name": { - "enum": ["postgres"] + "enum": [ + "postgres" + ] } } }, @@ -1677,11 +1870,19 @@ } } }, - "required": ["host", "port", "user", "password", "database"] + "required": [ + "host", + "port", + "user", + "password", + "database" + ] } } ], - "required": ["name"] + "required": [ + "name" + ] }, "SerializedContinueConfig": { "title": "config.json", @@ -1832,32 +2033,46 @@ "$ref": "#/definitions/RequestOptions" } }, - "required": ["provider"], + "required": [ + "provider" + ], "allOf": [ { "if": { "properties": { "provider": { - "enum": ["ollama"] + "enum": [ + "ollama" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { - "required": ["model"] + "required": [ + "model" + ] } }, { "if": { "properties": { "provider": { - "enum": ["cohere"] + "enum": [ + "cohere" + ] } }, - "required": ["provider"] + "required": [ + "provider" + ] }, "then": { - "required": ["apiKey"] + "required": [ + "apiKey" + ] } } ] @@ -1868,22 +2083,33 @@ "type": "object", "properties": { "name": { - "enum": ["cohere", "voyage", "llm", "free-trial"] + "enum": [ + "cohere", + "voyage", + "llm", + "free-trial" + ] }, "params": { "type": "object" } }, - "required": ["name"], + "required": [ + "name" + ], "allOf": [ { "if": { "properties": { "name": { - "enum": ["cohere"] + "enum": [ + "cohere" + ] } }, - "required": ["name"] + "required": [ + "name" + ] }, "then": { "properties": { @@ -1905,7 +2131,9 @@ "type": "string" } }, - "required": ["apiKey"] + "required": [ + "apiKey" + ] } } } @@ -1914,10 +2142,14 @@ "if": { "properties": { "name": { - "enum": ["llm"] + "enum": [ + "llm" + ] } }, - "required": ["name"] + "required": [ + "name" + ] }, "then": { "properties": { @@ -1928,7 +2160,9 @@ "type": "string" } }, - "required": ["modelTitle"] + "required": [ + "modelTitle" + ] } } } @@ -1937,10 +2171,14 @@ "if": { "properties": { "name": { - "enum": ["voyage"] + "enum": [ + "voyage" + ] } }, - "required": ["name"] + "required": [ + "name" + ] }, "then": { "properties": { @@ -1951,10 +2189,14 @@ "type": "string" }, "model": { - "enum": ["rerank-lite-1"] + "enum": [ + "rerank-lite-1" + ] } }, - "required": ["apiKey"] + "required": [ + "apiKey" + ] } } } @@ -2010,7 +2252,11 @@ "description": "An optional template string to be used for autocomplete. It will be rendered with the Mustache templating language, and is passed the 'prefix' and 'suffix' variables." }, "multilineCompletions": { - "enum": ["always", "never", "auto"], + "enum": [ + "always", + "never", + "auto" + ], "description": "If set to true, Continue will only complete a single line at a time." }, "useCache": { @@ -2039,7 +2285,10 @@ "type": "object", "properties": { "codeBlockToolbarPosition": { - "enum": ["top", "bottom"], + "enum": [ + "top", + "bottom" + ], "default": "top", "description": "Whether to show the copy and apply code buttons at the top or bottom of code blocks in the sidebar." }, @@ -2056,7 +2305,9 @@ "defaultContext": { "type": "array", "items": { - "enum": ["activeFile"] + "enum": [ + "activeFile" + ] } }, "modelRoles": { @@ -2067,6 +2318,9 @@ } } }, + "promptPath": { + "type": "string" + }, "contextMenuPrompts": { "type": "object", "properties": { @@ -2096,7 +2350,10 @@ }, "mergeBehavior": { "type": "string", - "enum": ["merge", "overwrite"], + "enum": [ + "merge", + "overwrite" + ], "default": "merge", "title": "Merge behavior", "markdownDescription": "If set to 'merge', .continuerc.json will be applied on top of config.json (arrays and objects are merged). If set to 'overwrite', then every top-level property of .continuerc.json will overwrite that property from config.json." @@ -2104,4 +2361,4 @@ } } } -} +} \ No newline at end of file diff --git a/extensions/vscode/scripts/prepackage.js b/extensions/vscode/scripts/prepackage.js index d42a7a21d1..a9e9d8dc44 100644 --- a/extensions/vscode/scripts/prepackage.js +++ b/extensions/vscode/scripts/prepackage.js @@ -269,17 +269,17 @@ const exe = os === "win32" ? ".exe" : ""; console.log("[info] Copied tree-sitter wasms"); // tree-sitter tag query files - // ncp( - // path.join( - // __dirname, - // "../../../core/node_modules/llm-code-highlighter/dist/tag-qry" - // ), - // path.join(__dirname, "../out/tag-qry"), - // (error) => { - // if (error) - // console.warn("Error copying code-highlighter tag-qry files", error); - // } - // ); + ncp( + path.join( + __dirname, + "../../../core/node_modules/llm-code-highlighter/dist/tag-qry", + ), + path.join(__dirname, "../out/tag-qry"), + (error) => { + if (error) + console.warn("Error copying code-highlighter tag-qry files", error); + }, + ); // textmate-syntaxes await new Promise((resolve, reject) => { diff --git a/extensions/vscode/src/extension/VsCodeMessenger.ts b/extensions/vscode/src/extension/VsCodeMessenger.ts index 7b6ea40f00..075f4adc49 100644 --- a/extensions/vscode/src/extension/VsCodeMessenger.ts +++ b/extensions/vscode/src/extension/VsCodeMessenger.ts @@ -219,7 +219,10 @@ export class VsCodeMessenger { ); }); this.onWebviewOrCore("listWorkspaceContents", async (msg) => { - return ide.listWorkspaceContents(); + return ide.listWorkspaceContents( + msg.data.directory, + msg.data.useGitIgnore, + ); }); this.onWebviewOrCore("getWorkspaceDirs", async (msg) => { return ide.getWorkspaceDirs(); diff --git a/extensions/vscode/src/extension/vscodeExtension.ts b/extensions/vscode/src/extension/vscodeExtension.ts index b89b30e5a6..c5ef8006d4 100644 --- a/extensions/vscode/src/extension/vscodeExtension.ts +++ b/extensions/vscode/src/extension/vscodeExtension.ts @@ -32,12 +32,18 @@ export class VsCodeExtension { private windowId: string; private diffManager: DiffManager; private verticalDiffManager: VerticalPerLineDiffManager; - webviewProtocol: VsCodeWebviewProtocol; + webviewProtocolPromise: Promise; private core: Core; constructor(context: vscode.ExtensionContext) { + let resolveWebviewProtocol: any = undefined; + this.webviewProtocolPromise = new Promise( + (resolve) => { + resolveWebviewProtocol = resolve; + }, + ); this.diffManager = new DiffManager(context); - this.ide = new VsCodeIde(this.diffManager); + this.ide = new VsCodeIde(this.diffManager, this.webviewProtocolPromise); this.extensionContext = context; this.windowId = uuidv4(); @@ -71,7 +77,7 @@ export class VsCodeExtension { }, ), ); - this.webviewProtocol = this.sidebar.webviewProtocol; + resolveWebviewProtocol(this.sidebar.webviewProtocol); // Config Handler with output channel const outputChannel = vscode.window.createOutputChannel( @@ -83,7 +89,7 @@ export class VsCodeExtension { >(); const vscodeMessenger = new VsCodeMessenger( inProcessMessenger, - this.webviewProtocol, + this.sidebar.webviewProtocol, this.ide, verticalDiffManagerPromise, ); @@ -99,7 +105,7 @@ export class VsCodeExtension { this.configHandler = this.core.configHandler; resolveConfigHandler?.(this.configHandler); this.configHandler.onConfigUpdate(() => { - this.webviewProtocol?.request("configUpdate", undefined); + this.sidebar.webviewProtocol?.request("configUpdate", undefined); }); this.configHandler.reloadConfig(); @@ -114,7 +120,7 @@ export class VsCodeExtension { ); // Indexing + pause token - this.diffManager.webviewProtocol = this.webviewProtocol; + this.diffManager.webviewProtocol = this.sidebar.webviewProtocol; // CodeLens const verticalDiffCodeLens = registerAllCodeLensProviders( @@ -153,7 +159,7 @@ export class VsCodeExtension { this.verticalDiffManager, ); - registerDebugTracker(this.webviewProtocol, this.ide); + registerDebugTracker(this.sidebar.webviewProtocol, this.ide); // Listen for file saving - use global file watcher so that changes // from outside the window are also caught @@ -184,6 +190,13 @@ export class VsCodeExtension { } }); + // When GitHub sign-in status changes, reload config + vscode.authentication.onDidChangeSessions((e) => { + if (e.provider.id === "github") { + this.configHandler.reloadConfig(); + } + }); + // Refresh index when branch is changed this.ide.getWorkspaceDirs().then((dirs) => dirs.forEach(async (dir) => { diff --git a/extensions/vscode/src/ideProtocol.ts b/extensions/vscode/src/ideProtocol.ts index b05e7183e0..3e7e5468bf 100644 --- a/extensions/vscode/src/ideProtocol.ts +++ b/extensions/vscode/src/ideProtocol.ts @@ -9,9 +9,9 @@ import type { IdeInfo, IndexTag, Problem, - Range, Thread, } from "core"; +import { Range } from "core"; import { defaultIgnoreFile } from "core/indexing/ignore"; import { IdeSettings } from "core/protocol/ideWebview"; import { @@ -29,11 +29,15 @@ import { openEditorAndRevealRange, uriFromFilePath, } from "./util/vscode"; +import { VsCodeWebviewProtocol } from "./webviewProtocol"; class VsCodeIde implements IDE { ideUtils: VsCodeIdeUtils; - constructor(private readonly diffManager: DiffManager) { + constructor( + private readonly diffManager: DiffManager, + private readonly vscodeWebviewProtocolPromise: Promise, + ) { this.ideUtils = new VsCodeIdeUtils(); } @@ -41,10 +45,79 @@ class VsCodeIde implements IDE { private askedForAuth = false; async getGitHubAuthToken(): Promise { + // Saved auth token if (this.authToken) { return this.authToken; } + + // Try to ask silently + const session = await vscode.authentication.getSession("github", [], { + silent: true, + }); + if (session) { + this.authToken = session.accessToken; + return this.authToken; + } + try { + // If we haven't asked yet, give explanation of what is happening and why + // But don't wait to return this immediately + // We will use a callback to refresh the config + if (!this.askedForAuth) { + vscode.window + .showInformationMessage( + "Continue will request read access to your GitHub email so that we can prevent abuse of the free trial. If you prefer not to sign in, you can use Continue with your own API keys or local model.", + "Sign in", + "Use API key / local model", + "Learn more", + ) + .then(async (selection) => { + if (selection === "Use API key / local model") { + await vscode.commands.executeCommand( + "continue.continueGUIView.focus", + ); + (await this.vscodeWebviewProtocolPromise).request( + "openOnboarding", + undefined, + ); + + // Remove free trial models + editConfigJson((config) => { + const tabAutocompleteModel = + config.tabAutocompleteModel?.provider === "free-trial" + ? undefined + : config.tabAutocompleteModel; + return { + ...config, + models: config.models.filter( + (model) => model.provider !== "free-trial", + ), + tabAutocompleteModel, + }; + }); + } else if (selection === "Learn more") { + vscode.env.openExternal( + vscode.Uri.parse( + "https://docs.continue.dev/reference/Model%20Providers/freetrial", + ), + ); + } else if (selection === "Sign in") { + const session = await vscode.authentication.getSession( + "github", + [], + { + createIfNone: true, + }, + ); + if (session) { + this.authToken = session.accessToken; + } + } + }); + this.askedForAuth = true; + return undefined; + } + const session = await vscode.authentication.getSession("github", [], { silent: this.askedForAuth, createIfNone: !this.askedForAuth, @@ -77,8 +150,6 @@ class VsCodeIde implements IDE { } } catch (error) { console.error("Failed to get GitHub authentication session:", error); - } finally { - this.askedForAuth = true; } return undefined; } @@ -197,14 +268,23 @@ class VsCodeIde implements IDE { return await this.ideUtils.getAvailableThreads(); } - async listWorkspaceContents(directory?: string): Promise { + async listWorkspaceContents( + directory?: string, + useGitIgnore?: boolean, + ): Promise { if (directory) { - return await this.ideUtils.getDirectoryContents(directory, true); + return await this.ideUtils.getDirectoryContents( + directory, + true, + useGitIgnore ?? true, + ); } const contents = await Promise.all( this.ideUtils .getWorkspaceDirectories() - .map((dir) => this.ideUtils.getDirectoryContents(dir, true)), + .map((dir) => + this.ideUtils.getDirectoryContents(dir, true, useGitIgnore ?? true), + ), ); return contents.flat(); } @@ -237,6 +317,7 @@ class VsCodeIde implements IDE { [], false, undefined, + true, )) { allDirs.push(dir); } diff --git a/extensions/vscode/src/test/test-suites/main.test.ts b/extensions/vscode/src/test/test-suites/main.test.ts index 15e6d8e4ee..348d1be11f 100644 --- a/extensions/vscode/src/test/test-suites/main.test.ts +++ b/extensions/vscode/src/test/test-suites/main.test.ts @@ -1,5 +1,5 @@ -import * as assert from "node:assert"; import { describe, test } from "mocha"; +import * as assert from "node:assert"; import * as vscode from "vscode"; import { vscodeExtensionPromise } from "../../activation/activate"; @@ -16,10 +16,9 @@ describe("Extension Test Suite", () => { const extension = await vscodeExtensionPromise; await vscode.commands.executeCommand("continue.continueGUIView.focus"); await new Promise((resolve) => setTimeout(resolve, 3_000)); - const title = await extension.webviewProtocol.request( - "getDefaultModelTitle", - undefined, - ); + const title = await ( + await extension.webviewProtocolPromise + ).request("getDefaultModelTitle", undefined); console.log("Title of default model is: ", title); assert.strictEqual(typeof title, "string"); }); diff --git a/extensions/vscode/src/util/ideUtils.ts b/extensions/vscode/src/util/ideUtils.ts index 90d9a773d8..4ec3df4134 100644 --- a/extensions/vscode/src/util/ideUtils.ts +++ b/extensions/vscode/src/util/ideUtils.ts @@ -251,6 +251,7 @@ export class VsCodeIdeUtils { async getDirectoryContents( directory: string, recursive: boolean, + useGitIgnore: boolean, ): Promise { if (!recursive) { return ( @@ -276,6 +277,7 @@ export class VsCodeIdeUtils { [], true, gitRoot === directory ? undefined : onlyThisDirectory, + useGitIgnore, )) { allFiles.push(file); } diff --git a/extensions/vscode/src/util/traverseDirectory.ts b/extensions/vscode/src/util/traverseDirectory.ts index e4daa6864d..5040a050a5 100644 --- a/extensions/vscode/src/util/traverseDirectory.ts +++ b/extensions/vscode/src/util/traverseDirectory.ts @@ -28,6 +28,7 @@ export async function* traverseDirectory( gitIgnorePatterns: string[], returnFiles = true, onlyThisDirectory: string[] | undefined, + useGitIgnore: boolean, ): AsyncGenerator { const nodes = await vscode.workspace.fs.readDirectory( uriFromFilePath(directory), @@ -79,7 +80,7 @@ export async function* traverseDirectory( if (!onlyThisDirectory) { for (const node of returnFiles ? files : dirs) { - if (!ig.ignores(node)) { + if (!useGitIgnore || !ig.ignores(node)) { yield path.join(directory, node); } } @@ -107,7 +108,7 @@ export async function* traverseDirectory( } // Recurse if not ignored - if (!(ig.ignores(`${dir}/`) || ig.ignores(dir))) { + if (!useGitIgnore || !(ig.ignores(`${dir}/`) || ig.ignores(dir))) { // For patterns who can potentially match items of this subdir, strip the subdir from the start const keepPatterns = [...wildcardPatterns]; for (const [startPattern, subDirPatterns] of entries) { @@ -122,6 +123,7 @@ export async function* traverseDirectory( onlyThisDirectory && onlyThisDirectory.length > 1 ? onlyThisDirectory.slice(1) : undefined, + useGitIgnore, )) { yield file; } diff --git a/gui/src/components/mainInput/resolveInput.ts b/gui/src/components/mainInput/resolveInput.ts index 5f36e82f28..14ac3bf4e3 100644 --- a/gui/src/components/mainInput/resolveInput.ts +++ b/gui/src/components/mainInput/resolveInput.ts @@ -7,7 +7,7 @@ import { RangeInFile, } from "core"; import { stripImages } from "core/llm/countTokens"; -import { getBasename } from "core/util"; +import { getBasename, getRelativePath } from "core/util"; import { IIdeMessenger } from "../../context/IdeMessenger"; interface MentionAttrs { @@ -96,8 +96,9 @@ async function resolveEditorContent( if (item.itemType === "file") { // This is a quick way to resolve @file references const basename = getBasename(item.id); + const relativeFilePath = getRelativePath(item.id, await ideMessenger.ide.getWorkspaceDirs()); const rawContent = await ideMessenger.ide.readFile(item.id); - const content = `\`\`\`title="${basename}"\n${rawContent}\n\`\`\`\n`; + const content = `\`\`\`${relativeFilePath}\n${rawContent}\n\`\`\`\n`; contextItemsText += content; contextItems.push({ name: basename, diff --git a/gui/src/hooks/useChatHandler.ts b/gui/src/hooks/useChatHandler.ts index b57b8c4b74..789f73266b 100644 --- a/gui/src/hooks/useChatHandler.ts +++ b/gui/src/hooks/useChatHandler.ts @@ -13,7 +13,7 @@ import { } from "core"; import { constructMessages } from "core/llm/constructMessages"; import { stripImages } from "core/llm/countTokens"; -import { getBasename } from "core/util"; +import { getBasename, getRelativePath } from "core/util"; import { usePostHog } from "posthog-js/react"; import { useEffect, useRef } from "react"; import { useSelector } from "react-redux"; @@ -179,8 +179,9 @@ function useChatHandler(dispatch: Dispatch, ideMessenger: IIdeMessenger) { .join("\n"); } contextItems.unshift({ - content: `The following file is currently open. Don't reference it if it's not relevant to the user's message.\n\n\`\`\`${getBasename( + content: `The following file is currently open. Don't reference it if it's not relevant to the user's message.\n\n\`\`\`${getRelativePath( currentFilePath, + await ideMessenger.ide.getWorkspaceDirs(), )}\n${currentFileContents}\n\`\`\``, name: `Active file: ${getBasename(currentFilePath)}`, description: currentFilePath,