From 2e51db04c7ecb4a83574717a7d5184d5e0bbae01 Mon Sep 17 00:00:00 2001 From: Nate Sesti <33237525+sestinj@users.noreply.github.com> Date: Tue, 4 Jun 2024 12:06:42 -0700 Subject: [PATCH] Preview (#1419) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * πŸ› fixes after refactor * 🚧 experimenting with perplexity style streaming * :bug: fix #1237 * πŸ’š fix type error * ⚑️ improve LSP usage in autocomplete * πŸ› fix content parsing regression in /edit * add PySide6 docs to preindexed docs (#1236) * CON-232 bring custom docs to top, alphabetize doc results, make scrol… (#1239) * CON-232 bring custom docs to top, alphabetize doc results, make scrollable * CON-232 cleanup --------- Co-authored-by: Justin Milner * CON-1067 condense some things * 🚚 [Auxiliary -> Continue] Sidebar * πŸ”Š log completion options in ~/.continue/sessions * CON-1067 wrong ret val fix * CON-1067: fixes from testing * ⚑️ filter out completions that are only punctuation/space * ⚑️ inject intellisense docs, no multi-line on comments * ⚑️ crawl type definitions for autocomplete * ⚑️ truncate function text * ⚑️ cache LSP calls * ⚑️ find recently edited ranges with perfect prefix match * πŸ› fix gif paths * ⚑️ bring back double new line stop words * πŸ“Œ add yarn lock files * πŸ› allow language keywords to be generated * πŸ’„ toggle on help button * 🎨 defaultContext option * πŸ› fix lancedb bug by upgrading * πŸ› fix groq stop tokens * πŸ› preventDefault to avoid double paste * 🚸 don't repeatedly override cmd+J * πŸ§‘β€πŸ’» fix npm run test in core * πŸ“ change description * πŸ› silence Ollama invalid server state warning * ⚑️ more accurate getTerminalContents * ⚑️ make getTerminalContents more accurate * πŸ§‘β€πŸ’» use yarn instead of npm * πŸ‘· fix yarn add --no-save in prepackge * πŸ› correctly read entire notebook file contents * βž• import handlebars * πŸ”₯ remove unnecessary migrations * ⚑️ improve /comment prompt * Add debug terminal context menu (#1261) * Add --no-dependencies to vsce package (#1255) This is not needed because we bundle first with esbuild, and vsce pack has issues with modern package managers. see: microsoft/vscode-vsce#421 (comment) * ui: change line decoration color to use vscode theme (#1253) * ui: change line decoration color to use vscode theme to give user a more consistent experience by letting the decoration color to user the color defined in the theme. * fix: incorrect color item should be line background not text background because the decoration is for the whole line * 🎨 refactor indexing state into a single object * CON-223 Correct diff streaming abort (#1263) Co-authored-by: Justin Milner * πŸ“¦ switch to pnpm (#1265) * 🎨 use pnpm instead of yarn * βž• make dependencies explicit for pnpm * πŸ› add powershell to extension mapping, and default to ext * Add stream support for Bedrock Anthropic * 🎨 make llamatokenizer commonjs compatible * βž• add esbuild optional deps * 🚚 rename vendor/node_modules -> modules * πŸ”– update core version * πŸ› fixes for transformers.js compatibility * πŸ”– update core version * 🎨 set modelPath in constructor * 🎨 fix transformers.js import * 🎨 eslint enforce import extensions * 🎨 require -> import * 🚸 notify user if not diff in /commit * πŸ’„ Improve colors of the IntelliJ tool window icon (#1273) Without this fix, the continue icon sticks out from the other toolwindow icons, resulting in an inconsistent appearance of the whole IDE and creates a feeling that the continue plugin "doesn't fit, something must be broken". According to https://plugins.jetbrains.com/docs/intellij/icons.html#new-ui-icon-colors specific colors are needed to work nicely with dark and light modes. Bonus is that the active tool window icon color then changes automatically to white. Co-authored-by: Lukas Baron * ✨ send Bearer token to Ollama if apiKey set * πŸ› fix slash command bug * πŸ§‘β€πŸ’» add onnxruntime (--save-dev) for types * πŸ› don't apply to file when running code block in terminal * πŸ› avoid double paste * ✨ gpt-4o * 🎨 pass uniqueId to constructor * 🚸 focus without scrolling into vie * 🎨 refactoring for continue-proxy LLM (#1277) * πŸ› continue server client fixes * 🎨 refactor OpenAI _getHeaders * 🎨 pass ideSettings to llmFromDescription * ⚑️ improve add docstring command * πŸ’š ci updates * πŸ› merge fixes * πŸ’„ increase font size in JB * πŸ› fix repeated paste bug * πŸ› fix build script * 🩹 various small improvements * πŸ“Œ pin esbuild 0.17.19 * πŸ§‘β€πŸ’» pnpm i gui in prepackage * πŸ› show all diff changes in vscode * 🩹 getMetaKeyName * πŸ› fix reading of unopened ipynb files * ⚑️ gpt-4o system prompt * πŸ’„ make font size configurable in config.json ui.fontSize * 🩹 properly dispose of diff handler * πŸ› fix indexing status display * ⚑️ context pruning * 🎨 update free trial models * fix: remove some backup files generated by pkg if present (#1287) * adjust toCopy (#1305) Co-authored-by: Justin Milner * Nate/prompt-file (#1308) * ✨ .prompt files * πŸ§‘β€πŸ’» back to npm : ( * 🎨 nicer dropdown icon * 🎨 full switch back to npm * πŸ§‘β€πŸ’» uninstall script for fresh start * 🎨 updated package-locks * πŸ”₯ remove hello from continuerc.json * πŸ”₯ remove example files * 🎨 update test prompt * 🎨 update prompt * 🎨 update test prompt * πŸ‘· create out/node_modules in prepackage.js * Log prompt-tokens to devdb and show in 'My Usage' view (#1309) * Also log the number of prompt tokens to the dev-db * Show prompt tokens in 'My Usage' view * πŸ”₯ remove console logs * add lm studio tab-autocomplete walkthrough (#1298) * πŸ“ update autocomplete LM Studio docs * πŸ’š fix prepackage.js * ⬆️ upgrade lancedb version * 🚸 make it easier to continue past trial * 🚸 improve model setup process * πŸ“Œ update package-lock.jsons * πŸ”€ merge changes * 🩹 small fixes * πŸ› fix jetbrains build * 🩹 more jetbrains fixes * 🎨 jetbrains improvements * πŸ› fix lancedb prob in jetbrains build * πŸ‘· update jetbrains build process * ✨ intellij problems context provider * πŸ‘· add script utils file * πŸ’š fix jetbrains build regression * 🎨 dynamic import transformers.js * 🩹 small jetbrains updates * ✨ folder context provider in jetbrains * 🎨 many more jetbrains improvements * πŸ› fix prompt file loading bug * docs: add setup guide for OpenRouter (#1284) * Update preIndexedDocs.ts (#1292) Add Bootstrap and Alpine.js as pre indexed docs. * add toString (#1324) * add toString * Fix indentation --------- Co-authored-by: Justin Milner * don't overwrite all models in onboarding * πŸ”§ override headers with custom headers * ✨ allow overwriting slash command description * feature flag (#1327) * βž• feature flags dependency * 🎨 make getHeaders awaitable * πŸ”’οΈ token count for abuse monitoring * πŸ‘· remove win32-arm64 target * Update build.js (#1330) Fix error: EBUSY: resource busy or locked, rmdir '...\continue\binary\tmp\continue-node_modules-lancedb' * πŸ“Œ pin onnxruntime version to match transformers.js * 🎨 transformers cleanup, extensionVersion header * πŸ”₯ remove lingering pnpm * 🎨 update default models * 🎨 update defaults * πŸ‘· fix version in build post-check * 🎨 test.js in dev * πŸ§‘β€πŸ’» cleaning * Add Gemini 1.5 Flash as a model (#1337) * 🎨 log dev data for tokens generated * Expose custom context provider registration through a vscode extension api. (#1288) * πŸ”€ merge fixes * ⚑️ small improvements * 🎨 tell users transformers.js not supported * 🎨 trial updates * πŸ“ update jetbrains readmej * ⚑️ Use gptEditPrompt for Mistral * ✨ Codestral FIM * πŸ”– update gradle version * Fix typo "Experimantal" (#1353) * 🎨 Refactor core (#1281) * 🎨 refactor out react context * 🎨 refactor local storage * 🎨 refactor IdeMessenger * πŸ”₯ removing unused files * 🚚 move scripts * βœ… setup VSCode integration testing * 🎨 fix test-related stuff * 🚧 testing * 🎨 tweak terminal cmd+L * πŸ§‘β€πŸ’» install biome in core * 🎨 run biome check on core * 🎨 run biome check in vscode * 🎨 run biome check in binary * πŸ§‘β€πŸ’» global biome config * 🎨 run biome check on core * 🎨 run biome check in vscode * 🎨 fix a few biome warnings * 🚧 WIP on protocols * 🚧 progress, organizing, about to remove webviewCore protocol * 🚧 now running * 🚧 move indexing code to core * 🚧 WIP on JetBrains indexing implementation * 🎨 finish listDir * 🚧 gui fixes * 🏷️ update IMessenger.on return type * πŸ› fix jetbrains types + IDE detection * πŸ§‘β€πŸ’» set up debugging for binary with TcpMessenger * πŸ‘· fix prepackage.js * πŸ§‘β€πŸ’» turn off debug mode for intellij * πŸ› merge fixes * πŸ› fixes after refactor * πŸ› merge fixes * πŸ’„ increase font size in JB * πŸ”€ merge changes * 🩹 small fixes * πŸ› fix jetbrains build * 🩹 more jetbrains fixes * 🎨 jetbrains improvements * πŸ› fix lancedb prob in jetbrains build * πŸ‘· update jetbrains build process * ✨ intellij problems context provider * πŸ‘· add script utils file * πŸ’š fix jetbrains build regression * 🎨 dynamic import transformers.js * 🩹 small jetbrains updates * ✨ folder context provider in jetbrains * 🎨 many more jetbrains improvements * πŸ”€ merge fixes * ⚑️ small improvements * 🎨 tell users transformers.js not supported * 🎨 trial updates * πŸ“ update jetbrains readmej * ⚑️ only use smaller context for local autocomplete models * ⚑️ improve bracket filter * ✨ global .continue/.prompts folder * πŸ’„ improved model setup process * πŸ₯… display VSCode req errs * ⚑️ improved autocomplete stopping * 🎨 count cmd+I * πŸ”₯ remove duplicate import * πŸ”₯ remove unnecessary headers * ⚑️ limit input history length * ⚑️ limit submenu items until you can use disk-based search, to avoid high memory use * πŸ”₯ remove duplicate function def * Free Trial Auth (#1367) * 🎨 pass gh auth token to free trial * πŸ’„ improved onboarding flow * 🎨 make fewer auth reqs * 🎨 refactor ordering of vscode deps * πŸ› resolve confighandler * ⚑️ autocomplete stopping improvements * πŸ”₯ remove unused auth code * ⚑️ consider previous completion in bracket matching * Build fixes: Remove duplicated code, re-add overwritten commit (#1358) * readd overwritten commit * Remove duplicated code * πŸ“ update keywords * ✨ /commit slash command * 🎨 make transformers.js esm compatible * ⚑️ fix jetbrains indexing perfomance issue * πŸ’„ autocomplete model onboarding * ✨ mistral free trial autocomplete * ✨ mistral description * 🩹 comment out fireworks.png * 🎨 trial fim model * 🎨 get gh token for autocomplete model trial * ✨ gh auth token jb * πŸ”§ update config files * 🎨 jetbrains onboarding * πŸ§‘β€πŸ’» create gui/dist if it doesn't exist * Fix sidebar indexing status timing-related bugs (#1368) * readd overwritten commit * Remove duplicated code * Fix status load in and failed state update * Late sidebar open bugs fixed * Change ideMessenger post parameters * debug pause * Don't use global, fix table creation bug * Creating new branch for config-related issues here * cleanup * 🚸 help user avoid login prompt if unwanted * 🚸 update onboarding flow * πŸ“ codestral as recommended in docs * feat: Add cloudflare as provider * πŸ“ set up codestral walkthrough * codestral api base * update codestral api link * codestral api link * bearer token * πŸ› fix config loading * cleaner error message * fix codestral templating * πŸ”₯ remove unused kotlin test * πŸ§‘β€πŸ’» ./gradlew build * codestral fim template * add additional llama.cpp params * feat:Add promptPath configuration to use promptFolder from JSON file in load.ts (#1377) Co-authored-by: catatapiafuentes * Full project file path in context (#1407) * Full workspace file path when using @Open Files * Full project file path for @Files and 'active file' context * FileTreeContextProvider uses splitPath from util instead of local copy * relative-to-root file paths for rag retrieval * ✨ jetbrains .continuerc.json support * tweak * ✨ allow non git tracked .prompts folder * ✨ currentFile in prompt files * explain the purpose of sign in * help users remove free trial models * better filtering of model artifacts * improve sign in flow --------- Co-authored-by: 5eqn <491100866@qq.com> Co-authored-by: Pixel <54180211+pixelsortr@users.noreply.github.com> Co-authored-by: Justin Milner <42585006+justinmilner1@users.noreply.github.com> Co-authored-by: Justin Milner Co-authored-by: Devin Gould Co-authored-by: Dipta Mahardhika <146386738+d-mahard@users.noreply.github.com> Co-authored-by: Ruben Kostandyan Co-authored-by: tnglemongrass <113173292+tnglemongrass@users.noreply.github.com> Co-authored-by: Lukas Baron Co-authored-by: Fernando Co-authored-by: Tijs Zwinkels Co-authored-by: DJ Johnson Co-authored-by: sam <1211977+sambarnes@users.noreply.github.com> Co-authored-by: Jose Vega Co-authored-by: Pratik Parmar Co-authored-by: Sam El-Husseini Co-authored-by: Peter Zaback Co-authored-by: James Delorey Co-authored-by: catatapiafuentes --- .vscode/launch.json | 2 +- core/autocomplete/completionProvider.ts | 2 +- core/autocomplete/lineStream.ts | 10 +- core/config/default.ts | 2 +- core/config/load.ts | 19 +- core/config/promptFile.ts | 11 +- core/config/types.ts | 2 +- .../CodeHighlightsContextProvider.ts | 13 +- .../providers/FileTreeContextProvider.ts | 10 +- .../providers/OpenFilesContextProvider.ts | 5 +- core/context/providers/index.ts | 1 + core/context/retrieval/retrieval.ts | 4 +- core/index.d.ts | 6 +- core/llm/index.ts | 15 +- core/llm/llms/LlamaCpp.ts | 3 + core/package-lock.json | 4 +- core/protocol/ide.ts | 5 +- core/util/filesystem.ts | 5 +- core/util/index.ts | 24 + core/util/messageIde.ts | 10 +- docs/static/schemas/config.json | 3 + .../ConfigJsonSchemaProviderFactory.kt | 2 +- .../continue/IdeProtocolClient.kt | 22 +- .../src/main/resources/config_schema.json | 3 + extensions/vscode/config_schema.json | 3 + extensions/vscode/continue_rc_schema.json | 481 ++++++++++++++---- extensions/vscode/scripts/prepackage.js | 22 +- .../vscode/src/extension/VsCodeMessenger.ts | 5 +- .../vscode/src/extension/vscodeExtension.ts | 27 +- extensions/vscode/src/ideProtocol.ts | 95 +++- .../vscode/src/test/test-suites/main.test.ts | 9 +- extensions/vscode/src/util/ideUtils.ts | 2 + .../vscode/src/util/traverseDirectory.ts | 6 +- gui/src/components/mainInput/resolveInput.ts | 5 +- gui/src/hooks/useChatHandler.ts | 5 +- 35 files changed, 646 insertions(+), 197 deletions(-) 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,