From 8eb45fd081a71067c94b5408dddb7bda95f0bbcd Mon Sep 17 00:00:00 2001 From: deepak-s-2000 Date: Thu, 25 Jun 2026 18:30:23 +0530 Subject: [PATCH 1/4] feat(core): implement Ultra Token Saving with on-demand chat history Ports shadow-chat's token optimization natively into the TypeScript core so all LLM providers (OpenAI, Gemini, Anthropic, Ollama, etc.) get up to 97% input token reduction without any BYOK config changes. New files: - core/util/shadowChatSessionId.ts: stable session ID via sha256(first message) - core/data/shadowChatDb.ts: SQLite storage with FTS5 for history/search/stats - core/tools/implementations/shadowChatHistory.ts: 7 internal shadow_* tools - core/llm/tokenOptimizedChat.ts: agentic loop that executes tools server-side Shadow tools available to the LLM on demand: shadow_get_chat_history, shadow_search_messages, shadow_semantic_search, shadow_get_conversation_stats, shadow_get_tool_result, shadow_search_all_sessions, shadow_semantic_search_all_sessions Modified files: - core/index.d.ts: add ultraTokenSaving to ContinueUIConfig - core/config/sharedConfig.ts: wire ultraTokenSaving through shared config - core/llm/streamChat.ts: intercept calls when ultra mode enabled; block mid-conversation mode changes with an in-chat error message - gui/src/pages/config/sections/UserSettingsSection.tsx: add Ultra Token Saving toggle in Chat settings (below Show Session Tabs) --- core/config/sharedConfig.ts | 5 + core/data/shadowChatDb.ts | 333 ++++++++++++++++++ core/index.d.ts | 1 + core/llm/streamChat.ts | 45 ++- core/llm/tokenOptimizedChat.ts | 296 ++++++++++++++++ .../implementations/shadowChatHistory.ts | 208 +++++++++++ core/util/shadowChatSessionId.ts | 12 + .../config/sections/UserSettingsSection.tsx | 8 + 8 files changed, 902 insertions(+), 6 deletions(-) create mode 100644 core/data/shadowChatDb.ts create mode 100644 core/llm/tokenOptimizedChat.ts create mode 100644 core/tools/implementations/shadowChatHistory.ts create mode 100644 core/util/shadowChatSessionId.ts diff --git a/core/config/sharedConfig.ts b/core/config/sharedConfig.ts index 87306a2aed8..437950ec1de 100644 --- a/core/config/sharedConfig.ts +++ b/core/config/sharedConfig.ts @@ -26,6 +26,7 @@ export const sharedConfigSchema = z // `ui` in `ContinueConfig` showSessionTabs: z.boolean(), + ultraTokenSaving: z.boolean(), codeBlockToolbarPosition: z.enum(["top", "bottom"]), fontSize: z.number(), codeWrap: z.boolean(), @@ -154,6 +155,10 @@ export function modifyAnyConfigWithSharedConfig< configCopy.ui.showSessionTabs = sharedConfig.showSessionTabs; } + if (sharedConfig.ultraTokenSaving !== undefined) { + configCopy.ui.ultraTokenSaving = sharedConfig.ultraTokenSaving; + } + if (sharedConfig.continueAfterToolRejection !== undefined) { configCopy.ui.continueAfterToolRejection = sharedConfig.continueAfterToolRejection; diff --git a/core/data/shadowChatDb.ts b/core/data/shadowChatDb.ts new file mode 100644 index 00000000000..464a404e5f0 --- /dev/null +++ b/core/data/shadowChatDb.ts @@ -0,0 +1,333 @@ +import fs from "fs"; +import path from "path"; +import os from "os"; + +import { open } from "sqlite"; +import sqlite3 from "sqlite3"; + +import { DatabaseConnection } from "../indexing/refreshIndex.js"; +import { ChatMessage } from "../index.js"; + +function getShadowChatDbPath(): string { + const devDataDir = path.join(os.homedir(), ".continue", "devdata"); + if (!fs.existsSync(devDataDir)) { + fs.mkdirSync(devDataDir, { recursive: true }); + } + return path.join(devDataDir, "shadow-chat.sqlite"); +} + +export class ShadowChatDb { + static db: DatabaseConnection | null = null; + + private static async createTables(db: DatabaseConnection): Promise { + await db.exec(` + CREATE TABLE IF NOT EXISTS shadow_sessions ( + session_id TEXT PRIMARY KEY, + ultra_mode_enabled INTEGER NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS shadow_messages ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT NOT NULL, + role TEXT NOT NULL, + content TEXT NOT NULL, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS shadow_turns ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT NOT NULL, + user_message TEXT NOT NULL, + response TEXT NOT NULL DEFAULT '', + actual_tokens_in INTEGER NOT NULL DEFAULT 0, + actual_tokens_out INTEGER NOT NULL DEFAULT 0, + estimated_baseline_tokens INTEGER NOT NULL DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS shadow_tool_results ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + session_id TEXT NOT NULL, + tool_name TEXT NOT NULL, + tool_call_id TEXT NOT NULL UNIQUE, + result TEXT NOT NULL, + turn_index INTEGER NOT NULL DEFAULT 0, + created_at DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE VIRTUAL TABLE IF NOT EXISTS shadow_messages_fts + USING fts5(content, content='shadow_messages', content_rowid='id'); + + CREATE TRIGGER IF NOT EXISTS shadow_messages_ai + AFTER INSERT ON shadow_messages BEGIN + INSERT INTO shadow_messages_fts(rowid, content) VALUES (new.id, new.content); + END; + `); + } + + static async get(): Promise { + const dbPath = getShadowChatDbPath(); + if (ShadowChatDb.db && fs.existsSync(dbPath)) { + return ShadowChatDb.db; + } + ShadowChatDb.db = await open({ filename: dbPath, driver: sqlite3.Database }); + await ShadowChatDb.db.exec("PRAGMA busy_timeout = 3000;"); + await ShadowChatDb.createTables(ShadowChatDb.db); + return ShadowChatDb.db; + } + + static async createSession( + sessionId: string, + ultraModeEnabled: boolean, + ): Promise { + const db = await ShadowChatDb.get(); + await db?.run( + "INSERT OR IGNORE INTO shadow_sessions (session_id, ultra_mode_enabled) VALUES (?, ?)", + [sessionId, ultraModeEnabled ? 1 : 0], + ); + } + + static async getSession( + sessionId: string, + ): Promise<{ ultraModeEnabled: boolean } | undefined> { + const db = await ShadowChatDb.get(); + const row = await db?.get( + "SELECT ultra_mode_enabled FROM shadow_sessions WHERE session_id = ?", + [sessionId], + ); + if (!row) return undefined; + return { ultraModeEnabled: row.ultra_mode_enabled === 1 }; + } + + static async saveMessages( + sessionId: string, + messages: ChatMessage[], + ): Promise { + const db = await ShadowChatDb.get(); + if (!db) return; + + const existing = await db.get( + "SELECT COUNT(*) as cnt FROM shadow_messages WHERE session_id = ?", + [sessionId], + ); + const toInsert = messages.slice(existing?.cnt ?? 0); + if (toInsert.length === 0) return; + + // Build toolCallId → toolName map from all assistant messages (for tool result tracking) + const toolCallNames = new Map(); + for (const msg of messages) { + if (msg.role === "assistant" && msg.toolCalls) { + for (const tc of msg.toolCalls) { + if (tc.id && tc.function?.name) { + toolCallNames.set(tc.id, tc.function.name); + } + } + } + } + + // Current turn count for age tracking on tool results + const turnRow = await db.get( + "SELECT COUNT(*) as cnt FROM shadow_turns WHERE session_id = ?", + [sessionId], + ); + const turnIndex: number = turnRow?.cnt ?? 0; + + for (const msg of toInsert) { + const content = + typeof msg.content === "string" + ? msg.content + : JSON.stringify(msg.content); + + await db.run( + "INSERT INTO shadow_messages (session_id, role, content) VALUES (?, ?, ?)", + [sessionId, msg.role, content], + ); + + // Cache external MCP tool results for get_tool_result lookups + if (msg.role === "tool" && msg.toolCallId) { + const toolName = toolCallNames.get(msg.toolCallId) ?? "unknown"; + await db.run( + `INSERT OR IGNORE INTO shadow_tool_results + (session_id, tool_name, tool_call_id, result, turn_index) + VALUES (?, ?, ?, ?, ?)`, + [sessionId, toolName, msg.toolCallId, content, turnIndex], + ); + } + } + } + + static async getHistory( + sessionId: string, + limit: number, + ): Promise { + const db = await ShadowChatDb.get(); + const rows = await db?.all( + "SELECT role, content FROM shadow_messages WHERE session_id = ? ORDER BY id DESC LIMIT ?", + [sessionId, limit], + ); + if (!rows) return []; + return rows + .reverse() + .map((r: any) => ({ role: r.role, content: r.content }) as ChatMessage); + } + + static async searchMessages( + sessionId: string, + query: string, + limit: number, + ): Promise { + const db = await ShadowChatDb.get(); + const rows = await db?.all( + `SELECT role, content FROM shadow_messages + WHERE session_id = ? AND content LIKE ? + ORDER BY id DESC LIMIT ?`, + [sessionId, `%${query}%`, limit], + ); + if (!rows) return []; + return rows.map( + (r: any) => ({ role: r.role, content: r.content }) as ChatMessage, + ); + } + + // FTS5 ranked full-text search within a session (BM25 scoring) + static async semanticSearch( + sessionId: string, + query: string, + limit: number, + ): Promise { + const db = await ShadowChatDb.get(); + const rows = await db?.all( + `SELECT m.role, m.content + FROM shadow_messages_fts fts + JOIN shadow_messages m ON m.id = fts.rowid + WHERE fts.content MATCH ? AND m.session_id = ? + ORDER BY rank + LIMIT ?`, + [query, sessionId, limit], + ); + if (!rows) return []; + return rows.map( + (r: any) => ({ role: r.role, content: r.content }) as ChatMessage, + ); + } + + // Keyword search across all sessions + static async searchAllSessions( + query: string, + limit: number, + ): Promise> { + const db = await ShadowChatDb.get(); + const rows = await db?.all( + `SELECT session_id, role, content FROM shadow_messages + WHERE content LIKE ? + ORDER BY id DESC LIMIT ?`, + [`%${query}%`, limit], + ); + if (!rows) return []; + return rows.map((r: any) => ({ + role: r.role, + content: r.content, + sessionId: r.session_id, + })); + } + + // FTS5 ranked full-text search across all sessions + static async semanticSearchAllSessions( + query: string, + limit: number, + ): Promise> { + const db = await ShadowChatDb.get(); + const rows = await db?.all( + `SELECT m.session_id, m.role, m.content + FROM shadow_messages_fts fts + JOIN shadow_messages m ON m.id = fts.rowid + WHERE fts.content MATCH ? + ORDER BY rank + LIMIT ?`, + [query, limit], + ); + if (!rows) return []; + return rows.map((r: any) => ({ + role: r.role, + content: r.content, + sessionId: r.session_id, + })); + } + + static async getConversationStats(sessionId: string): Promise<{ + messageCount: number; + turnCount: number; + totalTokensSaved: number; + totalActualTokensIn: number; + totalEstimatedBaselineTokens: number; + }> { + const db = await ShadowChatDb.get(); + const msgRow = await db?.get( + "SELECT COUNT(*) as cnt FROM shadow_messages WHERE session_id = ?", + [sessionId], + ); + const turnRow = await db?.get( + `SELECT + COUNT(*) as turn_count, + COALESCE(SUM(estimated_baseline_tokens - actual_tokens_in), 0) as tokens_saved, + COALESCE(SUM(actual_tokens_in), 0) as tokens_in, + COALESCE(SUM(estimated_baseline_tokens), 0) as baseline + FROM shadow_turns WHERE session_id = ?`, + [sessionId], + ); + return { + messageCount: msgRow?.cnt ?? 0, + turnCount: turnRow?.turn_count ?? 0, + totalTokensSaved: turnRow?.tokens_saved ?? 0, + totalActualTokensIn: turnRow?.tokens_in ?? 0, + totalEstimatedBaselineTokens: turnRow?.baseline ?? 0, + }; + } + + static async getToolResult( + sessionId: string, + toolName: string, + maxAgeTurns: number, + ): Promise { + const db = await ShadowChatDb.get(); + const turnRow = await db?.get( + "SELECT COUNT(*) as cnt FROM shadow_turns WHERE session_id = ?", + [sessionId], + ); + const currentTurnIndex: number = turnRow?.cnt ?? 0; + const minTurnIndex = Math.max(0, currentTurnIndex - maxAgeTurns); + + const row = await db?.get( + `SELECT result FROM shadow_tool_results + WHERE session_id = ? AND tool_name = ? AND turn_index >= ? + ORDER BY id DESC LIMIT 1`, + [sessionId, toolName, minTurnIndex], + ); + return row?.result; + } + + static async saveTurn( + sessionId: string, + userMessage: string, + response: string, + actualTokensIn: number, + actualTokensOut: number, + estimatedBaselineTokens: number, + ): Promise { + const db = await ShadowChatDb.get(); + await db?.run( + `INSERT INTO shadow_turns + (session_id, user_message, response, actual_tokens_in, actual_tokens_out, estimated_baseline_tokens) + VALUES (?, ?, ?, ?, ?, ?)`, + [ + sessionId, + userMessage, + response, + actualTokensIn, + actualTokensOut, + estimatedBaselineTokens, + ], + ); + } +} diff --git a/core/index.d.ts b/core/index.d.ts index bec3e0e0ff8..d7c61f74826 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -1453,6 +1453,7 @@ export interface ContinueUIConfig { codeWrap?: boolean; showSessionTabs?: boolean; continueAfterToolRejection?: boolean; + ultraTokenSaving?: boolean; } export interface ContextMenuConfig { diff --git a/core/llm/streamChat.ts b/core/llm/streamChat.ts index bd4aebb22e9..93584998c44 100644 --- a/core/llm/streamChat.ts +++ b/core/llm/streamChat.ts @@ -1,8 +1,11 @@ import { fetchwithRequestOptions } from "@continuedev/fetch"; import { ChatMessage, IDE, PromptLog } from ".."; import { ConfigHandler } from "../config/ConfigHandler"; +import { ShadowChatDb } from "../data/shadowChatDb"; import { FromCoreProtocol, ToCoreProtocol } from "../protocol"; import { IMessenger, Message } from "../protocol/messenger"; +import { deriveSessionId } from "../util/shadowChatSessionId"; +import { tokenOptimizedStreamChat } from "./tokenOptimizedChat"; import { TTS } from "../util/tts"; @@ -112,12 +115,42 @@ export async function* llmStreamChat( return next.value; } else { - const gen = model.streamChat( - messages, - abortController.signal, - completionOptions, - messageOptions, - ); + const ultraModeEnabled = config.ui?.ultraTokenSaving ?? false; + const historyLimit = 20; + const sessionId = deriveSessionId(messages); + + // Guard against toggling Ultra Token Saving mid-conversation + if (messages.length > 1) { + const storedSession = await ShadowChatDb.getSession(sessionId); + if (storedSession && storedSession.ultraModeEnabled !== ultraModeEnabled) { + const direction = ultraModeEnabled ? "enabled" : "disabled"; + yield { + role: "assistant", + content: `⚠️ Ultra Token Saving has been ${direction}. Please start a new chat to continue.`, + }; + return errorPromptLog; + } + } else { + // First message of a new conversation — record the current mode + await ShadowChatDb.createSession(sessionId, ultraModeEnabled); + } + + const gen = ultraModeEnabled + ? tokenOptimizedStreamChat( + model, + messages, + abortController.signal, + completionOptions, + sessionId, + historyLimit, + ) + : model.streamChat( + messages, + abortController.signal, + completionOptions, + messageOptions, + ); + let next = await gen.next(); while (!next.done) { if (abortController.signal.aborted) { diff --git a/core/llm/tokenOptimizedChat.ts b/core/llm/tokenOptimizedChat.ts new file mode 100644 index 00000000000..6c2fc763807 --- /dev/null +++ b/core/llm/tokenOptimizedChat.ts @@ -0,0 +1,296 @@ +import { + AssistantChatMessage, + ChatMessage, + ILLM, + LLMFullCompletionOptions, + PromptLog, + ToolCallDelta, +} from "../index.js"; +import { ShadowChatDb } from "../data/shadowChatDb.js"; +import { + createShadowHistoryToolDefinitions, + SHADOW_TOOL_NAMES, +} from "../tools/implementations/shadowChatHistory.js"; + +interface CompletedToolCall { + id: string; + name: string; + args: string; +} + +function extractCompletedToolCalls(chunks: ChatMessage[]): CompletedToolCall[] { + const callsById = new Map(); + const callOrder: string[] = []; + let currentId = ""; + + for (const chunk of chunks) { + if (chunk.role !== "assistant" || !chunk.toolCalls?.length) continue; + for (const delta of chunk.toolCalls as ToolCallDelta[]) { + if (delta.id) { + currentId = delta.id; + if (!callsById.has(currentId)) { + callsById.set(currentId, { id: currentId, name: "", args: "" }); + callOrder.push(currentId); + } + } + if (!currentId) continue; + const call = callsById.get(currentId); + if (!call) continue; + if (delta.function?.name) call.name += delta.function.name; + if (delta.function?.arguments) call.args += delta.function.arguments; + } + } + + return callOrder + .map((id) => callsById.get(id)!) + .filter((c) => c && c.name); +} + +function extractUsageFromChunks( + chunks: ChatMessage[], +): { promptTokens: number; completionTokens: number } { + for (let i = chunks.length - 1; i >= 0; i--) { + const chunk = chunks[i]; + if (chunk.role === "assistant" && chunk.usage) { + return { + promptTokens: chunk.usage.promptTokens, + completionTokens: chunk.usage.completionTokens, + }; + } + } + return { promptTokens: 0, completionTokens: 0 }; +} + +function buildTextContent(chunks: ChatMessage[]): string { + return chunks + .filter((c) => c.role === "assistant") + .map((c) => (typeof c.content === "string" ? c.content : "")) + .join(""); +} + +async function executeShadowTool( + call: CompletedToolCall, + sessionId: string, + historyLimit: number, +): Promise { + try { + const args = JSON.parse(call.args || "{}"); + + if (call.name === "shadow_get_chat_history") { + const limit: number = + typeof args.limit === "number" ? args.limit : historyLimit; + const history = await ShadowChatDb.getHistory(sessionId, limit); + return JSON.stringify(history); + } + + if (call.name === "shadow_search_messages") { + const query: string = + typeof args.query === "string" ? args.query : ""; + const limit: number = + typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.searchMessages(sessionId, query, limit); + return JSON.stringify(results); + } + + if (call.name === "shadow_semantic_search") { + const query: string = + typeof args.query === "string" ? args.query : ""; + const limit: number = + typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.semanticSearch(sessionId, query, limit); + return JSON.stringify(results); + } + + if (call.name === "shadow_get_conversation_stats") { + const stats = await ShadowChatDb.getConversationStats(sessionId); + const savingsPct = + stats.totalEstimatedBaselineTokens > 0 + ? Math.round( + (stats.totalTokensSaved / stats.totalEstimatedBaselineTokens) * + 100, + ) + : 0; + return JSON.stringify({ ...stats, savingsPercent: savingsPct }); + } + + if (call.name === "shadow_get_tool_result") { + const toolName: string = + typeof args.tool_name === "string" ? args.tool_name : ""; + const maxAgeTurns: number = + typeof args.max_age_turns === "number" ? args.max_age_turns : 5; + const result = await ShadowChatDb.getToolResult( + sessionId, + toolName, + maxAgeTurns, + ); + if (result === undefined) { + return JSON.stringify({ + found: false, + message: `No cached result found for tool '${toolName}' within the last ${maxAgeTurns} turns.`, + }); + } + return JSON.stringify({ found: true, result }); + } + + if (call.name === "shadow_search_all_sessions") { + const query: string = + typeof args.query === "string" ? args.query : ""; + const limit: number = + typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.searchAllSessions(query, limit); + return JSON.stringify(results); + } + + if (call.name === "shadow_semantic_search_all_sessions") { + const query: string = + typeof args.query === "string" ? args.query : ""; + const limit: number = + typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.semanticSearchAllSessions(query, limit); + return JSON.stringify(results); + } + + return JSON.stringify({ error: `Unknown shadow tool: ${call.name}` }); + } catch (e) { + return JSON.stringify({ error: String(e) }); + } +} + +export async function* tokenOptimizedStreamChat( + model: ILLM, + messages: ChatMessage[], + signal: AbortSignal, + options: LLMFullCompletionOptions, + sessionId: string, + historyLimit: number, +): AsyncGenerator { + // Estimate baseline: what would have been sent without optimization + const allText = messages + .map((m) => + typeof m.content === "string" ? m.content : JSON.stringify(m.content), + ) + .join(" "); + const estimatedBaselineTokens = Math.ceil(allText.length / 4); + + // Save the full incoming messages to DB (includes tool results from prior turns) + await ShadowChatDb.saveMessages(sessionId, messages); + + // Extract the current user message and optional system message + const systemMsg = messages.find((m) => m.role === "system"); + const currentUserMsg = [...messages].reverse().find((m) => m.role === "user"); + + if (!currentUserMsg) { + // Fallback: send messages as-is if no user message found + yield* model.streamChat(messages, signal, options, { precompiled: true }); + return { modelTitle: "", modelProvider: "", prompt: "", completion: "" }; + } + + const userMessageText = + typeof currentUserMsg.content === "string" + ? currentUserMsg.content + : JSON.stringify(currentUserMsg.content); + + // Shadow tools let the LLM pull history on demand instead of receiving it all upfront + const shadowTools = createShadowHistoryToolDefinitions(); + const augmentedOptions: LLMFullCompletionOptions = { + ...options, + tools: [...shadowTools, ...(options.tools ?? [])], + }; + + let loopMessages: ChatMessage[] = [ + ...(systemMsg ? [systemMsg] : []), + currentUserMsg, + ]; + + let totalActualTokensIn = 0; + let totalActualTokensOut = 0; + let finalPromptLog: PromptLog = { + modelTitle: model.title ?? model.model, + modelProvider: (model as any).providerName ?? "unknown", + prompt: userMessageText, + completion: "", + }; + + // Internal agentic loop: execute shadow_* tools server-side, pass external tools to client + while (true) { + const chunks: ChatMessage[] = []; + const gen = model.streamChat(loopMessages, signal, augmentedOptions, { + precompiled: true, + }); + + let next = await gen.next(); + while (!next.done) { + chunks.push(next.value); + next = await gen.next(); + } + if (next.value && typeof next.value === "object" && "prompt" in next.value) { + finalPromptLog = next.value as PromptLog; + } + + const { promptTokens, completionTokens } = extractUsageFromChunks(chunks); + totalActualTokensIn += promptTokens; + totalActualTokensOut += completionTokens; + + const toolCalls = extractCompletedToolCalls(chunks); + + if (toolCalls.length === 0) { + // Pure text response — stream all chunks to the caller + for (const chunk of chunks) { + yield chunk; + } + finalPromptLog = { + ...finalPromptLog, + completion: buildTextContent(chunks), + }; + break; + } + + const shadowCalls = toolCalls.filter((tc) => SHADOW_TOOL_NAMES.has(tc.name)); + const externalCalls = toolCalls.filter( + (tc) => !SHADOW_TOOL_NAMES.has(tc.name), + ); + + if (externalCalls.length > 0) { + // External/MCP tool calls — pass all chunks through to the client unchanged + for (const chunk of chunks) { + yield chunk; + } + break; + } + + // All tool calls are shadow tools — execute server-side and loop + const assistantToolCallMsg: AssistantChatMessage = { + role: "assistant", + content: "", + toolCalls: shadowCalls.map((tc) => ({ + id: tc.id, + type: "function" as const, + function: { name: tc.name, arguments: tc.args }, + })), + }; + loopMessages = [...loopMessages, assistantToolCallMsg]; + + for (const call of shadowCalls) { + const result = await executeShadowTool(call, sessionId, historyLimit); + const toolResultMsg: ChatMessage = { + role: "tool", + content: result, + toolCallId: call.id, + }; + loopMessages = [...loopMessages, toolResultMsg]; + } + // Loop: the LLM will now see the tool results and produce its final answer + } + + // Log token savings for this turn + await ShadowChatDb.saveTurn( + sessionId, + userMessageText, + finalPromptLog.completion, + totalActualTokensIn, + totalActualTokensOut, + estimatedBaselineTokens, + ); + + return finalPromptLog; +} diff --git a/core/tools/implementations/shadowChatHistory.ts b/core/tools/implementations/shadowChatHistory.ts new file mode 100644 index 00000000000..9bd929df585 --- /dev/null +++ b/core/tools/implementations/shadowChatHistory.ts @@ -0,0 +1,208 @@ +import { Tool } from "../../index.js"; + +export const SHADOW_TOOL_NAMES = new Set([ + "shadow_get_chat_history", + "shadow_search_messages", + "shadow_semantic_search", + "shadow_get_conversation_stats", + "shadow_get_tool_result", + "shadow_search_all_sessions", + "shadow_semantic_search_all_sessions", +]); + +export function createShadowHistoryToolDefinitions(): Tool[] { + return [ + { + type: "function", + function: { + name: "shadow_get_chat_history", + description: + "Retrieve the most recent messages from this conversation. Use this when the user refers to something said earlier, asks follow-up questions, or you need context from previous turns.", + parameters: { + type: "object", + properties: { + limit: { + type: "number", + description: + "Maximum number of recent messages to retrieve (default: 20)", + }, + }, + required: [], + }, + }, + displayTitle: "Get Chat History", + wouldLikeTo: "retrieve chat history", + isCurrently: "retrieving chat history", + hasAlready: "retrieved chat history", + readonly: true, + group: "shadow", + }, + { + type: "function", + function: { + name: "shadow_search_messages", + description: + "Search this conversation for messages containing specific keywords or phrases. Use when looking for a particular topic, code snippet, or piece of information mentioned earlier.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "The keyword or phrase to search for", + }, + limit: { + type: "number", + description: + "Maximum number of matching messages to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + displayTitle: "Search Messages", + wouldLikeTo: "search chat history", + isCurrently: "searching chat history", + hasAlready: "searched chat history", + readonly: true, + group: "shadow", + }, + { + type: "function", + function: { + name: "shadow_semantic_search", + description: + "Full-text ranked search of this conversation using BM25 scoring. Finds messages by meaning and relevance, not just exact keyword matches. Prefer this over shadow_search_messages when looking for conceptually related content.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: + "The search query — describe what you are looking for", + }, + limit: { + type: "number", + description: + "Maximum number of results to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + displayTitle: "Semantic Search", + wouldLikeTo: "semantically search chat history", + isCurrently: "searching chat history", + hasAlready: "searched chat history", + readonly: true, + group: "shadow", + }, + { + type: "function", + function: { + name: "shadow_get_conversation_stats", + description: + "Get statistics about this conversation: total messages, number of turns, and how many input tokens have been saved so far by Ultra Token Saving mode.", + parameters: { + type: "object", + properties: {}, + required: [], + }, + }, + displayTitle: "Get Conversation Stats", + wouldLikeTo: "get conversation statistics", + isCurrently: "retrieving conversation statistics", + hasAlready: "retrieved conversation statistics", + readonly: true, + group: "shadow", + }, + { + type: "function", + function: { + name: "shadow_get_tool_result", + description: + "Retrieve a cached result from a previously executed tool call in this conversation. Use this to avoid re-running expensive tool calls (file reads, searches, API calls) when their result is still valid.", + parameters: { + type: "object", + properties: { + tool_name: { + type: "string", + description: + "Name of the tool whose result you want to retrieve (e.g. 'read_file', 'run_terminal_command')", + }, + max_age_turns: { + type: "number", + description: + "How many conversation turns back to look (default: 5)", + }, + }, + required: ["tool_name"], + }, + }, + displayTitle: "Get Tool Result", + wouldLikeTo: "retrieve a cached tool result", + isCurrently: "retrieving cached tool result", + hasAlready: "retrieved cached tool result", + readonly: true, + group: "shadow", + }, + { + type: "function", + function: { + name: "shadow_search_all_sessions", + description: + "Search across all past conversations (not just this one) for messages matching a keyword or phrase. Use when the user refers to something discussed in a previous chat session.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "The keyword or phrase to search for", + }, + limit: { + type: "number", + description: + "Maximum number of results to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + displayTitle: "Search All Sessions", + wouldLikeTo: "search all past chat sessions", + isCurrently: "searching all past sessions", + hasAlready: "searched all past sessions", + readonly: true, + group: "shadow", + }, + { + type: "function", + function: { + name: "shadow_semantic_search_all_sessions", + description: + "Full-text ranked search across all past conversations using BM25 scoring. Use when the user refers to something discussed in a previous session and a keyword search may not be precise enough.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: + "The search query — describe what you are looking for", + }, + limit: { + type: "number", + description: + "Maximum number of results to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + displayTitle: "Semantic Search All Sessions", + wouldLikeTo: "semantically search all past sessions", + isCurrently: "searching all past sessions", + hasAlready: "searched all past sessions", + readonly: true, + group: "shadow", + }, + ]; +} diff --git a/core/util/shadowChatSessionId.ts b/core/util/shadowChatSessionId.ts new file mode 100644 index 00000000000..19a0b7c3b34 --- /dev/null +++ b/core/util/shadowChatSessionId.ts @@ -0,0 +1,12 @@ +import crypto from "crypto"; + +import { ChatMessage } from "../index.js"; + +export function deriveSessionId(messages: ChatMessage[]): string { + const first = messages[0]; + const content = + typeof first?.content === "string" + ? first.content + : JSON.stringify(first?.content ?? ""); + return crypto.createHash("sha256").update(content).digest("hex").slice(0, 32); +} diff --git a/gui/src/pages/config/sections/UserSettingsSection.tsx b/gui/src/pages/config/sections/UserSettingsSection.tsx index 0b5549f5a08..ae39015377f 100644 --- a/gui/src/pages/config/sections/UserSettingsSection.tsx +++ b/gui/src/pages/config/sections/UserSettingsSection.tsx @@ -48,6 +48,7 @@ export function UserSettingsSection() { // TODO defaults are in multiple places, should be consolidated and probably not explicit here const showSessionTabs = config.ui?.showSessionTabs ?? false; + const ultraTokenSaving = config.ui?.ultraTokenSaving ?? false; const continueAfterToolRejection = config.ui?.continueAfterToolRejection ?? false; const codeWrap = config.ui?.codeWrap ?? false; @@ -102,6 +103,13 @@ export function UserSettingsSection() { value={showSessionTabs} onChange={(value) => handleUpdate({ showSessionTabs: value })} /> + handleUpdate({ ultraTokenSaving: value })} + /> Date: Sat, 15 Aug 2026 12:49:23 +0530 Subject: [PATCH 2/4] fix(core): correct shadow chat session scoping, promote history tools to built-ins Ultra Token Saving's session ID was hashed from messages[0], which is the system prompt (constructMessages.ts always unshifts one) rather than the first user message. Since the system prompt is near-identical across unrelated conversations, this collided every chat sharing a mode into one session, leaking chat history between them, and meant the mid-conversation toggle guard never actually recorded a session to compare against. Session ID now prefers the GUI's real per-conversation UUID (threaded through llm/streamChat and tools/call), falling back to hashing the first user message only when a client doesn't supply one. The 7 shadow_* tools are now registered as real built-in tools (core/tools/builtIn.ts, definitions/shadowChatHistory.ts, callTool.ts) in the BUILT_IN_GROUP_NAME group, so they're visible in Settings > Tools and usable by normal (non-ultra) chat, not just force-injected in ultra mode. Ultra mode still executes them inside its own internal loop rather than round-tripping through the client -- that's what keeps "only the current message is sent" true -- but now yields the resolved call/result as chunks so the UI renders the same tool-call card instead of silently swallowing them, and streamUpdate() marks the call done+output immediately instead of leaving it stuck on "generating" (nothing else would ever resolve it). Also: - Persist tool call input/output immediately at execution time for every built-in and MCP tool (not just shadow ones), via a hook in callTool() plus one in the ultra-mode internal loop. Replaces the old capture in saveMessages(), which only saw client-echoed results a turn late and never had the arguments. - Fix shadow_semantic_search throwing on any query containing punctuation (FTS5 MATCH parses it as query syntax) by quoting queries as a literal phrase. - Show tool arguments in the UI for tools without a bespoke display (previously only output was ever visible). - core/sqlite3 and core/sharp bumped to versions with prebuilt Windows ARM64 binaries (dev-environment fix, unrelated to the above). --- core/core.ts | 7 +- core/data/shadowChatDb.ts | 102 +- core/index.d.ts | 1 + core/llm/streamChat.ts | 13 +- core/llm/tokenOptimizedChat.ts | 110 +- core/package-lock.json | 1745 ++++++++--------- core/package.json | 6 +- core/protocol/core.ts | 3 +- core/tools/builtIn.ts | 19 + core/tools/callTool.ts | 46 + core/tools/definitions/index.ts | 9 + core/tools/definitions/shadowChatHistory.ts | 219 +++ .../implementations/shadowChatHistory.ts | 314 ++- core/tools/index.ts | 7 + core/util/shadowChatSessionId.ts | 11 +- extensions/vscode/package-lock.json | 682 +------ extensions/vscode/package.json | 4 +- gui/package-lock.json | 2 +- .../FunctionSpecificToolCallDiv.tsx | 10 + gui/src/redux/slices/sessionSlice.ts | 21 + gui/src/redux/thunks/callToolById.ts | 1 + gui/src/redux/thunks/streamNormalInput.ts | 1 + 22 files changed, 1514 insertions(+), 1819 deletions(-) create mode 100644 core/tools/definitions/shadowChatHistory.ts diff --git a/core/core.ts b/core/core.ts index daba8ffaac5..f8580cd1298 100644 --- a/core/core.ts +++ b/core/core.ts @@ -1044,8 +1044,8 @@ export class Core { return { url: "" }; }); - on("tools/call", async ({ data: { toolCall } }) => - this.handleToolCall(toolCall), + on("tools/call", async ({ data: { toolCall, sessionId } }) => + this.handleToolCall(toolCall, sessionId), ); on( @@ -1147,7 +1147,7 @@ export class Core { }); } - private async handleToolCall(toolCall: ToolCall) { + private async handleToolCall(toolCall: ToolCall, sessionId?: string) { const { config } = await this.configHandler.loadConfig(); if (!config) { throw new Error("Config not loaded"); @@ -1183,6 +1183,7 @@ export class Core { toolCallId: toolCall.id, onPartialOutput, codeBaseIndexer: this.codeBaseIndexer, + sessionId, }); return result; diff --git a/core/data/shadowChatDb.ts b/core/data/shadowChatDb.ts index 464a404e5f0..9219ae4bc63 100644 --- a/core/data/shadowChatDb.ts +++ b/core/data/shadowChatDb.ts @@ -16,6 +16,14 @@ function getShadowChatDbPath(): string { return path.join(devDataDir, "shadow-chat.sqlite"); } +// FTS5's MATCH syntax treats punctuation (?, ", *, (, ), :, -, ...) as query +// operators, so a natural-language query like "did we talk about coding?" +// is a syntax error, not just a search with no results. Quoting it as a +// literal phrase sidesteps FTS5 query syntax entirely. +function toFtsQuery(query: string): string { + return `"${query.replace(/"/g, '""')}"`; +} + export class ShadowChatDb { static db: DatabaseConnection | null = null; @@ -51,6 +59,7 @@ export class ShadowChatDb { session_id TEXT NOT NULL, tool_name TEXT NOT NULL, tool_call_id TEXT NOT NULL UNIQUE, + input_args TEXT NOT NULL DEFAULT '', result TEXT NOT NULL, turn_index INTEGER NOT NULL DEFAULT 0, created_at DATETIME DEFAULT CURRENT_TIMESTAMP @@ -64,6 +73,14 @@ export class ShadowChatDb { INSERT INTO shadow_messages_fts(rowid, content) VALUES (new.id, new.content); END; `); + + // Migration for DBs created before input_args existed + const columns = await db.all("PRAGMA table_info(shadow_tool_results)"); + if (!columns.some((c: any) => c.name === "input_args")) { + await db.exec( + "ALTER TABLE shadow_tool_results ADD COLUMN input_args TEXT NOT NULL DEFAULT ''", + ); + } } static async get(): Promise { @@ -71,7 +88,10 @@ export class ShadowChatDb { if (ShadowChatDb.db && fs.existsSync(dbPath)) { return ShadowChatDb.db; } - ShadowChatDb.db = await open({ filename: dbPath, driver: sqlite3.Database }); + ShadowChatDb.db = await open({ + filename: dbPath, + driver: sqlite3.Database, + }); await ShadowChatDb.db.exec("PRAGMA busy_timeout = 3000;"); await ShadowChatDb.createTables(ShadowChatDb.db); return ShadowChatDb.db; @@ -114,25 +134,6 @@ export class ShadowChatDb { const toInsert = messages.slice(existing?.cnt ?? 0); if (toInsert.length === 0) return; - // Build toolCallId → toolName map from all assistant messages (for tool result tracking) - const toolCallNames = new Map(); - for (const msg of messages) { - if (msg.role === "assistant" && msg.toolCalls) { - for (const tc of msg.toolCalls) { - if (tc.id && tc.function?.name) { - toolCallNames.set(tc.id, tc.function.name); - } - } - } - } - - // Current turn count for age tracking on tool results - const turnRow = await db.get( - "SELECT COUNT(*) as cnt FROM shadow_turns WHERE session_id = ?", - [sessionId], - ); - const turnIndex: number = turnRow?.cnt ?? 0; - for (const msg of toInsert) { const content = typeof msg.content === "string" @@ -143,20 +144,42 @@ export class ShadowChatDb { "INSERT INTO shadow_messages (session_id, role, content) VALUES (?, ?, ?)", [sessionId, msg.role, content], ); - - // Cache external MCP tool results for get_tool_result lookups - if (msg.role === "tool" && msg.toolCallId) { - const toolName = toolCallNames.get(msg.toolCallId) ?? "unknown"; - await db.run( - `INSERT OR IGNORE INTO shadow_tool_results - (session_id, tool_name, tool_call_id, result, turn_index) - VALUES (?, ?, ?, ?, ?)`, - [sessionId, toolName, msg.toolCallId, content, turnIndex], - ); - } } } + // Current turn count for a session — used both for shadow_turns/tool-result + // age tracking and as the turn index stamped on newly-saved tool calls. + static async getCurrentTurnIndex(sessionId: string): Promise { + const db = await ShadowChatDb.get(); + const turnRow = await db?.get( + "SELECT COUNT(*) as cnt FROM shadow_turns WHERE session_id = ?", + [sessionId], + ); + return turnRow?.cnt ?? 0; + } + + // Records a tool call's input and output at the moment it executes, so + // shadow_get_tool_result can serve it immediately (not one turn late). + static async saveToolCall( + sessionId: string, + toolName: string, + toolCallId: string, + inputArgs: string, + result: string, + turnIndex: number, + ): Promise { + const db = await ShadowChatDb.get(); + await db?.run( + `INSERT INTO shadow_tool_results + (session_id, tool_name, tool_call_id, input_args, result, turn_index) + VALUES (?, ?, ?, ?, ?, ?) + ON CONFLICT(tool_call_id) DO UPDATE SET + input_args = excluded.input_args, + result = excluded.result`, + [sessionId, toolName, toolCallId, inputArgs, result, turnIndex], + ); + } + static async getHistory( sessionId: string, limit: number, @@ -204,7 +227,7 @@ export class ShadowChatDb { WHERE fts.content MATCH ? AND m.session_id = ? ORDER BY rank LIMIT ?`, - [query, sessionId, limit], + [toFtsQuery(query), sessionId, limit], ); if (!rows) return []; return rows.map( @@ -245,7 +268,7 @@ export class ShadowChatDb { WHERE fts.content MATCH ? ORDER BY rank LIMIT ?`, - [query, limit], + [toFtsQuery(query), limit], ); if (!rows) return []; return rows.map((r: any) => ({ @@ -289,22 +312,19 @@ export class ShadowChatDb { sessionId: string, toolName: string, maxAgeTurns: number, - ): Promise { + ): Promise<{ inputArgs: string; result: string } | undefined> { const db = await ShadowChatDb.get(); - const turnRow = await db?.get( - "SELECT COUNT(*) as cnt FROM shadow_turns WHERE session_id = ?", - [sessionId], - ); - const currentTurnIndex: number = turnRow?.cnt ?? 0; + const currentTurnIndex = await ShadowChatDb.getCurrentTurnIndex(sessionId); const minTurnIndex = Math.max(0, currentTurnIndex - maxAgeTurns); const row = await db?.get( - `SELECT result FROM shadow_tool_results + `SELECT input_args, result FROM shadow_tool_results WHERE session_id = ? AND tool_name = ? AND turn_index >= ? ORDER BY id DESC LIMIT 1`, [sessionId, toolName, minTurnIndex], ); - return row?.result; + if (!row) return undefined; + return { inputArgs: row.input_args, result: row.result }; } static async saveTurn( diff --git a/core/index.d.ts b/core/index.d.ts index d7c61f74826..3d82683b828 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -1120,6 +1120,7 @@ export interface ToolExtras { }) => void; config: ContinueConfig; codeBaseIndexer?: CodebaseIndexer; + sessionId?: string; } export interface McpToolMeta { diff --git a/core/llm/streamChat.ts b/core/llm/streamChat.ts index 93584998c44..96b0cb690ac 100644 --- a/core/llm/streamChat.ts +++ b/core/llm/streamChat.ts @@ -31,6 +31,7 @@ export async function* llmStreamChat( completionOptions, messages, messageOptions, + sessionId: clientSessionId, } = msg.data; const model = config.selectedModelByRole.chat; @@ -117,12 +118,14 @@ export async function* llmStreamChat( } else { const ultraModeEnabled = config.ui?.ultraTokenSaving ?? false; const historyLimit = 20; - const sessionId = deriveSessionId(messages); + // Prefer the GUI's real per-conversation ID; fall back to a + // content-derived one for callers that don't supply it yet. + const sessionId = clientSessionId ?? deriveSessionId(messages); // Guard against toggling Ultra Token Saving mid-conversation - if (messages.length > 1) { - const storedSession = await ShadowChatDb.getSession(sessionId); - if (storedSession && storedSession.ultraModeEnabled !== ultraModeEnabled) { + const storedSession = await ShadowChatDb.getSession(sessionId); + if (storedSession) { + if (storedSession.ultraModeEnabled !== ultraModeEnabled) { const direction = ultraModeEnabled ? "enabled" : "disabled"; yield { role: "assistant", @@ -131,7 +134,7 @@ export async function* llmStreamChat( return errorPromptLog; } } else { - // First message of a new conversation — record the current mode + // First turn seen for this session — record the current mode await ShadowChatDb.createSession(sessionId, ultraModeEnabled); } diff --git a/core/llm/tokenOptimizedChat.ts b/core/llm/tokenOptimizedChat.ts index 6c2fc763807..0a17f51608b 100644 --- a/core/llm/tokenOptimizedChat.ts +++ b/core/llm/tokenOptimizedChat.ts @@ -7,10 +7,8 @@ import { ToolCallDelta, } from "../index.js"; import { ShadowChatDb } from "../data/shadowChatDb.js"; -import { - createShadowHistoryToolDefinitions, - SHADOW_TOOL_NAMES, -} from "../tools/implementations/shadowChatHistory.js"; +import { SHADOW_TOOL_NAMES } from "../tools/builtIn.js"; +import { shadowChatHistoryTools } from "../tools/definitions/shadowChatHistory.js"; interface CompletedToolCall { id: string; @@ -41,14 +39,13 @@ function extractCompletedToolCalls(chunks: ChatMessage[]): CompletedToolCall[] { } } - return callOrder - .map((id) => callsById.get(id)!) - .filter((c) => c && c.name); + return callOrder.map((id) => callsById.get(id)!).filter((c) => c && c.name); } -function extractUsageFromChunks( - chunks: ChatMessage[], -): { promptTokens: number; completionTokens: number } { +function extractUsageFromChunks(chunks: ChatMessage[]): { + promptTokens: number; + completionTokens: number; +} { for (let i = chunks.length - 1; i >= 0; i--) { const chunk = chunks[i]; if (chunk.role === "assistant" && chunk.usage) { @@ -84,20 +81,24 @@ async function executeShadowTool( } if (call.name === "shadow_search_messages") { - const query: string = - typeof args.query === "string" ? args.query : ""; - const limit: number = - typeof args.limit === "number" ? args.limit : 10; - const results = await ShadowChatDb.searchMessages(sessionId, query, limit); + const query: string = typeof args.query === "string" ? args.query : ""; + const limit: number = typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.searchMessages( + sessionId, + query, + limit, + ); return JSON.stringify(results); } if (call.name === "shadow_semantic_search") { - const query: string = - typeof args.query === "string" ? args.query : ""; - const limit: number = - typeof args.limit === "number" ? args.limit : 10; - const results = await ShadowChatDb.semanticSearch(sessionId, query, limit); + const query: string = typeof args.query === "string" ? args.query : ""; + const limit: number = typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.semanticSearch( + sessionId, + query, + limit, + ); return JSON.stringify(results); } @@ -118,35 +119,38 @@ async function executeShadowTool( typeof args.tool_name === "string" ? args.tool_name : ""; const maxAgeTurns: number = typeof args.max_age_turns === "number" ? args.max_age_turns : 5; - const result = await ShadowChatDb.getToolResult( + const cached = await ShadowChatDb.getToolResult( sessionId, toolName, maxAgeTurns, ); - if (result === undefined) { + if (cached === undefined) { return JSON.stringify({ found: false, message: `No cached result found for tool '${toolName}' within the last ${maxAgeTurns} turns.`, }); } - return JSON.stringify({ found: true, result }); + return JSON.stringify({ + found: true, + input: cached.inputArgs, + result: cached.result, + }); } if (call.name === "shadow_search_all_sessions") { - const query: string = - typeof args.query === "string" ? args.query : ""; - const limit: number = - typeof args.limit === "number" ? args.limit : 10; + const query: string = typeof args.query === "string" ? args.query : ""; + const limit: number = typeof args.limit === "number" ? args.limit : 10; const results = await ShadowChatDb.searchAllSessions(query, limit); return JSON.stringify(results); } if (call.name === "shadow_semantic_search_all_sessions") { - const query: string = - typeof args.query === "string" ? args.query : ""; - const limit: number = - typeof args.limit === "number" ? args.limit : 10; - const results = await ShadowChatDb.semanticSearchAllSessions(query, limit); + const query: string = typeof args.query === "string" ? args.query : ""; + const limit: number = typeof args.limit === "number" ? args.limit : 10; + const results = await ShadowChatDb.semanticSearchAllSessions( + query, + limit, + ); return JSON.stringify(results); } @@ -190,11 +194,17 @@ export async function* tokenOptimizedStreamChat( ? currentUserMsg.content : JSON.stringify(currentUserMsg.content); - // Shadow tools let the LLM pull history on demand instead of receiving it all upfront - const shadowTools = createShadowHistoryToolDefinitions(); + // Shadow tools let the LLM pull history on demand instead of receiving it all + // upfront. They're normally included via the client's active tool list, but + // ultra mode's correctness depends on them always being present, so they're + // force-included here regardless of the user's tool settings. + const clientTools = options.tools ?? []; + const missingShadowTools = shadowChatHistoryTools.filter( + (st) => !clientTools.some((t) => t.function.name === st.function.name), + ); const augmentedOptions: LLMFullCompletionOptions = { ...options, - tools: [...shadowTools, ...(options.tools ?? [])], + tools: [...clientTools, ...missingShadowTools], }; let loopMessages: ChatMessage[] = [ @@ -223,7 +233,11 @@ export async function* tokenOptimizedStreamChat( chunks.push(next.value); next = await gen.next(); } - if (next.value && typeof next.value === "object" && "prompt" in next.value) { + if ( + next.value && + typeof next.value === "object" && + "prompt" in next.value + ) { finalPromptLog = next.value as PromptLog; } @@ -245,7 +259,9 @@ export async function* tokenOptimizedStreamChat( break; } - const shadowCalls = toolCalls.filter((tc) => SHADOW_TOOL_NAMES.has(tc.name)); + const shadowCalls = toolCalls.filter((tc) => + SHADOW_TOOL_NAMES.has(tc.name), + ); const externalCalls = toolCalls.filter( (tc) => !SHADOW_TOOL_NAMES.has(tc.name), ); @@ -258,7 +274,13 @@ export async function* tokenOptimizedStreamChat( break; } - // All tool calls are shadow tools — execute server-side and loop + // All tool calls are shadow tools — execute server-side (never round-tripped + // to the client, so the reduced-history guarantee holds), but still yield + // the resolved call/result so the client renders the same tool-call card + // it would for any other tool. Since this is followed by more streamed + // chunks from the next loop iteration (ending in plain text), the client's + // "pending tool call" detection naturally treats it as already resolved + // history rather than something it needs to execute itself. const assistantToolCallMsg: AssistantChatMessage = { role: "assistant", content: "", @@ -269,7 +291,9 @@ export async function* tokenOptimizedStreamChat( })), }; loopMessages = [...loopMessages, assistantToolCallMsg]; + yield assistantToolCallMsg; + const turnIndex = await ShadowChatDb.getCurrentTurnIndex(sessionId); for (const call of shadowCalls) { const result = await executeShadowTool(call, sessionId, historyLimit); const toolResultMsg: ChatMessage = { @@ -278,6 +302,16 @@ export async function* tokenOptimizedStreamChat( toolCallId: call.id, }; loopMessages = [...loopMessages, toolResultMsg]; + yield toolResultMsg; + + await ShadowChatDb.saveToolCall( + sessionId, + call.name, + call.id, + call.args, + result, + turnIndex, + ); } // Loop: the LLM will now see the tool results and produce its final answer } diff --git a/core/package-lock.json b/core/package-lock.json index 7c75889e0e4..a74e4ae7c59 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -65,7 +65,7 @@ "shell-quote": "^1.8.3", "socket.io-client": "^4.7.3", "sqlite": "^5.1.1", - "sqlite3": "^5.1.7", + "sqlite3": "^6.0.1", "system-ca": "^1.0.3", "tar": "^7.5.13", "tree-sitter-wasms": "^0.1.11", @@ -3178,6 +3178,16 @@ "kuler": "^2.0.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -3738,13 +3748,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "license": "MIT", - "optional": true - }, "node_modules/@google/generative-ai": { "version": "0.11.5", "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.11.5.tgz", @@ -3791,53 +3794,601 @@ "node": ">=10.10.0" } }, - "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT" - }, - "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { - "version": "1.1.15", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", - "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", - "license": "MIT", + "node_modules/@humanwhocodes/config-array/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" + }, + "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz", + "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "license": "BSD-3-Clause" + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.3.tgz", + "integrity": "sha512-RMnFX7YQsMoh7lWfcM4NEHHymBX/rLuKNPVM84XE9ONPcaSCDgE7CHIHpSgPcO2xcRthgBy1HfNO319mwhIAkg==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.3.tgz", + "integrity": "sha512-Xo+5uFBtLN0BKqieTxiFzFPQAUlBbbH5iBKyRX/z1JrbnYsHTfKJnUfL8+p2TPXr1pXqao4eeL4Rl144uDpK9w==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.3.tgz", + "integrity": "sha512-lUxcqWIj2wMQ9BrwNjngcr1gWUr5xgaGThBRqPPalIC2n67Cqj1uPh8NnA/ZhAg8hUbKl+kVHKwgUIwe6ZYPrg==", + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.2.tgz", + "integrity": "sha512-9J6ypZFpQBj4YnePGoq/S38w6nz+vqg5WZLrLGY4YuSemdMq47GMLBPO42MzwdGwpg/agZ7xzZcFHa48xlywfg==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.2.tgz", + "integrity": "sha512-m2pW1n6cns9VaubNwsZ+c3CRYjxNQWgJ5gPlnL1nbBcpkBvFm6SCFN5o0psFHI8w9n11NKhFkeEDns98tiqbEw==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.2.tgz", + "integrity": "sha512-1eMLzy92I4J6rmi4mAT8yC3HxOtniyGELlzGbNMLLeqe052ahFQ0h6LFq+lh5DsDIdYViIDst08abvSbcEdLXQ==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.2.tgz", + "integrity": "sha512-dqVSFynCox4C/J8kT16V7SIFAns0IjgLwkvYT7p8LQVmJ5OS5b6tI9IGflxTeuBS//zXeFIUbwt5dwxyZ17cnA==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.2.tgz", + "integrity": "sha512-3z0NHDxD6n5I9gc05U1eW1AyRm+Gznzq3naMrthPNqE6oYykcogW0l/jfpJdjYnuNl8R7yI9pNbE1XiUeyq0Aw==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.2.tgz", + "integrity": "sha512-bsb4rI+NldGOsXuej2r8OdSS8+zXDVaCWxyWrcv6kneTOlgAHtZABRzBBCwdsPiD90J4myNJuHpg6kA20ImW/w==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.2.tgz", + "integrity": "sha512-/ABshyj8gCpyIrNXnHn4LorDJ0HHm1VhXPBlxZ8zAtfVPAaSafXPGn+sUSIRiwaSBy0mmFjSjiXI5mkcwdChKQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.2.tgz", + "integrity": "sha512-ITPEtgffGJ0S6G9dRyw/366tJQqFRcHWPHhC+Stpg3Z8AEMrDrTr2lhdz4f/Y/HMbRh//7Z5mBzEpVdi62Oc3w==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.2.tgz", + "integrity": "sha512-zE9EdiUzUmg5mDT5a1rk5fYJ6GWPloTwWBYDS14naqHsL+EaMpDj1AWnpLgh3u0YCORv2Tt50wrcrpYqkP97Kw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.2.tgz", + "integrity": "sha512-m0lrLiUt+lBYnCFr8qV/65yMR4E/c7/wf78I5eKTdkEakFAlZ9QlzEM3QIhhAwVeUhLAHLcCq7a7Vszq/oFNZQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.3.tgz", + "integrity": "sha512-affVWCTLooy8TSxbDx2qkzuDeaWLNVBA+P//FNBirHsXpP2fuBhk5AuboYUnrDnzoXes8GFjpTx0SBFOCRg+FA==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.3.tgz", + "integrity": "sha512-QgKDspHPnrU+GQ55XPhGwyhC8acLVOOSyAvo1oVfFmrIXLkDNmGWzAfDZ4xK8oSA1qBQrALcHX0G5UZni/SuFQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.3.tgz", + "integrity": "sha512-sMd8rDxmpLOwv/7N44klFjOD5DUO7FLdjiXDI0hoxYaf7Ar262dQIEkosE98bps+5HPLtp/EvNqeqQtOycP/IA==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.3.tgz", + "integrity": "sha512-0Eob78yjlYPfL5vMNWAW55l3R9Y6BQS/gOfe0ZcP9mEz9ohhKSt4im1hayiknXgf8AWrFqMvJcKIdmLmEe7yeQ==", + "cpu": [ + "riscv64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.3.tgz", + "integrity": "sha512-KgAxQ0DxpNOq1rG2t5cgTgShJFGSuU7XO45cqC+1NVOuZnP6tlgZRuSYOfNupGkHID0o3cJOsw4DVeJpMovcGw==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.2" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.3.tgz", + "integrity": "sha512-8pqvxubL2PGdhlPy6GLqzDYMUjyRmKAwKHYKixpdJYBUK7PJ0C029XdsnpFIdgRZG68fZiGdHVWcKPvtiPB4cA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.3.tgz", + "integrity": "sha512-Vz0iQjzzcSX3HCbfwFfCSG/9SCIqyO0mH2sXyiHaAYfBk0cRsCWXRyQYX0ovCK/PAQBbTzQ0dsPQHh5MAFL59w==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.3.tgz", + "integrity": "sha512-6O1NPKcDVj9QEdg7Hx549EX8U0rp6yXQERqru6yRN7fGBn32UvIRJUlWnk+8xDCiG76hXVBbX82NZ/ZKr0euIg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.2" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.3.tgz", + "integrity": "sha512-cZ0XkcYGpHZkqW6iCkqTcmUC0CD9DhD5d/qeZlZkfRBn6GnHniZXLUo5+9xw8Iv76YE6LQFN9YNBlKREcCG76w==", + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "@emnapi/runtime": "^1.11.1" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { - "version": "3.1.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", - "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", - "license": "ISC", + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.3.tgz", + "integrity": "sha512-2rnq7bX3NzeR2T4YWgz8qiG4h3TSdMe+vN1iQXpJleSJ3SM5zQ8Fy2SyyXAWlbxpEZ2Y+Z4u1BePgJEYbSy80Q==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0", + "optional": true, "dependencies": { - "brace-expansion": "^1.1.7" + "@img/sharp-wasm32": "0.35.3" }, "engines": { - "node": "*" + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "license": "Apache-2.0", + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.3.tgz", + "integrity": "sha512-4bPwFdMbeC4JQ8L8LOyWp6nsHcboP5fxkp6iPOXz2Vg49R42TuMs2whkJ5OAP4/Ul035qOzy0AecOF9VOscn4w==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=12.22" + "node": ">=20.9.0" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "url": "https://opencollective.com/libvips" } }, - "node_modules/@humanwhocodes/object-schema": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", - "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", - "deprecated": "Use @eslint/object-schema instead", - "license": "BSD-3-Clause" + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.3.tgz", + "integrity": "sha512-r53mXsBN6lFUDiST764SvgwUdHAqM4rPAiDzAmf4fLoB6X/rkfyTrLCg6+g17wJJiCmB3JYgHuUldCWUIRFSXw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.3.tgz", + "integrity": "sha512-D4y1vNeZrIIJCN+uHaWVtH86B+aCrdMYYjicy9pXHvbGZeGYLLSd3wdVuC37FxVXlU1ARsk84eKWfWMXGYEqvA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", @@ -4530,45 +5081,6 @@ "node": ">= 8" } }, - "node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/@npmcli/fs/node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", - "license": "ISC", - "optional": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "license": "MIT", - "optional": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/@octokit/auth-token": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", @@ -5532,8 +6044,7 @@ "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" - }, - "peerDependencies": {} + } }, "node_modules/@types/babel__template": { "version": "7.4.4", @@ -5554,8 +6065,7 @@ "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" - }, - "peerDependencies": {} + } }, "node_modules/@types/caseless": { "version": "0.12.5", @@ -5573,8 +6083,7 @@ "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" - }, - "peerDependencies": {} + } }, "node_modules/@types/command-line-args": { "version": "5.2.0", @@ -5602,16 +6111,14 @@ "resolved": "https://registry.npmjs.org/@types/diff/-/diff-7.0.2.tgz", "integrity": "sha512-JSWRMozjFKsGlEjiiKajUjIJVKuKdE3oVy2DNtK+fUo8q82nhFZ2CPQwicAIkXrofahDXrWJ7mjelvZphMS98Q==", "dev": true, - "license": "MIT", - "peerDependencies": {} + "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, - "license": "MIT", - "peerDependencies": {} + "license": "MIT" }, "node_modules/@types/follow-redirects": { "version": "1.14.4", @@ -5679,8 +6186,7 @@ "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" - }, - "peerDependencies": {} + } }, "node_modules/@types/jquery": { "version": "3.5.34", @@ -5690,8 +6196,7 @@ "license": "MIT", "dependencies": { "@types/sizzle": "*" - }, - "peerDependencies": {} + } }, "node_modules/@types/jsdom": { "version": "21.1.7", @@ -5747,8 +6252,7 @@ "resolved": "https://registry.npmjs.org/@types/mustache/-/mustache-4.2.6.tgz", "integrity": "sha512-t+8/QWTAhOFlrF1IVZqKnMRJi84EgkIK5Kh0p2JV4OLywUvCwJPFxbJAl7XAow7DVIHsF+xW9f1MVzg0L6Szjw==", "dev": true, - "license": "MIT", - "peerDependencies": {} + "license": "MIT" }, "node_modules/@types/node": { "version": "25.9.2", @@ -5757,8 +6261,7 @@ "license": "MIT", "dependencies": { "undici-types": ">=7.24.0 <7.24.7" - }, - "peerDependencies": {} + } }, "node_modules/@types/node-fetch": { "version": "2.6.13", @@ -5769,8 +6272,7 @@ "dependencies": { "@types/node": "*", "form-data": "^4.0.4" - }, - "peerDependencies": {} + } }, "node_modules/@types/node-forge": { "version": "1.3.14", @@ -5780,8 +6282,7 @@ "license": "MIT", "dependencies": { "@types/node": "*" - }, - "peerDependencies": {} + } }, "node_modules/@types/pad-left": { "version": "2.1.1", @@ -5800,8 +6301,7 @@ "@types/node": "*", "pg-protocol": "*", "pg-types": "^2.2.0" - }, - "peerDependencies": {} + } }, "node_modules/@types/plist": { "version": "3.0.5", @@ -5821,8 +6321,7 @@ "license": "MIT", "dependencies": { "@types/node": "*" - }, - "peerDependencies": {} + } }, "node_modules/@types/request": { "version": "2.48.13", @@ -5835,8 +6334,7 @@ "@types/node": "*", "@types/tough-cookie": "*", "form-data": "^2.5.5" - }, - "peerDependencies": {} + } }, "node_modules/@types/request/node_modules/form-data": { "version": "2.5.5", @@ -5891,8 +6389,7 @@ "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.10.tgz", "integrity": "sha512-TC0dmN0K8YcWEAEfiPi5gJP14eJe30TTGjkvek3iM/1NdHHsdCA/Td6GvNndMOo/iSnIsZ4HuuhrYPDAmbxzww==", "dev": true, - "license": "MIT", - "peerDependencies": {} + "license": "MIT" }, "node_modules/@types/stack-utils": { "version": "2.0.3", @@ -5958,8 +6455,7 @@ "license": "MIT", "dependencies": { "@types/yargs-parser": "*" - }, - "peerDependencies": {} + } }, "node_modules/@types/yargs-parser": { "version": "21.0.3", @@ -6397,11 +6893,14 @@ "license": "BSD-3-Clause" }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "license": "ISC", - "optional": true + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -6491,33 +6990,6 @@ "node": ">= 14" } }, - "node_modules/agentkeepalive": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", - "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "license": "MIT", - "optional": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -6653,36 +7125,6 @@ "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", "license": "ISC" }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "license": "MIT", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", @@ -7459,8 +7901,7 @@ "license": "MIT", "engines": { "node": "*" - }, - "optionalDependencies": {} + } }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", @@ -7509,62 +7950,6 @@ "node": ">=8" } }, - "node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/cacache/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/cacache/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/call-bind": { "version": "1.0.9", "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.9.tgz", @@ -7796,16 +8181,6 @@ "url": "https://github.com/sponsors/fb55" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "license": "ISC", - "optional": true, - "engines": { - "node": ">=10" - } - }, "node_modules/chromium-bidi": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-14.0.0.tgz", @@ -7842,16 +8217,6 @@ "dev": true, "license": "MIT" }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=6" - } - }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -7884,19 +8249,6 @@ "dev": true, "license": "MIT" }, - "node_modules/color": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", - "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1", - "color-string": "^1.9.0" - }, - "engines": { - "node": ">=12.5.0" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -7915,16 +8267,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, - "node_modules/color-string": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", - "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "license": "MIT", - "dependencies": { - "color-name": "^1.0.0", - "simple-swizzle": "^0.2.2" - } - }, "node_modules/color-support": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", @@ -8535,13 +8877,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "license": "MIT", - "optional": true - }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -8803,16 +9138,6 @@ "node": ">= 0.8" } }, - "node_modules/encoding": { - "version": "0.1.13", - "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", - "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", - "license": "MIT", - "optional": true, - "dependencies": { - "iconv-lite": "^0.6.2" - } - }, "node_modules/encoding-sniffer": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", @@ -8899,13 +9224,6 @@ "node": ">=6" } }, - "node_modules/err-code": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", - "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", - "license": "MIT", - "optional": true - }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -9702,6 +10020,13 @@ "node": ">=12.0.0" } }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "license": "Apache-2.0", + "optional": true + }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", @@ -10176,32 +10501,6 @@ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", "license": "MIT" }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -10923,13 +11222,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/http-cache-semantics": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", - "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", - "license": "BSD-2-Clause", - "optional": true - }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", @@ -10986,16 +11278,6 @@ "node": ">=10.17.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -11082,23 +11364,6 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "license": "ISC", - "optional": true - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -11410,13 +11675,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "license": "MIT", - "optional": true - }, "node_modules/is-localhost-ip": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-localhost-ip/-/is-localhost-ip-2.0.0.tgz", @@ -13275,137 +13533,6 @@ "dev": true, "license": "ISC" }, - "node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "license": "ISC", - "optional": true, - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/make-fetch-happen/node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "license": "MIT", - "optional": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/make-fetch-happen/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/make-fetch-happen/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/make-fetch-happen/node_modules/negotiator": { - "version": "0.6.4", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", - "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/make-fetch-happen/node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", @@ -13594,235 +13721,73 @@ }, "node_modules/mime-types": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", - "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", - "license": "MIT", - "dependencies": { - "mime-db": "^1.54.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, - "node_modules/mimic-fn": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", - "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/mimic-response": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", - "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/minimatch": { - "version": "10.2.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", - "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "brace-expansion": "^5.0.5" - }, - "engines": { - "node": "18 || 20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minimist": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", - "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/minipass": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", - "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-collect/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", - "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, - "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" - } - }, - "node_modules/minipass-fetch/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-flush": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.7.tgz", - "integrity": "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA==", - "license": "BlueOak-1.0.0", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minipass-flush/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/minipass-pipeline": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", - "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", - "license": "ISC", - "optional": true, + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "mime-db": "^1.54.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, - "node_modules/minipass-pipeline/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=6" } }, - "node_modules/minipass-sized": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", - "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/minipass-sized/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "yallist": "^4.0.0" + "brace-expansion": "^5.0.5" }, "engines": { - "node": ">=8" + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", - "optional": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, "engines": { "node": ">=8" } @@ -13833,19 +13798,6 @@ "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", "license": "MIT" }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "license": "MIT", - "optional": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", @@ -14042,10 +13994,13 @@ } }, "node_modules/node-addon-api": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-6.1.0.tgz", - "integrity": "sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==", - "license": "MIT" + "version": "8.9.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.1.tgz", + "integrity": "sha512-4eUQWVPCUUUiBjLnHS3cXWeC6ryoPUc0U3rP7IuzapoGbzMqd/r6KKO0clr0b+snQhsrueFEhCZDdK+LK7hxKg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/node-domexception": { "version": "1.0.0", @@ -14114,34 +14069,44 @@ } }, "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", "license": "MIT", "optional": true, "dependencies": { "env-paths": "^2.2.0", - "glob": "^7.1.4", + "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">= 10.12.0" + "node": "^20.17.0 || >=22.9.0" + } + }, + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", + "optional": true, + "engines": { + "node": ">=20" } }, "node_modules/node-gyp/node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "optional": true, "bin": { @@ -14151,6 +14116,32 @@ "node": ">=10" } }, + "node_modules/node-gyp/node_modules/undici": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18.17" + } + }, + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", + "optional": true, + "dependencies": { + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" + }, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/node-html-markdown": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/node-html-markdown/-/node-html-markdown-1.3.0.tgz", @@ -14213,19 +14204,19 @@ } }, "node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "license": "ISC", "optional": true, "dependencies": { - "abbrev": "1" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/normalize-path": { @@ -14251,44 +14242,6 @@ "node": ">=8" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/npmlog/node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "license": "ISC", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -14691,22 +14644,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "license": "MIT", - "optional": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -15396,6 +15333,16 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } + }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -15414,27 +15361,6 @@ "node": ">=0.4.0" } }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "license": "ISC", - "optional": true - }, - "node_modules/promise-retry": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", - "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", - "license": "MIT", - "optional": true, - "dependencies": { - "err-code": "^2.0.2", - "retry": "^0.12.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -16109,16 +16035,6 @@ "node": ">=10" } }, - "node_modules/retry": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", - "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">= 4" - } - }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", @@ -16409,13 +16325,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "license": "ISC", - "optional": true - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -16471,32 +16380,58 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.32.6", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.32.6.tgz", - "integrity": "sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==", - "hasInstallScript": true, + "version": "0.35.3", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.3.tgz", + "integrity": "sha512-ej0zVHuZGHCiABXcNxeYhpRnPNPAcvbG8RMdBAhDAxLKkCRVSpK3Iyu7qbqw3JMzoj0REeM6f3tJLtVwl0023Q==", "license": "Apache-2.0", "dependencies": { - "color": "^4.2.3", - "detect-libc": "^2.0.2", - "node-addon-api": "^6.1.0", - "prebuild-install": "^7.1.1", - "semver": "^7.5.4", - "simple-get": "^4.0.1", - "tar-fs": "^3.0.4", - "tunnel-agent": "^0.6.0" + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" }, "engines": { - "node": ">=14.15.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.3", + "@img/sharp-darwin-x64": "0.35.3", + "@img/sharp-freebsd-wasm32": "0.35.3", + "@img/sharp-libvips-darwin-arm64": "1.3.2", + "@img/sharp-libvips-darwin-x64": "1.3.2", + "@img/sharp-libvips-linux-arm": "1.3.2", + "@img/sharp-libvips-linux-arm64": "1.3.2", + "@img/sharp-libvips-linux-ppc64": "1.3.2", + "@img/sharp-libvips-linux-riscv64": "1.3.2", + "@img/sharp-libvips-linux-s390x": "1.3.2", + "@img/sharp-libvips-linux-x64": "1.3.2", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.2", + "@img/sharp-libvips-linuxmusl-x64": "1.3.2", + "@img/sharp-linux-arm": "0.35.3", + "@img/sharp-linux-arm64": "0.35.3", + "@img/sharp-linux-ppc64": "0.35.3", + "@img/sharp-linux-riscv64": "0.35.3", + "@img/sharp-linux-s390x": "0.35.3", + "@img/sharp-linux-x64": "0.35.3", + "@img/sharp-linuxmusl-arm64": "0.35.3", + "@img/sharp-linuxmusl-x64": "0.35.3", + "@img/sharp-webcontainers-wasm32": "0.35.3", + "@img/sharp-win32-arm64": "0.35.3", + "@img/sharp-win32-ia32": "0.35.3", + "@img/sharp-win32-x64": "0.35.3" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/sharp/node_modules/semver": { - "version": "7.8.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", - "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -16638,7 +16573,7 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "devOptional": true, + "dev": true, "license": "ISC" }, "node_modules/simple-concat": { @@ -16686,21 +16621,6 @@ "simple-concat": "^1.0.0" } }, - "node_modules/simple-swizzle": { - "version": "0.2.4", - "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", - "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", - "license": "MIT", - "dependencies": { - "is-arrayish": "^0.3.1" - } - }, - "node_modules/simple-swizzle/node_modules/is-arrayish": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", - "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", - "license": "MIT" - }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -16844,8 +16764,7 @@ }, "engines": { "node": "*" - }, - "optionalDependencies": {} + } }, "node_modules/split2": { "version": "4.2.0", @@ -16884,22 +16803,25 @@ "license": "MIT" }, "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-6.0.1.tgz", + "integrity": "sha512-X0czUUMG2tmSqJpEQa3tCuZSHKIx8PwM53vLZzKp/o6Rpy25fiVfjdbnZ988M8+O3ZWR1ih0K255VumCb3MAnQ==", "hasInstallScript": true, "license": "BSD-3-Clause", "dependencies": { "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" + "node-addon-api": "^8.0.0", + "prebuild-install": "^7.1.3", + "tar": "^7.5.10" + }, + "engines": { + "node": ">=20.17.0" }, "optionalDependencies": { - "node-gyp": "8.x" + "node-gyp": "12.x" }, "peerDependencies": { - "node-gyp": "8.x" + "node-gyp": "12.x" }, "peerDependenciesMeta": { "node-gyp": { @@ -16907,38 +16829,6 @@ } } }, - "node_modules/sqlite3/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "minipass": "^3.1.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/ssri/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "license": "ISC", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/stack-trace": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", @@ -17510,7 +17400,7 @@ "version": "0.2.17", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", - "dev": true, + "devOptional": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -17527,7 +17417,7 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -17545,7 +17435,7 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, + "devOptional": true, "license": "MIT", "engines": { "node": ">=12" @@ -17813,8 +17703,7 @@ }, "engines": { "node": "*" - }, - "optionalDependencies": {} + } }, "node_modules/type-check": { "version": "0.4.0", @@ -18114,26 +18003,6 @@ "node": ">=4" } }, - "node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "license": "ISC", - "optional": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "license": "ISC", - "optional": true, - "dependencies": { - "imurmurhash": "^0.1.4" - } - }, "node_modules/unist-util-is": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.1.tgz", @@ -19366,8 +19235,7 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "license": "MIT", - "optional": true, - "optionalDependencies": {} + "optional": true }, "node_modules/wink-distance": { "version": "2.0.2", @@ -19674,13 +19542,6 @@ "node": ">=10" } }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC", - "optional": true - }, "node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", diff --git a/core/package.json b/core/package.json index b8441cc339c..3e883ea24f1 100644 --- a/core/package.json +++ b/core/package.json @@ -110,7 +110,7 @@ "shell-quote": "^1.8.3", "socket.io-client": "^4.7.3", "sqlite": "^5.1.1", - "sqlite3": "^5.1.7", + "sqlite3": "^6.0.1", "system-ca": "^1.0.3", "tar": "^7.5.13", "tree-sitter-wasms": "^0.1.11", @@ -130,6 +130,8 @@ "node": ">=20.20.1" }, "overrides": { - "tar": "^7.5.13" + "tar": "^7.5.13", + "sharp": "^0.35.3", + "sqlite3": "^6.0.1" } } diff --git a/core/protocol/core.ts b/core/protocol/core.ts index da752c27b0b..2d65fcb43e4 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -224,6 +224,7 @@ export type ToCoreFromIdeOrWebviewProtocol = { completionOptions: LLMFullCompletionOptions; title: string; messageOptions?: MessageOption; + sessionId?: string; legacySlashCommandData?: { command: SlashCommandDescWithSource; input: string; @@ -301,7 +302,7 @@ export type ToCoreFromIdeOrWebviewProtocol = { "auth/getAuthUrl": [{ useOnboarding: boolean }, { url: string }]; "tools/call": [ - { toolCall: ToolCall }, + { toolCall: ToolCall; sessionId?: string }, { contextItems: ContextItem[]; errorMessage?: string; diff --git a/core/tools/builtIn.ts b/core/tools/builtIn.ts index 0b1346bbca2..1af259b46cb 100644 --- a/core/tools/builtIn.ts +++ b/core/tools/builtIn.ts @@ -18,6 +18,15 @@ export enum BuiltInToolNames { CodebaseTool = "codebase", ReadSkill = "read_skill", + // Shadow chat history tools — backed by the local per-session SQLite cache + ShadowGetChatHistory = "shadow_get_chat_history", + ShadowSearchMessages = "shadow_search_messages", + ShadowSemanticSearch = "shadow_semantic_search", + ShadowGetConversationStats = "shadow_get_conversation_stats", + ShadowGetToolResult = "shadow_get_tool_result", + ShadowSearchAllSessions = "shadow_search_all_sessions", + ShadowSemanticSearchAllSessions = "shadow_semantic_search_all_sessions", + // excluded from allTools for now ViewRepoMap = "view_repo_map", ViewSubdirectory = "view_subdirectory", @@ -25,6 +34,16 @@ export enum BuiltInToolNames { export const BUILT_IN_GROUP_NAME = "Built-In"; +export const SHADOW_TOOL_NAMES: ReadonlySet = new Set([ + BuiltInToolNames.ShadowGetChatHistory, + BuiltInToolNames.ShadowSearchMessages, + BuiltInToolNames.ShadowSemanticSearch, + BuiltInToolNames.ShadowGetConversationStats, + BuiltInToolNames.ShadowGetToolResult, + BuiltInToolNames.ShadowSearchAllSessions, + BuiltInToolNames.ShadowSemanticSearchAllSessions, +]); + export const CLIENT_TOOLS_IMPLS = [ BuiltInToolNames.EditExistingFile, BuiltInToolNames.SingleFindAndReplace, diff --git a/core/tools/callTool.ts b/core/tools/callTool.ts index 22e83bb46ab..d8144610fc4 100644 --- a/core/tools/callTool.ts +++ b/core/tools/callTool.ts @@ -1,5 +1,6 @@ import { CallToolResultSchema } from "@modelcontextprotocol/sdk/types.js"; import { ContextItem, McpUiState, Tool, ToolCall, ToolExtras } from ".."; +import { ShadowChatDb } from "../data/shadowChatDb"; import { MCPManagerSingleton } from "../context/mcp/MCPManagerSingleton"; import { ContinueError, ContinueErrorReason } from "../util/errors"; import { canParseUrl } from "../util/url"; @@ -20,6 +21,15 @@ import { readSkillImpl } from "./implementations/readSkill"; import { requestRuleImpl } from "./implementations/requestRule"; import { runTerminalCommandImpl } from "./implementations/runTerminalCommand"; import { searchWebImpl } from "./implementations/searchWeb"; +import { + shadowGetChatHistoryImpl, + shadowGetConversationStatsImpl, + shadowGetToolResultImpl, + shadowSearchAllSessionsImpl, + shadowSearchMessagesImpl, + shadowSemanticSearchAllSessionsImpl, + shadowSemanticSearchImpl, +} from "./implementations/shadowChatHistory"; import { viewDiffImpl } from "./implementations/viewDiff"; import { viewRepoMapImpl } from "./implementations/viewRepoMap"; import { viewSubdirectoryImpl } from "./implementations/viewSubdirectory"; @@ -224,6 +234,20 @@ export async function callBuiltInTool( return await viewRepoMapImpl(args, extras); case BuiltInToolNames.ViewSubdirectory: return await viewSubdirectoryImpl(args, extras); + case BuiltInToolNames.ShadowGetChatHistory: + return await shadowGetChatHistoryImpl(args, extras); + case BuiltInToolNames.ShadowSearchMessages: + return await shadowSearchMessagesImpl(args, extras); + case BuiltInToolNames.ShadowSemanticSearch: + return await shadowSemanticSearchImpl(args, extras); + case BuiltInToolNames.ShadowGetConversationStats: + return await shadowGetConversationStatsImpl(args, extras); + case BuiltInToolNames.ShadowGetToolResult: + return await shadowGetToolResultImpl(args, extras); + case BuiltInToolNames.ShadowSearchAllSessions: + return await shadowSearchAllSessionsImpl(args, extras); + case BuiltInToolNames.ShadowSemanticSearchAllSessions: + return await shadowSemanticSearchAllSessionsImpl(args, extras); default: throw new Error(`Tool "${functionName}" not found`); } @@ -255,6 +279,28 @@ export async function callTool( }); } + if (extras.sessionId && toolCall.id) { + // Best-effort: record input/output so shadow_get_tool_result can serve + // this immediately, without blocking or failing the actual tool call. + void (async () => { + try { + const turnIndex = await ShadowChatDb.getCurrentTurnIndex( + extras.sessionId!, + ); + await ShadowChatDb.saveToolCall( + extras.sessionId!, + tool.function.name, + toolCall.id!, + JSON.stringify(args), + JSON.stringify(contextItems), + turnIndex, + ); + } catch (e) { + console.error("Failed to record tool call for shadow chat", e); + } + })(); + } + return { contextItems, errorMessage: undefined, diff --git a/core/tools/definitions/index.ts b/core/tools/definitions/index.ts index bfc78ac3d2e..800738ab9e6 100644 --- a/core/tools/definitions/index.ts +++ b/core/tools/definitions/index.ts @@ -15,6 +15,15 @@ export { readSkillTool } from "./readSkill"; export { requestRuleTool } from "./requestRule"; export { runTerminalCommandTool } from "./runTerminalCommand"; export { searchWebTool } from "./searchWeb"; +export { + shadowGetChatHistoryTool, + shadowSearchMessagesTool, + shadowSemanticSearchTool, + shadowGetConversationStatsTool, + shadowGetToolResultTool, + shadowSearchAllSessionsTool, + shadowSemanticSearchAllSessionsTool, +} from "./shadowChatHistory"; export { singleFindAndReplaceTool } from "./singleFindAndReplace"; export { viewDiffTool } from "./viewDiff"; export { viewRepoMapTool } from "./viewRepoMap"; diff --git a/core/tools/definitions/shadowChatHistory.ts b/core/tools/definitions/shadowChatHistory.ts new file mode 100644 index 00000000000..0fdbb22fedc --- /dev/null +++ b/core/tools/definitions/shadowChatHistory.ts @@ -0,0 +1,219 @@ +import { Tool } from "../.."; +import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn"; + +export const shadowGetChatHistoryTool: Tool = { + type: "function", + displayTitle: "Get Chat History", + wouldLikeTo: "retrieve chat history", + isCurrently: "retrieving chat history", + hasAlready: "retrieved chat history", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowGetChatHistory, + description: + "Retrieve the most recent messages from this conversation. Use this when the user refers to something said earlier, asks follow-up questions, or you need context from previous turns.", + parameters: { + type: "object", + properties: { + limit: { + type: "number", + description: + "Maximum number of recent messages to retrieve (default: 20)", + }, + }, + required: [], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowSearchMessagesTool: Tool = { + type: "function", + displayTitle: "Search Messages", + wouldLikeTo: "search chat history", + isCurrently: "searching chat history", + hasAlready: "searched chat history", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowSearchMessages, + description: + "Search this conversation for messages containing specific keywords or phrases. Use when looking for a particular topic, code snippet, or piece of information mentioned earlier.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "The keyword or phrase to search for", + }, + limit: { + type: "number", + description: + "Maximum number of matching messages to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowSemanticSearchTool: Tool = { + type: "function", + displayTitle: "Semantic Search", + wouldLikeTo: "semantically search chat history", + isCurrently: "searching chat history", + hasAlready: "searched chat history", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowSemanticSearch, + description: + "Full-text ranked search of this conversation using BM25 scoring. Finds messages by meaning and relevance, not just exact keyword matches. Prefer this over shadow_search_messages when looking for conceptually related content.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "The search query — describe what you are looking for", + }, + limit: { + type: "number", + description: "Maximum number of results to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowGetConversationStatsTool: Tool = { + type: "function", + displayTitle: "Get Conversation Stats", + wouldLikeTo: "get conversation statistics", + isCurrently: "retrieving conversation statistics", + hasAlready: "retrieved conversation statistics", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowGetConversationStats, + description: + "Get statistics about this conversation: total messages, number of turns, and how many input tokens have been saved so far by Ultra Token Saving mode.", + parameters: { + type: "object", + properties: {}, + required: [], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowGetToolResultTool: Tool = { + type: "function", + displayTitle: "Get Tool Result", + wouldLikeTo: "retrieve a cached tool result", + isCurrently: "retrieving cached tool result", + hasAlready: "retrieved cached tool result", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowGetToolResult, + description: + "Retrieve a cached result from a previously executed tool call in this conversation. Use this to avoid re-running expensive tool calls (file reads, searches, API calls) when their result is still valid.", + parameters: { + type: "object", + properties: { + tool_name: { + type: "string", + description: + "Name of the tool whose result you want to retrieve (e.g. 'read_file', 'run_terminal_command')", + }, + max_age_turns: { + type: "number", + description: "How many conversation turns back to look (default: 5)", + }, + }, + required: ["tool_name"], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowSearchAllSessionsTool: Tool = { + type: "function", + displayTitle: "Search All Sessions", + wouldLikeTo: "search all past chat sessions", + isCurrently: "searching all past sessions", + hasAlready: "searched all past sessions", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowSearchAllSessions, + description: + "Search across all past conversations (not just this one) for messages matching a keyword or phrase. Use when the user refers to something discussed in a previous chat session.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "The keyword or phrase to search for", + }, + limit: { + type: "number", + description: "Maximum number of results to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowSemanticSearchAllSessionsTool: Tool = { + type: "function", + displayTitle: "Semantic Search All Sessions", + wouldLikeTo: "semantically search all past sessions", + isCurrently: "searching all past sessions", + hasAlready: "searched all past sessions", + readonly: true, + isInstant: true, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.ShadowSemanticSearchAllSessions, + description: + "Full-text ranked search across all past conversations using BM25 scoring. Use when the user refers to something discussed in a previous session and a keyword search may not be precise enough.", + parameters: { + type: "object", + properties: { + query: { + type: "string", + description: "The search query — describe what you are looking for", + }, + limit: { + type: "number", + description: "Maximum number of results to return (default: 10)", + }, + }, + required: ["query"], + }, + }, + defaultToolPolicy: "allowedWithoutPermission", +}; + +export const shadowChatHistoryTools: Tool[] = [ + shadowGetChatHistoryTool, + shadowSearchMessagesTool, + shadowSemanticSearchTool, + shadowGetConversationStatsTool, + shadowGetToolResultTool, + shadowSearchAllSessionsTool, + shadowSemanticSearchAllSessionsTool, +]; diff --git a/core/tools/implementations/shadowChatHistory.ts b/core/tools/implementations/shadowChatHistory.ts index 9bd929df585..adbd3aaa1cf 100644 --- a/core/tools/implementations/shadowChatHistory.ts +++ b/core/tools/implementations/shadowChatHistory.ts @@ -1,208 +1,142 @@ -import { Tool } from "../../index.js"; +import { ShadowChatDb } from "../../data/shadowChatDb.js"; +import { ToolImpl } from "./index.js"; -export const SHADOW_TOOL_NAMES = new Set([ - "shadow_get_chat_history", - "shadow_search_messages", - "shadow_semantic_search", - "shadow_get_conversation_stats", - "shadow_get_tool_result", - "shadow_search_all_sessions", - "shadow_semantic_search_all_sessions", -]); +const DEFAULT_HISTORY_LIMIT = 20; +const DEFAULT_SEARCH_LIMIT = 10; +const DEFAULT_MAX_AGE_TURNS = 5; -export function createShadowHistoryToolDefinitions(): Tool[] { +function requireSessionId(extras: { sessionId?: string }): string { + if (!extras.sessionId) { + throw new Error( + "No session ID available for this conversation — shadow chat history tools require one.", + ); + } + return extras.sessionId; +} + +export const shadowGetChatHistoryImpl: ToolImpl = async (args, extras) => { + const sessionId = requireSessionId(extras); + const limit = + typeof args?.limit === "number" ? args.limit : DEFAULT_HISTORY_LIMIT; + const history = await ShadowChatDb.getHistory(sessionId, limit); return [ { - type: "function", - function: { - name: "shadow_get_chat_history", - description: - "Retrieve the most recent messages from this conversation. Use this when the user refers to something said earlier, asks follow-up questions, or you need context from previous turns.", - parameters: { - type: "object", - properties: { - limit: { - type: "number", - description: - "Maximum number of recent messages to retrieve (default: 20)", - }, - }, - required: [], - }, - }, - displayTitle: "Get Chat History", - wouldLikeTo: "retrieve chat history", - isCurrently: "retrieving chat history", - hasAlready: "retrieved chat history", - readonly: true, - group: "shadow", + name: "Chat History", + description: "Recent messages in this conversation", + content: JSON.stringify(history), }, + ]; +}; + +export const shadowSearchMessagesImpl: ToolImpl = async (args, extras) => { + const sessionId = requireSessionId(extras); + const query = typeof args?.query === "string" ? args.query : ""; + const limit = + typeof args?.limit === "number" ? args.limit : DEFAULT_SEARCH_LIMIT; + const results = await ShadowChatDb.searchMessages(sessionId, query, limit); + return [ { - type: "function", - function: { - name: "shadow_search_messages", - description: - "Search this conversation for messages containing specific keywords or phrases. Use when looking for a particular topic, code snippet, or piece of information mentioned earlier.", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: "The keyword or phrase to search for", - }, - limit: { - type: "number", - description: - "Maximum number of matching messages to return (default: 10)", - }, - }, - required: ["query"], - }, - }, - displayTitle: "Search Messages", - wouldLikeTo: "search chat history", - isCurrently: "searching chat history", - hasAlready: "searched chat history", - readonly: true, - group: "shadow", + name: "Search Results", + description: `Messages matching "${query}"`, + content: JSON.stringify(results), }, + ]; +}; + +export const shadowSemanticSearchImpl: ToolImpl = async (args, extras) => { + const sessionId = requireSessionId(extras); + const query = typeof args?.query === "string" ? args.query : ""; + const limit = + typeof args?.limit === "number" ? args.limit : DEFAULT_SEARCH_LIMIT; + const results = await ShadowChatDb.semanticSearch(sessionId, query, limit); + return [ { - type: "function", - function: { - name: "shadow_semantic_search", - description: - "Full-text ranked search of this conversation using BM25 scoring. Finds messages by meaning and relevance, not just exact keyword matches. Prefer this over shadow_search_messages when looking for conceptually related content.", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: - "The search query — describe what you are looking for", - }, - limit: { - type: "number", - description: - "Maximum number of results to return (default: 10)", - }, - }, - required: ["query"], - }, - }, - displayTitle: "Semantic Search", - wouldLikeTo: "semantically search chat history", - isCurrently: "searching chat history", - hasAlready: "searched chat history", - readonly: true, - group: "shadow", + name: "Semantic Search Results", + description: `Ranked results for "${query}"`, + content: JSON.stringify(results), }, + ]; +}; + +export const shadowGetConversationStatsImpl: ToolImpl = async ( + _args, + extras, +) => { + const sessionId = requireSessionId(extras); + const stats = await ShadowChatDb.getConversationStats(sessionId); + const savingsPercent = + stats.totalEstimatedBaselineTokens > 0 + ? Math.round( + (stats.totalTokensSaved / stats.totalEstimatedBaselineTokens) * 100, + ) + : 0; + return [ { - type: "function", - function: { - name: "shadow_get_conversation_stats", - description: - "Get statistics about this conversation: total messages, number of turns, and how many input tokens have been saved so far by Ultra Token Saving mode.", - parameters: { - type: "object", - properties: {}, - required: [], - }, - }, - displayTitle: "Get Conversation Stats", - wouldLikeTo: "get conversation statistics", - isCurrently: "retrieving conversation statistics", - hasAlready: "retrieved conversation statistics", - readonly: true, - group: "shadow", + name: "Conversation Stats", + description: "Token usage and savings for this conversation", + content: JSON.stringify({ ...stats, savingsPercent }), }, + ]; +}; + +export const shadowGetToolResultImpl: ToolImpl = async (args, extras) => { + const sessionId = requireSessionId(extras); + const toolName = typeof args?.tool_name === "string" ? args.tool_name : ""; + const maxAgeTurns = + typeof args?.max_age_turns === "number" + ? args.max_age_turns + : DEFAULT_MAX_AGE_TURNS; + const cached = await ShadowChatDb.getToolResult( + sessionId, + toolName, + maxAgeTurns, + ); + return [ { - type: "function", - function: { - name: "shadow_get_tool_result", - description: - "Retrieve a cached result from a previously executed tool call in this conversation. Use this to avoid re-running expensive tool calls (file reads, searches, API calls) when their result is still valid.", - parameters: { - type: "object", - properties: { - tool_name: { - type: "string", - description: - "Name of the tool whose result you want to retrieve (e.g. 'read_file', 'run_terminal_command')", - }, - max_age_turns: { - type: "number", - description: - "How many conversation turns back to look (default: 5)", - }, - }, - required: ["tool_name"], - }, - }, - displayTitle: "Get Tool Result", - wouldLikeTo: "retrieve a cached tool result", - isCurrently: "retrieving cached tool result", - hasAlready: "retrieved cached tool result", - readonly: true, - group: "shadow", + name: "Cached Tool Result", + description: + cached === undefined + ? `No cached result for '${toolName}'` + : `Cached result for '${toolName}'`, + content: + cached === undefined + ? JSON.stringify({ + found: false, + message: `No cached result found for tool '${toolName}' within the last ${maxAgeTurns} turns.`, + }) + : JSON.stringify({ + found: true, + input: cached.inputArgs, + result: cached.result, + }), }, + ]; +}; + +export const shadowSearchAllSessionsImpl: ToolImpl = async (args) => { + const query = typeof args?.query === "string" ? args.query : ""; + const limit = + typeof args?.limit === "number" ? args.limit : DEFAULT_SEARCH_LIMIT; + const results = await ShadowChatDb.searchAllSessions(query, limit); + return [ { - type: "function", - function: { - name: "shadow_search_all_sessions", - description: - "Search across all past conversations (not just this one) for messages matching a keyword or phrase. Use when the user refers to something discussed in a previous chat session.", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: "The keyword or phrase to search for", - }, - limit: { - type: "number", - description: - "Maximum number of results to return (default: 10)", - }, - }, - required: ["query"], - }, - }, - displayTitle: "Search All Sessions", - wouldLikeTo: "search all past chat sessions", - isCurrently: "searching all past sessions", - hasAlready: "searched all past sessions", - readonly: true, - group: "shadow", + name: "Cross-Session Search Results", + description: `Messages matching "${query}" across all sessions`, + content: JSON.stringify(results), }, + ]; +}; + +export const shadowSemanticSearchAllSessionsImpl: ToolImpl = async (args) => { + const query = typeof args?.query === "string" ? args.query : ""; + const limit = + typeof args?.limit === "number" ? args.limit : DEFAULT_SEARCH_LIMIT; + const results = await ShadowChatDb.semanticSearchAllSessions(query, limit); + return [ { - type: "function", - function: { - name: "shadow_semantic_search_all_sessions", - description: - "Full-text ranked search across all past conversations using BM25 scoring. Use when the user refers to something discussed in a previous session and a keyword search may not be precise enough.", - parameters: { - type: "object", - properties: { - query: { - type: "string", - description: - "The search query — describe what you are looking for", - }, - limit: { - type: "number", - description: - "Maximum number of results to return (default: 10)", - }, - }, - required: ["query"], - }, - }, - displayTitle: "Semantic Search All Sessions", - wouldLikeTo: "semantically search all past sessions", - isCurrently: "searching all past sessions", - hasAlready: "searched all past sessions", - readonly: true, - group: "shadow", + name: "Cross-Session Semantic Search Results", + description: `Ranked results for "${query}" across all sessions`, + content: JSON.stringify(results), }, ]; -} +}; diff --git a/core/tools/index.ts b/core/tools/index.ts index f03eca03161..5812359fcf2 100644 --- a/core/tools/index.ts +++ b/core/tools/index.ts @@ -13,6 +13,13 @@ export const getBaseToolDefinitions = () => [ toolDefinitions.lsTool, toolDefinitions.createRuleBlock, toolDefinitions.fetchUrlContentTool, + toolDefinitions.shadowGetChatHistoryTool, + toolDefinitions.shadowSearchMessagesTool, + toolDefinitions.shadowSemanticSearchTool, + toolDefinitions.shadowGetConversationStatsTool, + toolDefinitions.shadowGetToolResultTool, + toolDefinitions.shadowSearchAllSessionsTool, + toolDefinitions.shadowSemanticSearchAllSessionsTool, ]; export const getConfigDependentToolDefinitions = async ( diff --git a/core/util/shadowChatSessionId.ts b/core/util/shadowChatSessionId.ts index 19a0b7c3b34..c034060d6a3 100644 --- a/core/util/shadowChatSessionId.ts +++ b/core/util/shadowChatSessionId.ts @@ -3,10 +3,13 @@ import crypto from "crypto"; import { ChatMessage } from "../index.js"; export function deriveSessionId(messages: ChatMessage[]): string { - const first = messages[0]; + // messages[0] may be a system message (constructMessages.ts unshifts one onto + // every request), and that system prompt is identical across unrelated + // conversations — hashing it would collide all of them into one session. + const firstUser = messages.find((m) => m.role === "user"); const content = - typeof first?.content === "string" - ? first.content - : JSON.stringify(first?.content ?? ""); + typeof firstUser?.content === "string" + ? firstUser.content + : JSON.stringify(firstUser?.content ?? ""); return crypto.createHash("sha256").update(content).digest("hex").slice(0, 32); } diff --git a/extensions/vscode/package-lock.json b/extensions/vscode/package-lock.json index 6fb35c7dde9..0f1611d4a3c 100644 --- a/extensions/vscode/package-lock.json +++ b/extensions/vscode/package-lock.json @@ -1,12 +1,12 @@ { "name": "continue", - "version": "1.3.39", + "version": "1.3.40", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "continue", - "version": "1.3.39", + "version": "1.3.40", "license": "Apache-2.0", "dependencies": { "@continuedev/config-types": "file:../../packages/config-types", @@ -153,7 +153,7 @@ "shell-quote": "^1.8.3", "socket.io-client": "^4.7.3", "sqlite": "^5.1.1", - "sqlite3": "^5.1.7", + "sqlite3": "^6.0.1", "system-ca": "^1.0.3", "tar": "^7.5.13", "tree-sitter-wasms": "^0.1.11", @@ -244,12 +244,6 @@ "vitest": "^3.2.0" } }, - "../packages/config-types": { - "extraneous": true - }, - "../packages/fetch": { - "extraneous": true - }, "node_modules/@75lb/deep-merge": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@75lb/deep-merge/-/deep-merge-1.1.2.tgz", @@ -1272,12 +1266,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/@gar/promisify": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", - "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", - "optional": true - }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", @@ -3522,10 +3510,14 @@ } }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "optional": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", + "license": "ISC", + "optional": true, + "engines": { + "node": "^20.17.0 || >=22.9.0" + } }, "node_modules/abort-controller": { "version": "3.0.0", @@ -3580,18 +3572,6 @@ "node": ">= 14" } }, - "node_modules/agentkeepalive": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.5.0.tgz", - "integrity": "sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==", - "optional": true, - "dependencies": { - "humanize-ms": "^1.2.1" - }, - "engines": { - "node": ">= 8.0.0" - } - }, "node_modules/aggregate-error": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-4.0.1.tgz", @@ -3721,40 +3701,6 @@ "integrity": "sha512-cumHmIAf6On83X7yP+LrsEyUOf/YlociZelmpRYaGFydoaPdxdt80MAbu6vWerQT2COCp2nPvHdsbD7tHn/YlQ==", "peer": true }, - "node_modules/aproba": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", - "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", - "optional": true - }, - "node_modules/are-we-there-yet": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", - "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", - "deprecated": "This package is no longer supported.", - "optional": true, - "dependencies": { - "delegates": "^1.0.0", - "readable-stream": "^3.6.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/are-we-there-yet/node_modules/readable-stream": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", - "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", - "optional": true, - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/arg": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", @@ -4003,7 +3949,7 @@ "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "devOptional": true, + "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4478,15 +4424,6 @@ "node": ">= 6" } }, - "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "optional": true, - "engines": { - "node": ">=10" - } - }, "node_modules/ci-info": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", @@ -4851,15 +4788,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" }, - "node_modules/color-support": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", - "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", - "optional": true, - "bin": { - "color-support": "bin.js" - } - }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -4951,7 +4879,7 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "devOptional": true + "dev": true }, "node_modules/concurrently": { "version": "9.2.1", @@ -5004,12 +4932,6 @@ "node": "^14.18.0 || >=16.10.0" } }, - "node_modules/console-control-strings": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", - "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", - "optional": true - }, "node_modules/console.table": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/console.table/-/console.table-0.10.0.tgz", @@ -5357,12 +5279,6 @@ "node": ">=0.4.0" } }, - "node_modules/delegates": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "optional": true - }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -6757,23 +6673,11 @@ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" }, - "node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "devOptional": true + "dev": true }, "node_modules/fsevents": { "version": "2.3.3", @@ -6796,47 +6700,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gauge": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", - "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", - "deprecated": "This package is no longer supported.", - "optional": true, - "dependencies": { - "aproba": "^1.0.3 || ^2.0.0", - "color-support": "^1.1.3", - "console-control-strings": "^1.1.0", - "has-unicode": "^2.0.1", - "signal-exit": "^3.0.7", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1", - "wide-align": "^1.1.5" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, - "node_modules/gauge/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "optional": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/gauge/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "optional": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/generate-function": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz", @@ -7161,12 +7024,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-unicode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", - "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", - "optional": true - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -7399,15 +7256,6 @@ "node": ">=10.17.0" } }, - "node_modules/humanize-ms": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", - "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", - "optional": true, - "dependencies": { - "ms": "^2.0.0" - } - }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -7496,18 +7344,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/infer-owner": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", - "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", - "optional": true - }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", - "devOptional": true, + "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -7577,18 +7419,6 @@ } } }, - "node_modules/inquirer/node_modules/@types/node": { - "version": "25.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", - "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, "node_modules/inquirer/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -7612,15 +7442,6 @@ "node": ">=8" } }, - "node_modules/inquirer/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "dev": true, - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/ip-address": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", @@ -7784,12 +7605,6 @@ "node": ">=8" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "optional": true - }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -9214,7 +9029,7 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "devOptional": true, + "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -9241,18 +9056,6 @@ "node": ">=8" } }, - "node_modules/minipass-collect": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", - "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/minipass-fetch": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-4.0.1.tgz", @@ -9326,24 +9129,11 @@ "resolved": "https://registry.npmjs.org/minisearch/-/minisearch-7.1.0.tgz", "integrity": "sha512-tv7c/uefWdEhcu6hvrfTihflgeEi2tN6VV7HJnCjK6VxM75QQJh4t9FwJCsA2EsRS8LCnu3W87CuGPWMocOLCA==" }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "optional": true, - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "devOptional": true, + "dev": true, "bin": { "mkdirp": "bin/cmd.js" }, @@ -9634,9 +9424,10 @@ } }, "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" }, "node_modules/native-duplexpair": { "version": "1.0.0", @@ -9754,280 +9545,80 @@ } }, "node_modules/node-gyp": { - "version": "8.4.1", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-8.4.1.tgz", - "integrity": "sha512-olTJRgUtAb/hOXG0E93wZDs5YiJlgbXxTwQAFHyNlRsXQnYzUaF2aGgujZbw+hR8aF4ZG/rST57bWMWD16jr9w==", + "version": "12.4.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.4.0.tgz", + "integrity": "sha512-OMcPNvqTCFUnNaBlmdgq+lfNqY7gTiSmNRDjY3uAXRyudeKZEZxu3CLtjMQrx4zZxCX2b/mpNqTtwuCJgXhHkw==", + "license": "MIT", "optional": true, "dependencies": { "env-paths": "^2.2.0", - "glob": "^7.1.4", + "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^9.1.0", - "nopt": "^5.0.0", - "npmlog": "^6.0.0", - "rimraf": "^3.0.2", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^2.0.2" + "tar": "^7.5.4", + "tinyglobby": "^0.2.12", + "undici": "^6.25.0", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": ">= 10.12.0" - } - }, - "node_modules/node-gyp/node_modules/@npmcli/fs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-1.1.1.tgz", - "integrity": "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ==", - "optional": true, - "dependencies": { - "@gar/promisify": "^1.0.1", - "semver": "^7.3.5" - } - }, - "node_modules/node-gyp/node_modules/@npmcli/move-file": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-1.1.2.tgz", - "integrity": "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg==", - "deprecated": "This functionality has been moved to @npmcli/fs", - "optional": true, - "dependencies": { - "mkdirp": "^1.0.4", - "rimraf": "^3.0.2" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/@tootallnate/once": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", - "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "optional": true, - "engines": { - "node": ">= 6" - } - }, - "node_modules/node-gyp/node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", - "optional": true, - "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/cacache": { - "version": "15.3.0", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-15.3.0.tgz", - "integrity": "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==", - "optional": true, - "dependencies": { - "@npmcli/fs": "^1.0.0", - "@npmcli/move-file": "^1.0.1", - "chownr": "^2.0.0", - "fs-minipass": "^2.0.0", - "glob": "^7.1.4", - "infer-owner": "^1.0.4", - "lru-cache": "^6.0.0", - "minipass": "^3.1.1", - "minipass-collect": "^1.0.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.2", - "mkdirp": "^1.0.3", - "p-map": "^4.0.0", - "promise-inflight": "^1.0.1", - "rimraf": "^3.0.2", - "ssri": "^8.0.1", - "tar": "^6.0.2", - "unique-filename": "^1.1.1" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/node-gyp/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "optional": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/node-gyp/node_modules/http-proxy-agent": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", - "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", - "optional": true, - "dependencies": { - "@tootallnate/once": "1", - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/node-gyp/node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", - "optional": true, - "dependencies": { - "agent-base": "6", - "debug": "4" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/node-gyp/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "optional": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/node-gyp/node_modules/make-fetch-happen": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-9.1.0.tgz", - "integrity": "sha512-+zopwDy7DNknmwPQplem5lAZX/eCOzSvSNNcSKm5eVwTkOBzoktEfXsa9L23J/GIRhxRsaxzkPEhrJEpE2F4Gg==", - "optional": true, - "dependencies": { - "agentkeepalive": "^4.1.3", - "cacache": "^15.2.0", - "http-cache-semantics": "^4.1.0", - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "is-lambda": "^1.0.1", - "lru-cache": "^6.0.0", - "minipass": "^3.1.3", - "minipass-collect": "^1.0.2", - "minipass-fetch": "^1.3.2", - "minipass-flush": "^1.0.5", - "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.2", - "promise-retry": "^2.0.1", - "socks-proxy-agent": "^6.0.0", - "ssri": "^8.0.0" - }, - "engines": { - "node": ">= 10" - } - }, - "node_modules/node-gyp/node_modules/minipass-fetch": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-1.4.1.tgz", - "integrity": "sha512-CGH1eblLq26Y15+Azk7ey4xh0J/XfJfrCox5LDJiKqI2Q2iwOLOKrlmIaODiSQS8d18jalF6y2K2ePUm0CmShw==", + "node_modules/node-gyp/node_modules/isexe": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", + "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", + "license": "BlueOak-1.0.0", "optional": true, - "dependencies": { - "minipass": "^3.1.0", - "minipass-sized": "^1.0.3", - "minizlib": "^2.0.0" - }, "engines": { - "node": ">=8" - }, - "optionalDependencies": { - "encoding": "^0.1.12" + "node": ">=20" } }, "node_modules/node-gyp/node_modules/nopt": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", - "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", + "license": "ISC", "optional": true, "dependencies": { - "abbrev": "1" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": ">=6" - } - }, - "node_modules/node-gyp/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", - "optional": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/socks-proxy-agent": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-6.2.1.tgz", - "integrity": "sha512-a6KW9G+6B3nWZ1yB8G7pJwL3ggLy1uTzKAgCb7ttblwqdz9fMGJUuTy3uFzEP48FAs9FLILlmzDlE2JJhVQaXQ==", + "node_modules/node-gyp/node_modules/proc-log": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.1.0.tgz", + "integrity": "sha512-iG+GYldRf2BQ0UDUAd6JQ/RwzaQy6mXmsk/IzlYyal4A4SNFw54MeH4/tLkF4I5WoWG9SQwuqWzS99jaFQHBuQ==", + "license": "ISC", "optional": true, - "dependencies": { - "agent-base": "^6.0.2", - "debug": "^4.3.3", - "socks": "^2.6.2" - }, "engines": { - "node": ">= 10" + "node": "^20.17.0 || >=22.9.0" } }, - "node_modules/node-gyp/node_modules/ssri": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz", - "integrity": "sha512-97qShzy1AiyxvPNIkLWoGua7xoQzzPjQ0HAH4B0rWKo7SZ6USuPcrUiAFrws0UH8RrbWmgq3LMTObhPIHbbBeQ==", + "node_modules/node-gyp/node_modules/which": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", + "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", + "license": "ISC", "optional": true, "dependencies": { - "minipass": "^3.1.1" + "isexe": "^4.0.0" + }, + "bin": { + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/node-gyp/node_modules/unique-filename": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz", - "integrity": "sha512-Vmp0jIp2ln35UTXuryvjzkjGdRyf9b2lTXuSYUiPmzRcl3FDtYqAwOnTJkAngD9SWhnoJzDbTKwaOrZ+STtxNQ==", - "optional": true, - "dependencies": { - "unique-slug": "^2.0.0" - } - }, - "node_modules/node-gyp/node_modules/unique-slug": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-2.0.2.tgz", - "integrity": "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w==", - "optional": true, - "dependencies": { - "imurmurhash": "^0.1.4" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-int64": { @@ -10093,22 +9684,6 @@ "node": ">=8" } }, - "node_modules/npmlog": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", - "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", - "deprecated": "This package is no longer supported.", - "optional": true, - "dependencies": { - "are-we-there-yet": "^3.0.0", - "console-control-strings": "^1.1.0", - "gauge": "^4.0.3", - "set-blocking": "^2.0.0" - }, - "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" - } - }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -10326,52 +9901,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", - "optional": true, - "dependencies": { - "aggregate-error": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-map/node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "optional": true, - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/p-map/node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", - "optional": true, - "engines": { - "node": ">=6" - } - }, - "node_modules/p-map/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "optional": true, - "engines": { - "node": ">=8" - } - }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", @@ -10515,7 +10044,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", - "devOptional": true, + "dev": true, "engines": { "node": ">=0.10.0" } @@ -10908,16 +10437,18 @@ } }, "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", - "napi-build-utils": "^1.0.1", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", @@ -11000,12 +10531,6 @@ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "node_modules/promise-inflight": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", - "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", - "optional": true - }, "node_modules/promise-retry": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", @@ -11859,12 +11384,6 @@ "node": ">= 0.8.0" } }, - "node_modules/set-blocking": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", - "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", - "optional": true - }, "node_modules/set-function-length": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", @@ -12117,21 +11636,25 @@ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==" }, "node_modules/sqlite3": { - "version": "5.1.7", - "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-5.1.7.tgz", - "integrity": "sha512-GGIyOiFaG+TUra3JIfkI/zGP8yZYLPQ0pl1bH+ODjiX57sPhrLU5sQJn1y9bDKZUFYkX1crlrPfSYt0BKKdkog==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/sqlite3/-/sqlite3-6.0.1.tgz", + "integrity": "sha512-X0czUUMG2tmSqJpEQa3tCuZSHKIx8PwM53vLZzKp/o6Rpy25fiVfjdbnZ988M8+O3ZWR1ih0K255VumCb3MAnQ==", "hasInstallScript": true, + "license": "BSD-3-Clause", "dependencies": { "bindings": "^1.5.0", - "node-addon-api": "^7.0.0", - "prebuild-install": "^7.1.1", - "tar": "^6.1.11" + "node-addon-api": "^8.0.0", + "prebuild-install": "^7.1.3", + "tar": "^7.5.10" + }, + "engines": { + "node": ">=20.17.0" }, "optionalDependencies": { - "node-gyp": "8.x" + "node-gyp": "12.x" }, "peerDependencies": { - "node-gyp": "8.x" + "node-gyp": "12.x" }, "peerDependenciesMeta": { "node-gyp": { @@ -12140,9 +11663,13 @@ } }, "node_modules/sqlite3/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==" + "version": "8.9.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.9.1.tgz", + "integrity": "sha512-4eUQWVPCUUUiBjLnHS3cXWeC6ryoPUc0U3rP7IuzapoGbzMqd/r6KKO0clr0b+snQhsrueFEhCZDdK+LK7hxKg==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" + } }, "node_modules/sqlstring": { "version": "2.3.3", @@ -13318,9 +12845,10 @@ "dev": true }, "node_modules/undici": { - "version": "6.24.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-6.24.0.tgz", - "integrity": "sha512-lVLNosgqo5EkGqh5XUDhGfsMSoO8K0BAN0TyJLvwNRSl4xWGZlCVYsAIpa/OpA3TvmnM01GWcoKmc3ZWo5wKKA==", + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-6.28.0.tgz", + "integrity": "sha512-LIY910g9TI13YS95lrMFrs8Rm/u/irgHeTWoKCoteeJ04CUJ92eEfj0rVn+7VKMPBpUPiUoBKfhNyLI23EE/KA==", + "license": "MIT", "engines": { "node": ">=18.17" } @@ -13770,17 +13298,6 @@ "url": "https://opencollective.com/vitest" } }, - "node_modules/vite-node/node_modules/@types/node": { - "version": "25.9.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-25.9.2.tgz", - "integrity": "sha512-G05zqtJhcDLb8uslf5EjCxXg9G1KQxiV8OS0R26IC//Eoyitzqe8z37I7cqvnZlrlSfgocQRfSn/AHBZJJFyGw==", - "license": "MIT", - "optional": true, - "peer": true, - "dependencies": { - "undici-types": ">=7.24.0 <7.24.7" - } - }, "node_modules/vite-node/node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", @@ -13812,14 +13329,6 @@ } } }, - "node_modules/vite-node/node_modules/undici-types": { - "version": "7.24.6", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", - "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", - "license": "MIT", - "optional": true, - "peer": true - }, "node_modules/vite-node/node_modules/vite": { "version": "6.3.5", "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", @@ -14920,15 +14429,6 @@ "node": ">=8" } }, - "node_modules/wide-align": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", - "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", - "optional": true, - "dependencies": { - "string-width": "^1.0.2 || 2 || 3 || 4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/extensions/vscode/package.json b/extensions/vscode/package.json index 0030d3ea59c..9932a49ffaa 100644 --- a/extensions/vscode/package.json +++ b/extensions/vscode/package.json @@ -781,6 +781,8 @@ "esbuild": "^0.25.0" }, "tar": "^7.5.13", - "undici-types@~7.18.0": "7.18.1" + "undici-types@~7.18.0": "7.18.1", + "sharp": "^0.35.3", + "sqlite3": "^6.0.1" } } diff --git a/gui/package-lock.json b/gui/package-lock.json index 06763b6bd75..4ca3b254da6 100644 --- a/gui/package-lock.json +++ b/gui/package-lock.json @@ -170,7 +170,7 @@ "shell-quote": "^1.8.3", "socket.io-client": "^4.7.3", "sqlite": "^5.1.1", - "sqlite3": "^5.1.7", + "sqlite3": "^6.0.1", "system-ca": "^1.0.3", "tar": "^7.5.13", "tree-sitter-wasms": "^0.1.11", diff --git a/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx b/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx index ed00060a4ad..5fe73a150a1 100644 --- a/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx +++ b/gui/src/pages/gui/ToolCallDiv/FunctionSpecificToolCallDiv.tsx @@ -75,6 +75,16 @@ function FunctionSpecificToolCallDiv({ /> ); default: + // Tools without a bespoke display (grep_search, shadow_* tools, MCP + // tools, etc.) still show their arguments so it's clear what was + // actually sent, rather than only ever showing the output. + if (args && Object.keys(args).length > 0) { + return ( +
+ {JSON.stringify(args)} +
+ ); + } return null; } } diff --git a/gui/src/redux/slices/sessionSlice.ts b/gui/src/redux/slices/sessionSlice.ts index 8784d0c41dc..61b6845749f 100644 --- a/gui/src/redux/slices/sessionSlice.ts +++ b/gui/src/redux/slices/sessionSlice.ts @@ -603,6 +603,27 @@ export const sessionSlice = createSlice({ lastMessage = lastItem.message; } + // A tool result streamed inline (not via the client's own tool + // execution, e.g. resolved server-side) means the call is already + // complete — mark the matching tool call state done directly rather + // than leaving it stuck "generating" with nothing to resolve it. + if (message.role === "tool" && message.toolCallId) { + const preResolvedState = findToolCallById( + state.history, + message.toolCallId, + ); + if (preResolvedState && preResolvedState.status !== "done") { + preResolvedState.status = "done"; + preResolvedState.output = [ + { + name: preResolvedState.toolCall.function.name, + description: "Tool output", + content: messageContent, + }, + ]; + } + } + // Add to the existing message if (messageContent) { if (messageContent.includes("") && message.role !== "tool") { diff --git a/gui/src/redux/thunks/callToolById.ts b/gui/src/redux/thunks/callToolById.ts index 3ce76b02466..63ff0578810 100644 --- a/gui/src/redux/thunks/callToolById.ts +++ b/gui/src/redux/thunks/callToolById.ts @@ -78,6 +78,7 @@ export const callToolById = createAsyncThunk< // Tool is called on core side const result = await extra.ideMessenger.request("tools/call", { toolCall: toolCallState.toolCall, + sessionId: state.session.id, }); if (result.status === "error") { throw new Error(result.error); diff --git a/gui/src/redux/thunks/streamNormalInput.ts b/gui/src/redux/thunks/streamNormalInput.ts index 23429852d7a..7683d7974a4 100644 --- a/gui/src/redux/thunks/streamNormalInput.ts +++ b/gui/src/redux/thunks/streamNormalInput.ts @@ -203,6 +203,7 @@ export const streamNormalInput = createAsyncThunk< messages: compiledChatMessages, legacySlashCommandData, messageOptions: { precompiled: true }, + sessionId: state.session.id, }, streamAborter.signal, ); From 545ca35ddb124ec180e3e7a83c41bb187c96159c Mon Sep 17 00:00:00 2001 From: deepak-s-2000 Date: Sun, 16 Aug 2026 00:07:24 +0530 Subject: [PATCH 3/4] feat: add Claude Code CLI as a subscription-based chat provider Adds a "claudecode" LLM provider that spawns `claude -p` per turn instead of calling a metered API, so chat can run through a Claude subscription instead of an API key. Continue's own harness stays authoritative: - core/mcp/shadowCodeToolsServer.ts hosts Continue's own tools as an in-process MCP server (stateful Streamable HTTP - stateless mode is broken on Windows in the installed SDK) instead of letting Claude Code use its own Read/Write/Edit/Bash. - core/llm/llms/ClaudeCodeCli.ts is the provider: disables Claude Code's built-in tools, overrides its system prompt, and yields paired toolCall/result chunks so already-resolved MCP tool calls render as history rather than pending work. - A new claudeCodeCli/authorizeToolCall Core->GUI request (core/protocol/webview.ts, gui/src/hooks/ClaudeCodeCliApprovalGate.tsx) routes every tool call through the same policy/approval flow normal tool calls use. - Cross-turn memory reuses the existing Ultra Token Saving path (ShadowChatDb + shadow_* tools) instead of CLI-native session continuity, forced on for this provider in core/llm/streamChat.ts and core/llm/tokenOptimizedChat.ts since it isn't optional here. - core/tools/implementations/serverSideEdit.ts makes single_find_and_replace/multi_edit work when called via MCP (previously client-only), reusing the same deterministic preprocessArgs computation the GUI's client tools already depend on. Also adds CLAUDE.md documenting the product intent and architecture for future sessions. Co-Authored-By: Claude Sonnet 5 --- CLAUDE.md | 108 +++++++ core/core.ts | 17 ++ core/index.d.ts | 8 + core/llm/llms/ClaudeCodeCli.ts | 277 ++++++++++++++++++ core/llm/llms/index.ts | 2 + core/llm/streamChat.ts | 12 +- core/llm/tokenOptimizedChat.ts | 21 ++ core/mcp/shadowCodeToolsServer.ts | 282 +++++++++++++++++++ core/protocol/webview.ts | 16 ++ core/tools/callTool.ts | 15 + core/tools/implementations/serverSideEdit.ts | 76 +++++ gui/src/App.tsx | 2 + gui/src/hooks/ClaudeCodeCliApprovalGate.tsx | 126 +++++++++ gui/src/redux/thunks/evaluateToolPolicies.ts | 2 +- 14 files changed, 962 insertions(+), 2 deletions(-) create mode 100644 CLAUDE.md create mode 100644 core/llm/llms/ClaudeCodeCli.ts create mode 100644 core/mcp/shadowCodeToolsServer.ts create mode 100644 core/tools/implementations/serverSideEdit.ts create mode 100644 gui/src/hooks/ClaudeCodeCliApprovalGate.tsx diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000000..defa28fddc2 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,108 @@ +# CLAUDE.md + +This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. + +## What this project is + +shadow-code is a fork of [Continue](https://github.com/continuedev/continue) (the open-source AI coding agent - VS Code extension, JetBrains plugin, and CLI; upstream is now read-only/unmaintained, so this fork is the active codebase going forward). + +The fork exists to add one capability Continue doesn't have: **using coding-agent CLIs the user already has a subscription for (Claude Code CLI first, later Codex CLI / GitHub Copilot CLI) as the model-execution backend, instead of paying per-token for an API key.** The design constraint driving all of this is that **Continue's own harness stays authoritative** - its own system prompt, its own tool definitions, its own permission/approval flow. The CLI is used purely for authenticated model execution, never for its own built-in agent loop (its Read/Write/Edit/Bash tools are explicitly disabled on every invocation). + +### The Claude Code CLI integration + +- `core/llm/llms/ClaudeCodeCli.ts` - an `ILLM` provider (`providerName: "claudecode"`) that implements `_streamChat` by spawning `claude -p --output-format stream-json` per turn instead of making an HTTP call. Key decisions, don't relitigate without reason: + - `--tools ""` disables every Claude Code built-in; `--system-prompt` (full override, not `--append-system-prompt`) makes Continue's system prompt authoritative; `--permission-mode bypassPermissions` because there's no terminal to prompt against in `-p` mode (our own MCP server is the real approval gate, not Claude Code's). + - Deliberately **not** using `--bare` - it forces API-key-only auth (no OAuth/keychain), which would defeat the entire point of running through a Pro/Max/Team subscription. + - No `--session-id`/`--resume` continuity. Each call is a fresh Claude Code session; multi-turn memory instead reuses the existing **Ultra Token Saving** mode (`core/llm/tokenOptimizedChat.ts`, `core/data/shadowChatDb.ts`) that every provider already has: only `[systemMessage, currentUserMessage]` is sent, and the model pulls earlier turns back on demand via the `shadow_*` tools (`shadow_get_chat_history`, `shadow_search_messages`, etc.). For this provider, Ultra Token Saving is forced on unconditionally in `core/llm/streamChat.ts` regardless of the user's setting, because it isn't optional here - without it there is no cross-turn memory at all. + - Tool calls Claude Code makes are already fully resolved (via MCP) by the time they appear in the output stream, so the provider yields them as _paired_ `{role: "assistant", toolCalls}` → `{role: "tool", toolCallId, content}` chunks - this is what tells Continue's UI/state machine "already-resolved history," not "pending work," matching how `tokenOptimizedStreamChat` already renders shadow tool calls. `tokenOptimizedChat.ts` has an explicit `providerName === "claudecode"` bypass so its own interception loop doesn't try to re-execute (and double-run) anything. +- `core/mcp/shadowCodeToolsServer.ts` - `ShadowCodeToolsMcpServer`, an MCP server exposing Continue's own tools (not Claude Code's) to the spawned CLI. Runs **in-process inside Core** as a Streamable HTTP server on `127.0.0.1` (not stdio, not a separate binary) so tool execution has direct access to the same config/execution path `handleToolCall` already uses. **Must use stateful mode** (`sessionIdGenerator: () => randomUUID()`) - stateless mode is broken on Windows in the installed `@modelcontextprotocol/sdk` version (confirmed by direct repro: every request after the first silently 500s). The tool set exposed is session-scoped (`registerToolsForSession`/`resolveTools`), matching whatever `options.tools` was active for that specific turn rather than a static reload of `config.tools`. +- Approval bridge: `claudeCodeCli/authorizeToolCall` (`core/protocol/webview.ts`) is a Core → GUI request/response message. The MCP server's `CallTool` handler blocks on it before executing anything; `gui/src/hooks/ClaudeCodeCliApprovalGate.tsx` answers it by reusing the real `evaluateToolPolicy` (exported from `gui/src/redux/thunks/evaluateToolPolicies.ts`) against the user's actual tool settings, only showing an interactive Allow/Deny banner when the resolved policy is `allowedWithPermission`. +- `core/llm/tokenOptimizedChat.ts` and `core/llm/streamChat.ts` thread a `shadowSessionId` field through `CompletionOptions` (`core/index.d.ts`) so `ClaudeCodeCli` uses the _same_ ShadowChatDb session id the rest of the pipeline recorded history under (Continue's real GUI session id when available), not an independently-derived one - they'd otherwise disagree whenever a real session id exists, silently breaking `shadow_get_chat_history`. +- `core/tools/implementations/serverSideEdit.ts` - `single_find_and_replace` and `multi_edit` now work when called via MCP (they're normally client-only, `CLIENT_TOOLS_IMPLS` in `core/tools/builtIn.ts`, so the GUI can stream a live diff preview). This reuses the _same_ deterministic `tool.preprocessArgs` computation the GUI's client-side implementations already depend on (`core/edit/searchAndReplace/*`) and just writes the file directly, skipping the live-preview UX. `edit_existing_file` (the freeform "changes" sketch format) has **no** server-side implementation - there's no deterministic reconciliation for it anywhere in this codebase, only the GUI's interactive apply flow - so it returns a message redirecting the model to `single_find_and_replace`/`multi_edit` instead of silently failing or risking a bad patch. + +### Known open items + +- `shadow-mcp` (a separate Go MCP gateway/aggregator binary, `github.com/deepak-s-2000/shadow-mcp`) was deliberately deferred, not rejected - its value (multi-server aggregation, lazy tool-catalog loading, profile-based filtering) only pays for the packaging cost (per-platform binary, code signing) once there's more than one downstream MCP server to front. Revisit if/when Codex CLI / Copilot CLI integrations need it. +- Built and verified with real `claude` CLI invocations (flag behavior, MCP handshake, event shapes) and `tsc --noEmit`, but never inside a running VS Code extension host - GUI-side rendering (approval banner, tool-call cards) is unconfirmed visually. +- Extending to Codex CLI / Copilot CLI: the MCP-exposure and approval-bridge pieces (`shadowCodeToolsServer.ts`, `claudeCodeCli/authorizeToolCall`) are provider-agnostic by construction; only a new `ILLM` subclass analogous to `ClaudeCodeCli.ts` should be needed, with per-vendor flag/transport differences contained there. + +## Commands + +This is an npm workspaces monorepo; most packages are developed independently from their own directory. + +```bash +# Root: format all packages, or type-check-watch everything at once +npm run format # prettier --write +npm run format:check +npm run tsc:watch # watches gui + vscode + core + binary concurrently +``` + +**core/** (the shared engine - config, LLM providers, tools, indexing): + +```bash +cd core +npm run tsc:check # type-check +npm run lint # eslint . --ext ts +npm test # Jest - *.test.ts files +npm run vitest # Vitest - *.vitest.ts files (preferred for new tests) +npm test -- path/to/file.test.ts # single Jest test file +npx vitest run path/to/file.vitest.ts # single Vitest test file +npm run build # tsc -p ./tsconfig.npm.json +``` + +**gui/** (React webview): + +```bash +cd gui +npm run dev # vite dev server +npm run tsc:check +npm run lint +npm test # vitest run +npx vitest run path/to/File.test.tsx # single test file +npm run build # tsc && vite build +``` + +**extensions/vscode/** (the VS Code extension host): + +```bash +cd extensions/vscode +npm run esbuild-watch # rebuild on change +npm run tsc:check +npm run lint +npm run package # produce a .vsix +``` + +**binary/** (packages core as a standalone executable for JetBrains/other IDEs): + +```bash +cd binary +npm test +npm run build # node build.js +``` + +## Architecture + +Standard Continue layout - four packages talk to each other over a typed message-passing protocol, not direct imports: + +- **`core/`** - IDE-agnostic engine: LLM provider implementations (`core/llm/llms/`), the tool system (`core/tools/`), config loading, indexing, MCP client code (`core/context/mcp/`). Runs as a long-lived child process (the "binary") separate from the extension host. +- **`gui/`** - the React webview UI, communicates with Core exclusively via `IIdeMessenger` (`gui/src/context/IdeMessenger.tsx`) and Redux (`gui/src/redux/`). +- **`extensions/vscode/`** - the VS Code extension host; relays messages between the webview and the Core process. +- **`extensions/cli`**, **`extensions/intellij`** - other front-ends onto the same Core. +- **`binary/`** - bundles `core/` into a platform-specific standalone executable (`pkg`) that non-VS Code front-ends spawn. + +**Message protocol** (`core/protocol/`): every cross-process message type is declared once and consumed on both ends by name - there is no per-message-type relay code to write when adding a new one, just the type declaration plus a sender (`messenger.request`/`.send`) and receiver (`.on` on the Core side, `useWebviewListener` on the GUI side). + +- `core/protocol/core.ts` → `ToCoreFromIdeOrWebviewProtocol`: GUI/IDE → Core requests (e.g. `tools/call`, `tools/evaluatePolicy`). +- `core/protocol/webview.ts` → `ToWebviewFromIdeOrCoreProtocol`: Core/IDE → GUI requests/pushes (e.g. `configUpdate`, `claudeCodeCli/authorizeToolCall`). A Core-initiated request that blocks on a GUI response (not just a one-way push) is a real, supported pattern here - see `claudeCodeCli/authorizeToolCall` for the shape. +- `core/protocol/index.ts` composes the directional types (`ToCoreProtocol`, `FromCoreProtocol`, `ToWebviewProtocol`, ...) from the per-pair files. + +**Tool execution & approval** (this is the part that requires reading several files together to understand): a tool call from the model normally becomes an `AssistantChatMessage.toolCalls` entry; the GUI (`gui/src/redux/thunks/evaluateToolPolicies.ts`) resolves a `ToolPolicy` (`disabled` / `allowedWithPermission` / `allowedWithoutPermission`) by combining the tool's `defaultToolPolicy`, the user's stored override, and any dynamic per-args policy (`tool.evaluateToolCallPolicy`, round-tripped through Core via `tools/evaluatePolicy`). Only if the policy is `allowedWithoutPermission` does the GUI immediately call `tools/call`; otherwise the call sits pending in Redux until the user clicks Allow in `PendingToolCallToolbar.tsx`, which is what finally sends `tools/call`. **There is no server-side blocking/await for approval in the normal flow** - Core's `handleToolCall` (`core/core.ts`) executes unconditionally whenever it receives `tools/call`; the GUI is what decides whether/when to send it. The Claude Code CLI integration had to build its own version of this gate (`claudeCodeCli/authorizeToolCall`) precisely because tool calls there originate from inside a spawned subprocess, not from the GUI's normal streaming loop. + +**Provider registration** (`core/llm/llms/index.ts`): providers are plain classes with a static `providerName`, collected into the `LLMClasses` array and matched by that string in `llmFromDescription`. Adding a provider is additive - `class Foo extends BaseLLM { static providerName = "..." }`, implement `_streamChat`, add to the array. + +## Conventions (from `.continue/rules/`) + +- Prefer functional programming; modifying existing classes or a singleton is fine when that's genuinely the right shape, but default to functions. +- Prefer `enum` over string-literal unions in TypeScript where reasonable. +- New tests: prefer Vitest (`*.vitest.ts` in core, `*.test.tsx` in gui) over Jest. Write tests as top-level `test()` functions, not inside `describe()` blocks; put the function name under test in the description. +- Don't add features beyond what was asked - solve the stated problem, then propose further work rather than doing it unprompted. diff --git a/core/core.ts b/core/core.ts index f8580cd1298..e8f323e13f4 100644 --- a/core/core.ts +++ b/core/core.ts @@ -14,6 +14,10 @@ import { DataLogger } from "./data/log"; import { CodebaseIndexer } from "./indexing/CodebaseIndexer"; import DocsService from "./indexing/docs/DocsService"; import { countTokens } from "./llm/countTokens"; +import { + ShadowCodeToolsMcpServer, + setShadowCodeToolsMcpServer, +} from "./mcp/shadowCodeToolsServer"; import Lemonade from "./llm/llms/Lemonade"; import { fetchModels } from "./llm/fetchModels"; import Ollama from "./llm/llms/Ollama"; @@ -94,6 +98,7 @@ export class Core { private docsService: DocsService; private globalContext = new GlobalContext(); llmLogger = new LLMLogger(); + shadowCodeToolsMcpServer: ShadowCodeToolsMcpServer; private messageAbortControllers = new Map(); private addMessageAbortController(id: string): AbortController { @@ -137,6 +142,18 @@ export class Core { const ideSettingsPromise = messenger.request("getIdeSettings", undefined); this.configHandler = new ConfigHandler(this.ide, this.llmLogger); + this.shadowCodeToolsMcpServer = new ShadowCodeToolsMcpServer({ + loadConfig: async () => (await this.configHandler.loadConfig()).config, + executeTool: (toolCall, sessionId) => + this.handleToolCall(toolCall, sessionId), + requestApproval: (params) => + this.messenger.request("claudeCodeCli/authorizeToolCall", params), + }); + setShadowCodeToolsMcpServer(this.shadowCodeToolsMcpServer); + void this.shadowCodeToolsMcpServer.ensureStarted().catch((e) => { + console.error("Failed to start shadow-code-tools MCP server", e); + }); + this.docsService = DocsService.createSingleton( this.configHandler, this.ide, diff --git a/core/index.d.ts b/core/index.d.ts index 3d82683b828..2f320b78b27 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -1216,6 +1216,14 @@ export interface BaseCompletionOptions { reasoning?: boolean; reasoningBudgetTokens?: number; promptCaching?: boolean; + /** + * The ShadowChatDb session id this call's turn is recorded/looked-up under + * (see core/llm/tokenOptimizedChat.ts and core/util/shadowChatSessionId.ts). + * Set by tokenOptimizedStreamChat so providers that need to correlate their + * own side-channel state with the same session (e.g. ClaudeCodeCli's MCP + * tool calls) use the real id rather than re-deriving a different one. + */ + shadowSessionId?: string; } export interface ModelCapability { diff --git a/core/llm/llms/ClaudeCodeCli.ts b/core/llm/llms/ClaudeCodeCli.ts new file mode 100644 index 00000000000..345c60ec818 --- /dev/null +++ b/core/llm/llms/ClaudeCodeCli.ts @@ -0,0 +1,277 @@ +import { spawn } from "node:child_process"; +import * as os from "node:os"; +import * as path from "node:path"; +import * as fs from "node:fs/promises"; + +import { + ChatMessage, + CompletionOptions, + ToolCallDelta, + Usage, +} from "../../index.js"; +import { getShadowCodeToolsMcpServer } from "../../mcp/shadowCodeToolsServer.js"; +import { renderChatMessage } from "../../util/messageContent.js"; +import { deriveSessionId } from "../../util/shadowChatSessionId.js"; +import { BaseLLM } from "../index.js"; + +const MCP_SERVER_NAME = "shadow-code"; +const MCP_TOOL_PREFIX = `mcp__${MCP_SERVER_NAME}__`; + +// Confirmed against a real `claude -p --output-format stream-json` run +// against a live MCP tool call (see conversation - not guessed). +interface StreamJsonEvent { + type: string; + subtype?: string; + message?: { + role?: string; + content?: Array< + | { type: "text"; text: string } + | { type: "thinking"; thinking: string } + | { type: "tool_use"; id: string; name: string; input: unknown } + | { + type: "tool_result"; + tool_use_id: string; + content?: Array<{ type: "text"; text: string }> | string; + } + >; + }; + usage?: { + input_tokens?: number; + output_tokens?: number; + cache_read_input_tokens?: number; + cache_creation_input_tokens?: number; + }; +} + +function toolResultText( + content: Array<{ type: "text"; text: string }> | string | undefined, +): string { + if (typeof content === "string") return content; + if (!content) return ""; + return content + .filter((c) => c.type === "text") + .map((c) => c.text) + .join(""); +} + +class ClaudeCodeCli extends BaseLLM { + static providerName = "claudecode"; + + // Reuses the same session identity the rest of the app already uses for + // ShadowChatDb (core/util/shadowChatSessionId.ts, core/llm/streamChat.ts). + // Continue's own real session id isn't available to LLM providers (every + // provider is a stateless completion API from Continue's perspective), so + // this is the correct id to key on regardless - it's what the shadow_* + // tools below already search against for any provider. + private async writeMcpConfig(sessionId: string): Promise { + const mcpServer = getShadowCodeToolsMcpServer(); + const url = await mcpServer.ensureStarted(); + const configPath = path.join( + os.tmpdir(), + `shadow-code-mcp-${sessionId}.json`, + ); + await fs.writeFile( + configPath, + JSON.stringify({ + mcpServers: { + [MCP_SERVER_NAME]: { + type: "http", + url: `${url}?continueSessionId=${encodeURIComponent(sessionId)}`, + }, + }, + }), + ); + return configPath; + } + + protected async *_streamChat( + messages: ChatMessage[], + signal: AbortSignal, + options: CompletionOptions, + ): AsyncGenerator { + const systemMessage = messages.find((m) => m.role === "system"); + const lastUserMessage = [...messages] + .reverse() + .find((m) => m.role === "user"); + if (!lastUserMessage) { + throw new Error("ClaudeCodeCli requires at least one user message"); + } + + // No --session-id/--resume: each call is a genuinely fresh Claude Code + // session. Continuity across turns isn't handled by Claude Code's own + // session state - it's handled the same way every other provider in + // this codebase handles it under Ultra Token Saving mode + // (core/llm/tokenOptimizedChat.ts): earlier turns live in ShadowChatDb, + // and the model pulls them back on demand via the shadow_* tools + // (shadow_get_chat_history, shadow_search_messages, etc.). + // + // Prefer options.shadowSessionId (the id tokenOptimizedStreamChat + // actually recorded this conversation's history under - Continue's real + // GUI session id when available, not a content hash) over deriving one + // ourselves. They'd disagree whenever a real session id exists, which is + // the normal case, and shadow_get_chat_history would silently query an + // empty session if we used the wrong one. + const sessionId = options.shadowSessionId ?? deriveSessionId(messages); + const mcpConfigPath = await this.writeMcpConfig(sessionId); + + // Expose exactly the tools active for this turn (options.tools already + // includes any client tool overrides plus, under Ultra Token Saving + // mode, the force-included shadow_* tools - see + // tokenOptimizedChat.ts:augmentedOptions), not a static reload of the + // full config. Registered/unregistered per call since this MCP server + // is a shared, long-lived singleton serving every Continue session. + const mcpServer = getShadowCodeToolsMcpServer(); + mcpServer.registerToolsForSession(sessionId, options.tools ?? []); + + const args = [ + "-p", + "--output-format", + "stream-json", + "--verbose", + "--mcp-config", + mcpConfigPath, + "--strict-mcp-config", + "--allowedTools", + `${MCP_TOOL_PREFIX}*`, + // Disable Claude Code's entire built-in tool set (Read/Write/Edit/ + // Bash/...) - every tool call must go through shadow-code-tools MCP. + "--tools", + "", + // Our own MCP server already gates every call through Continue's + // approval/policy flow (see ShadowCodeToolsMcpServer + the + // claudeCodeCli/authorizeToolCall round-trip to the GUI); Claude + // Code's own permission layer has no terminal to prompt against in + // -p mode and would otherwise just hang or auto-deny. + "--permission-mode", + "bypassPermissions", + ]; + if (systemMessage) { + // Full override, not append: Continue's own system prompt is the only + // one in effect. (--bare would additionally strip CLAUDE.md/hooks/ + // memory, but it also forces API-key-only auth, which defeats the + // whole point of running through a Pro/Max/Team subscription - so it + // is deliberately NOT used here.) + args.push("--system-prompt", renderChatMessage(systemMessage)); + } + if (options.model) { + args.push("--model", options.model); + } + + const child = spawn("claude", args, { + stdio: ["pipe", "pipe", "pipe"], + }); + + const onAbort = () => child.kill(); + signal.addEventListener("abort", onAbort); + + child.stdin.write(renderChatMessage(lastUserMessage)); + child.stdin.end(); + + let stderr = ""; + child.stderr.on("data", (chunk) => (stderr += chunk.toString())); + + let buffer = ""; + let usage: Usage | undefined; + + // Every tool call Claude Code makes (shadow_* or otherwise) is already + // fully resolved through shadow-code-tools MCP by the time it appears in + // this stream - Continue's real execution + approval path already ran. + // Pairing the assistant tool_use message with its tool_result + // immediately after (mirroring tokenOptimizedStreamChat's own pattern + // for shadow tool calls) is what tells Continue's UI/state machine this + // is already-resolved history rather than pending work to execute - + // critical for parity with the normal API-key path and to avoid + // double-execution (see the "claudecode" bypass in tokenOptimizedChat.ts + // and the forced-ultra-mode routing in streamChat.ts). + try { + for await (const chunk of child.stdout) { + buffer += chunk.toString(); + let newlineIndex: number; + while ((newlineIndex = buffer.indexOf("\n")) !== -1) { + const line = buffer.slice(0, newlineIndex).trim(); + buffer = buffer.slice(newlineIndex + 1); + if (!line) continue; + + let event: StreamJsonEvent; + try { + event = JSON.parse(line); + } catch { + continue; // Not every line is guaranteed to be JSON we care about + } + + if (event.type === "assistant" && event.message?.content) { + let text = ""; + const toolCalls: ToolCallDelta[] = []; + for (const block of event.message.content) { + if (block.type === "text") { + text += block.text; + } else if (block.type === "tool_use") { + toolCalls.push({ + id: block.id, + type: "function", + function: { + name: block.name.startsWith(MCP_TOOL_PREFIX) + ? block.name.slice(MCP_TOOL_PREFIX.length) + : block.name, + arguments: JSON.stringify(block.input ?? {}), + }, + }); + } + } + if (text || toolCalls.length > 0) { + yield { + role: "assistant", + content: text, + ...(toolCalls.length > 0 ? { toolCalls } : {}), + }; + } + continue; + } + + if (event.type === "user" && event.message?.content) { + for (const block of event.message.content) { + if (block.type === "tool_result") { + yield { + role: "tool", + toolCallId: block.tool_use_id, + content: toolResultText(block.content), + }; + } + } + continue; + } + + if (event.type === "result" && event.usage) { + usage = { + promptTokens: event.usage.input_tokens ?? 0, + completionTokens: event.usage.output_tokens ?? 0, + promptTokensDetails: { + cachedTokens: event.usage.cache_read_input_tokens, + cacheWriteTokens: event.usage.cache_creation_input_tokens, + }, + }; + } + } + } + } finally { + signal.removeEventListener("abort", onAbort); + mcpServer.unregisterSession(sessionId); + await fs.unlink(mcpConfigPath).catch(() => {}); + } + + const exitCode: number = await new Promise((resolve) => + child.once("close", resolve), + ); + if (exitCode !== 0 && !signal.aborted) { + throw new Error( + `claude CLI exited with code ${exitCode}${stderr ? `: ${stderr}` : ""}`, + ); + } + + if (usage) { + yield { role: "assistant", content: "", usage }; + } + } +} + +export default ClaudeCodeCli; diff --git a/core/llm/llms/index.ts b/core/llm/llms/index.ts index 4978f0617f2..cef740d5194 100644 --- a/core/llm/llms/index.ts +++ b/core/llm/llms/index.ts @@ -15,6 +15,7 @@ import Azure from "./Azure"; import Bedrock from "./Bedrock"; import BedrockImport from "./BedrockImport"; import Cerebras from "./Cerebras"; +import ClaudeCodeCli from "./ClaudeCodeCli"; import Cloudflare from "./Cloudflare"; import Cohere from "./Cohere"; import CometAPI from "./CometAPI"; @@ -72,6 +73,7 @@ import xAI from "./xAI"; import zAI from "./zAI"; export const LLMClasses = [ Anthropic, + ClaudeCodeCli, Cohere, CometAPI, FunctionNetwork, diff --git a/core/llm/streamChat.ts b/core/llm/streamChat.ts index 96b0cb690ac..10b654cf32b 100644 --- a/core/llm/streamChat.ts +++ b/core/llm/streamChat.ts @@ -116,7 +116,17 @@ export async function* llmStreamChat( return next.value; } else { - const ultraModeEnabled = config.ui?.ultraTokenSaving ?? false; + // Claude Code CLI (core/llm/llms/ClaudeCodeCli.ts) only ever sees the + // latest user message per call - it has no --resume-based continuity + // of its own, by design (see ClaudeCodeCli.ts's comments). It depends + // structurally on tokenOptimizedStreamChat's ShadowChatDb bookkeeping + // and forced shadow_* tool exposure to have any memory of earlier + // turns at all, not just as an optional token-saving optimization - + // so it always takes this path regardless of the user's Ultra Token + // Saving setting. + const ultraModeEnabled = + model.providerName === "claudecode" || + (config.ui?.ultraTokenSaving ?? false); const historyLimit = 20; // Prefer the GUI's real per-conversation ID; fall back to a // content-derived one for callers that don't supply it yet. diff --git a/core/llm/tokenOptimizedChat.ts b/core/llm/tokenOptimizedChat.ts index 0a17f51608b..76f62d2a045 100644 --- a/core/llm/tokenOptimizedChat.ts +++ b/core/llm/tokenOptimizedChat.ts @@ -205,6 +205,7 @@ export async function* tokenOptimizedStreamChat( const augmentedOptions: LLMFullCompletionOptions = { ...options, tools: [...clientTools, ...missingShadowTools], + shadowSessionId: sessionId, }; let loopMessages: ChatMessage[] = [ @@ -245,6 +246,26 @@ export async function* tokenOptimizedStreamChat( totalActualTokensIn += promptTokens; totalActualTokensOut += completionTokens; + // Claude Code CLI (core/llm/llms/ClaudeCodeCli.ts) resolves its entire + // tool-call loop internally, inside the single `claude -p` subprocess, + // via shadow-code-tools MCP - including shadow_* history lookups. Any + // toolCalls it yields here (paired with their results, for UI parity + // with the normal provider path) are already-resolved history, not + // pending work. Running the interception logic below would either + // re-execute already-resolved shadow_* calls a second time, or spawn a + // needless second `claude -p` process for calls that already have a + // final answer - so always treat a single iteration as done. + if (model.providerName === "claudecode") { + for (const chunk of chunks) { + yield chunk; + } + finalPromptLog = { + ...finalPromptLog, + completion: buildTextContent(chunks), + }; + break; + } + const toolCalls = extractCompletedToolCalls(chunks); if (toolCalls.length === 0) { diff --git a/core/mcp/shadowCodeToolsServer.ts b/core/mcp/shadowCodeToolsServer.ts new file mode 100644 index 00000000000..9f920a889ea --- /dev/null +++ b/core/mcp/shadowCodeToolsServer.ts @@ -0,0 +1,282 @@ +import { randomUUID } from "node:crypto"; +import * as http from "node:http"; + +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; +import { + CallToolRequestSchema, + ListToolsRequestSchema, +} from "@modelcontextprotocol/sdk/types.js"; + +import { ContinueConfig, Tool, ToolCall } from "../index.js"; + +/** + * Everything the MCP server needs from the running Core instance. Kept as a + * narrow interface (rather than a reference to Core itself) so this module + * stays testable and doesn't create a circular import with core.ts. + */ +export interface ShadowCodeToolsRuntime { + loadConfig: () => Promise; + /** Executes a tool call exactly the way the normal `tools/call` IPC path does. */ + executeTool: ( + toolCall: ToolCall, + sessionId: string | undefined, + ) => Promise<{ + contextItems: { content: string }[]; + errorMessage: string | undefined; + }>; + /** + * Asks the GUI to resolve the tool policy for this call (reusing the same + * policy/settings logic normal tool calls go through) and, if the policy + * requires it, to show an approval prompt and wait for the user's decision. + * Resolves immediately for auto-approved/auto-disabled tools. + */ + requestApproval: (params: { + approvalId: string; + toolCallId: string; + sessionId: string | undefined; + toolName: string; + args: Record; + displayTitle?: string; + wouldLikeTo?: string; + }) => Promise<{ approved: boolean }>; +} + +function toolToMcpSchema(tool: Tool) { + return { + name: tool.function.name, + description: tool.function.description ?? "", + inputSchema: tool.function.parameters ?? { type: "object", properties: {} }, + }; +} + +/** + * Hosts Continue's own built-in tools as an MCP server, so that a `claude -p` + * subprocess (see core/llm/llms/ClaudeCodeCli.ts) can be pointed at it via + * `--mcp-config` instead of using Claude Code's own built-in Read/Write/Edit/ + * Bash tools. Every tool call still goes through Continue's existing + * execution + approval path (this.runtime.executeTool / requestApproval) - + * Claude Code CLI itself never touches the filesystem or a shell directly. + * + * Runs as a stateless Streamable HTTP MCP server bound to 127.0.0.1 inside + * Core's own process, so tool execution has direct access to the same + * config/messenger state `handleToolCall` uses - no extra process, no new + * cross-process IPC. + */ +export class ShadowCodeToolsMcpServer { + private httpServer: http.Server | undefined; + private transport: StreamableHTTPServerTransport | undefined; + private url: string | undefined; + private startPromise: Promise | undefined; + + // The set of tools actually active for a given Continue session/turn + // (e.g. augmentedOptions.tools from tokenOptimizedStreamChat, which + // includes any tool overrides plus the force-included shadow_* tools) - + // registered by ClaudeCodeCli right before spawning `claude` for that + // session. Falls back to the full config.tools list if nothing was + // registered (e.g. a stale/unknown session id). + private toolsBySession = new Map(); + + constructor(private readonly runtime: ShadowCodeToolsRuntime) {} + + registerToolsForSession(sessionId: string, tools: Tool[]) { + this.toolsBySession.set(sessionId, tools); + } + + unregisterSession(sessionId: string) { + this.toolsBySession.delete(sessionId); + } + + private async resolveTools(sessionId: string | undefined): Promise { + if (sessionId && this.toolsBySession.has(sessionId)) { + return this.toolsBySession.get(sessionId)!; + } + const config = await this.runtime.loadConfig(); + return config?.tools ?? []; + } + + /** Idempotent: safe to call on every provider invocation. */ + async ensureStarted(): Promise { + if (this.url) { + return this.url; + } + if (!this.startPromise) { + this.startPromise = this.start(); + } + return this.startPromise; + } + + private buildMcpServer(): Server { + const server = new Server( + { name: "shadow-code", version: "1.0.0" }, + { capabilities: { tools: {} } }, + ); + + server.setRequestHandler(ListToolsRequestSchema, async (request) => { + const sessionId = (request.params?._meta as any)?.continueSessionId as + | string + | undefined; + const tools = await this.resolveTools(sessionId); + return { tools: tools.map(toolToMcpSchema) }; + }); + + server.setRequestHandler(CallToolRequestSchema, async (request) => { + const { name, arguments: args } = request.params; + + // The URL carries the originating Continue session id (see getUrlForSession). + const sessionId = (request.params._meta as any)?.continueSessionId as + | string + | undefined; + + const tools = await this.resolveTools(sessionId); + const tool = tools.find((t) => t.function.name === name); + + if (!tool) { + return { + isError: true, + content: [{ type: "text", text: `Unknown tool: ${name}` }], + }; + } + + const toolCallId = randomUUID(); + const approvalId = randomUUID(); + + const { approved } = await this.runtime.requestApproval({ + approvalId, + toolCallId, + sessionId, + toolName: tool.function.name, + args: args ?? {}, + displayTitle: tool.displayTitle, + wouldLikeTo: tool.wouldLikeTo, + }); + + if (!approved) { + return { + isError: true, + content: [ + { + type: "text", + text: `Tool call "${tool.function.name}" was not approved.`, + }, + ], + }; + } + + const toolCall: ToolCall = { + id: toolCallId, + type: "function", + function: { + name: tool.function.name, + arguments: JSON.stringify(args ?? {}), + }, + }; + + const result = await this.runtime.executeTool(toolCall, sessionId); + + if (result.errorMessage) { + return { + isError: true, + content: [{ type: "text", text: result.errorMessage }], + }; + } + + return { + content: result.contextItems.map((item) => ({ + type: "text" as const, + text: item.content, + })), + }; + }); + + return server; + } + + private async start(): Promise { + const mcpServer = this.buildMcpServer(); + // NOTE: stateless mode (sessionIdGenerator: undefined) is broken on + // Windows in the installed SDK version - the transport silently 500s on + // every request after the first on the same server instance (confirmed + // via direct repro, unrelated to Claude Code CLI). Stateful mode doesn't + // hit this; the `mcp-session-id` it negotiates is purely a transport- + // level connection id and is unrelated to `continueSessionId` below + // (which identifies the Continue conversation, carried via query param + // and threaded into every request's `_meta` regardless of transport + // session). + this.transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + }); + await mcpServer.connect(this.transport); + + this.httpServer = http.createServer((req, res) => { + let body = ""; + req.on("data", (chunk) => (body += chunk)); + req.on("end", () => { + const parsed = body ? JSON.parse(body) : undefined; + const sessionId = new URL( + req.url ?? "/", + "http://127.0.0.1", + ).searchParams.get("continueSessionId"); + if (parsed && sessionId) { + parsed.params = parsed.params ?? {}; + parsed.params._meta = { + ...(parsed.params._meta ?? {}), + continueSessionId: sessionId, + }; + } + void this.transport!.handleRequest(req, res, parsed); + }); + }); + + const port = await new Promise((resolve, reject) => { + this.httpServer!.once("error", reject); + this.httpServer!.listen(0, "127.0.0.1", () => { + const address = this.httpServer!.address(); + if (address && typeof address !== "string") { + resolve(address.port); + } else { + reject(new Error("Failed to determine shadow-code MCP server port")); + } + }); + }); + + this.url = `http://127.0.0.1:${port}/mcp`; + return this.url; + } + + /** URL to put in the generated --mcp-config for a given Continue session. */ + getUrlForSession(sessionId: string): string { + if (!this.url) { + throw new Error("ShadowCodeToolsMcpServer not started yet"); + } + return `${this.url}?continueSessionId=${encodeURIComponent(sessionId)}`; + } + + async stop(): Promise { + await this.transport?.close(); + await new Promise((resolve) => + this.httpServer?.close(() => resolve()), + ); + this.url = undefined; + this.startPromise = undefined; + } +} + +// Module-level singleton: Core instantiates and starts this once; the +// ClaudeCodeCli LLM provider (constructed generically via llmFromDescription, +// with no reference to Core) reads it from here instead of threading a Core +// reference through every provider's constructor. +let instance: ShadowCodeToolsMcpServer | undefined; + +export function setShadowCodeToolsMcpServer(server: ShadowCodeToolsMcpServer) { + instance = server; +} + +export function getShadowCodeToolsMcpServer(): ShadowCodeToolsMcpServer { + if (!instance) { + throw new Error( + "ShadowCodeToolsMcpServer has not been initialized by Core yet", + ); + } + return instance; +} diff --git a/core/protocol/webview.ts b/core/protocol/webview.ts index 215ec40109b..05fe525b070 100644 --- a/core/protocol/webview.ts +++ b/core/protocol/webview.ts @@ -41,4 +41,20 @@ export type ToWebviewFromIdeOrCoreProtocol = { "jetbrains/setColors": [Record, void]; sessionUpdate: [{ sessionInfo: any | undefined }, void]; toolCallPartialOutput: [{ toolCallId: string; contextItems: any[] }, void]; + + // Sent by the in-process shadow-code-tools MCP server (core/mcp/shadowCodeToolsServer.ts) + // when a Claude Code CLI-driven tool call needs approval per the same policy rules + // that gate normal tool calls. Blocks until the user accepts/rejects in the GUI. + "claudeCodeCli/authorizeToolCall": [ + { + approvalId: string; + toolCallId: string; + sessionId?: string; + toolName: string; + args: Record; + displayTitle?: string; + wouldLikeTo?: string; + }, + { approved: boolean }, + ]; }; diff --git a/core/tools/callTool.ts b/core/tools/callTool.ts index d8144610fc4..6a338c3d305 100644 --- a/core/tools/callTool.ts +++ b/core/tools/callTool.ts @@ -21,6 +21,11 @@ import { readSkillImpl } from "./implementations/readSkill"; import { requestRuleImpl } from "./implementations/requestRule"; import { runTerminalCommandImpl } from "./implementations/runTerminalCommand"; import { searchWebImpl } from "./implementations/searchWeb"; +import { + editExistingFileUnsupportedImpl, + multiEditServerImpl, + singleFindAndReplaceServerImpl, +} from "./implementations/serverSideEdit"; import { shadowGetChatHistoryImpl, shadowGetConversationStatsImpl, @@ -212,6 +217,16 @@ export async function callBuiltInTool( return await fileGlobSearchImpl(args, extras); case BuiltInToolNames.RunTerminalCommand: return await runTerminalCommandImpl(args, extras); + // Normally client-executed only (see CLIENT_TOOLS_IMPLS in ./builtIn) so + // the GUI can stream a live diff preview into the editor. These + // server-side fallbacks let callers with no interactive editor session + // (e.g. shadow-code-tools MCP) still actually apply the edit. + case BuiltInToolNames.SingleFindAndReplace: + return await singleFindAndReplaceServerImpl(args, extras); + case BuiltInToolNames.MultiEdit: + return await multiEditServerImpl(args, extras); + case BuiltInToolNames.EditExistingFile: + return await editExistingFileUnsupportedImpl(args, extras); case BuiltInToolNames.SearchWeb: return await searchWebImpl(args, extras); case BuiltInToolNames.FetchUrlContent: diff --git a/core/tools/implementations/serverSideEdit.ts b/core/tools/implementations/serverSideEdit.ts new file mode 100644 index 00000000000..608dd5190aa --- /dev/null +++ b/core/tools/implementations/serverSideEdit.ts @@ -0,0 +1,76 @@ +import { ContinueError, ContinueErrorReason } from "../../util/errors"; +import { getCleanUriPath, getUriPathBasename } from "../../util/uri"; +import { BuiltInToolNames } from "../builtIn"; +import { ToolImpl } from "."; + +/** + * Server-side execution for edit tools whose file-content computation is + * already deterministic and core-side via `tool.preprocessArgs` (see + * core/tools/definitions/singleFindAndReplace.ts and multiEdit.ts - both + * call into core/edit/searchAndReplace/*, which is the same logic the GUI's + * client-side edit implementations rely on for computing the new contents). + * + * Normal chat sessions run these through the GUI instead + * (gui/src/util/clientTools/*), which streams the diff live into an open + * editor tab for interactive review before writing. This server-side path + * skips that live-preview UX and writes the file directly - used when there + * is no interactive editor session driving the call, e.g. tool calls coming + * from shadow-code-tools MCP (see core/mcp/shadowCodeToolsServer.ts). + */ +async function applyPreprocessedEdit( + toolName: string, + args: any, + extras: Parameters[1], +) { + if (!extras.tool.preprocessArgs) { + throw new ContinueError( + ContinueErrorReason.Unknown, + `${toolName} has no preprocessArgs implementation to compute the edit`, + ); + } + const processed = await extras.tool.preprocessArgs(args, { + ide: extras.ide, + }); + const fileUri = processed.fileUri as string; + const newFileContents = processed.newFileContents as string; + + await extras.ide.writeFile(fileUri, newFileContents); + await extras.ide.saveFile(fileUri); + if (extras.codeBaseIndexer) { + void extras.codeBaseIndexer.refreshCodebaseIndexFiles([fileUri]); + } + + return [ + { + name: getUriPathBasename(fileUri), + description: getCleanUriPath(fileUri), + content: `Applied ${toolName} to ${getCleanUriPath(fileUri)}`, + uri: { type: "file" as const, value: fileUri }, + }, + ]; +} + +export const singleFindAndReplaceServerImpl: ToolImpl = async (args, extras) => + applyPreprocessedEdit(BuiltInToolNames.SingleFindAndReplace, args, extras); + +export const multiEditServerImpl: ToolImpl = async (args, extras) => + applyPreprocessedEdit(BuiltInToolNames.MultiEdit, args, extras); + +// edit_existing_file's `changes` argument is a freeform "sketch" of the +// edit (e.g. "// ... existing code ..." placeholders) with no deterministic, +// core-side reconciliation available anywhere in this codebase today - +// applying it correctly is normally done by the GUI's live diff-preview +// flow (gui/src/util/clientTools/editImpl.ts), which has no equivalent +// here. Rather than guess at a fuzzy-patch algorithm and risk corrupting +// files, this tells the model to use single_find_and_replace/multi_edit +// instead, which are always offered alongside edit_existing_file (see +// core/tools/index.ts:getConfigDependentToolDefinitions) and cover the same +// need with exact-match semantics that are safe to apply server-side. +export const editExistingFileUnsupportedImpl: ToolImpl = async () => { + throw new ContinueError( + ContinueErrorReason.Unknown, + "edit_existing_file is not available in this session. Use single_find_and_replace " + + "(or multi_edit for several changes to one file) instead - read the file first " + + "if you don't already know its exact current contents.", + ); +}; diff --git a/gui/src/App.tsx b/gui/src/App.tsx index c237b9327ee..eeafff95f28 100644 --- a/gui/src/App.tsx +++ b/gui/src/App.tsx @@ -3,6 +3,7 @@ import Layout from "./components/Layout"; import { MainEditorProvider } from "./components/mainInput/TipTapEditor"; import { SubmenuContextProvidersProvider } from "./context/SubmenuContextProviders"; import { VscThemeProvider } from "./context/VscTheme"; +import ClaudeCodeCliApprovalGate from "./hooks/ClaudeCodeCliApprovalGate"; import ParallelListeners from "./hooks/ParallelListeners"; import ConfigPage from "./pages/config"; import ErrorPage from "./pages/error"; @@ -59,6 +60,7 @@ function App() { + ); } diff --git a/gui/src/hooks/ClaudeCodeCliApprovalGate.tsx b/gui/src/hooks/ClaudeCodeCliApprovalGate.tsx new file mode 100644 index 00000000000..87adef96790 --- /dev/null +++ b/gui/src/hooks/ClaudeCodeCliApprovalGate.tsx @@ -0,0 +1,126 @@ +import { ToolCallState } from "core"; +import { useContext, useState } from "react"; +import { IdeMessengerContext } from "../context/IdeMessenger"; +import { useAppSelector } from "../redux/hooks"; +import { evaluateToolPolicy } from "../redux/thunks/evaluateToolPolicies"; +import { useWebviewListener } from "./useWebviewListener"; + +interface PendingApproval { + approvalId: string; + toolName: string; + displayTitle?: string; + wouldLikeTo?: string; + args: Record; + resolve: (approved: boolean) => void; +} + +/** + * Answers "claudeCodeCli/authorizeToolCall" requests sent by the in-core + * shadow-code-tools MCP server (core/mcp/shadowCodeToolsServer.ts) whenever a + * Claude Code CLI-driven tool call needs a policy decision. Reuses the same + * evaluateToolPolicy logic and toolSettings the normal agent tool-call flow + * uses, so a tool the user has configured as auto-approved/disabled behaves + * identically whether it was called by the normal chat loop or by Claude + * Code CLI. Only "allowedWithPermission" tools actually block on this + * component's banner UI. + */ +function ClaudeCodeCliApprovalGate() { + const ideMessenger = useContext(IdeMessengerContext); + const tools = useAppSelector((store) => store.config.config.tools); + const toolPolicies = useAppSelector((store) => store.ui.toolSettings); + const [pending, setPending] = useState([]); + + useWebviewListener( + "claudeCodeCli/authorizeToolCall", + async (data) => { + const syntheticToolCallState: ToolCallState = { + toolCallId: data.approvalId, + status: "generated", + parsedArgs: data.args, + toolCall: { + id: data.approvalId, + type: "function", + function: { + name: data.toolName, + arguments: JSON.stringify(data.args), + }, + }, + }; + + const { policy } = await evaluateToolPolicy( + ideMessenger, + tools, + syntheticToolCallState, + toolPolicies, + ); + + if (policy === "allowedWithoutPermission") { + return { approved: true }; + } + if (policy === "disabled") { + return { approved: false }; + } + + // allowedWithPermission: block until the user clicks Allow/Deny below. + return new Promise<{ approved: boolean }>((resolve) => { + setPending((prev) => [ + ...prev, + { + approvalId: data.approvalId, + toolName: data.toolName, + displayTitle: data.displayTitle, + wouldLikeTo: data.wouldLikeTo, + args: data.args, + resolve: (approved) => resolve({ approved }), + }, + ]); + }); + }, + [tools, toolPolicies, ideMessenger], + ); + + function resolvePending(approvalId: string, approved: boolean) { + setPending((prev) => { + prev.find((p) => p.approvalId === approvalId)?.resolve(approved); + return prev.filter((p) => p.approvalId !== approvalId); + }); + } + + if (pending.length === 0) { + return null; + } + + return ( +
+ {pending.map((p) => ( +
+
+ Claude Code wants to {p.wouldLikeTo ?? p.toolName} +
+
+ {p.displayTitle ?? p.toolName} +
+
+ + +
+
+ ))} +
+ ); +} + +export default ClaudeCodeCliApprovalGate; diff --git a/gui/src/redux/thunks/evaluateToolPolicies.ts b/gui/src/redux/thunks/evaluateToolPolicies.ts index 7874edabdcc..9e0d69be394 100644 --- a/gui/src/redux/thunks/evaluateToolPolicies.ts +++ b/gui/src/redux/thunks/evaluateToolPolicies.ts @@ -16,7 +16,7 @@ interface EvaluatedPolicy { * Evaluates the tool policy for a tool call, including dynamic policy evaluation * Note that tool group policies are not considered here because activeTools already excludes disabled groups */ -async function evaluateToolPolicy( +export async function evaluateToolPolicy( ideMessenger: IIdeMessenger, activeTools: Tool[], toolCallState: ToolCallState, From c567c80d00410281e1c58c7c509f7baaa3166135 Mon Sep 17 00:00:00 2001 From: deepak-s-2000 Date: Tue, 1 Sep 2026 00:00:22 +0530 Subject: [PATCH 4/4] rebrand: Continue -> Shadow Code across the monorepo Rename the product for independent publishing as a VS Code extension (+ JetBrains plugin + CLI). Naming scheme: - display name "Shadow Code"; package/slug "shadow-code" - code namespace for string IDs "shadowCode." (commands, settings, context keys, webview view IDs) - global data dir ~/.shadow-code; workspace dir .shadow-code/; ignore/rc files .shadow-codeignore / .shadow-coderc.json / .shadow-coderules - env var prefix SHADOW_CODE_; IntelliJ plugin id com.shadowcode.plugin Scope: extensions/vscode + gui + core + binary + extensions/cli + extensions/intellij + docs + .github. VSIX builds clean (extensions/vscode/build/shadow-code-*.vsix); all TS projects type-check; core/gui unit tests green. Icons: user-supplied spiral icon for media/icon.png; generated spiral SVG for the activity bar (media/sidebar-icon.svg), the GUI logo (ShadowCodeLogo.tsx), and the JetBrains plugin/tool-window icons. Deliberately unchanged (functional / legal, not user-facing branding): - @continuedev/* npm scope; continue-proxy provider id; X-Continue-Provider header; api./hub.continue.dev backend endpoints - upstream copyright line in LICENSE.txt (Apache-2.0 requires it; a Shadow Code line was added) - internal TS symbol names and webview-protocol message names (SerializedContinueConfig, focusContinueInput, ...) - IntelliJ Kotlin package com.github.continuedev.continueintellijextension - code comments linking upstream issues/PRs Still open: marketplace publisher id (placeholder "shadow-code"); CLI binary name (kept as "cn"). Includes pre-existing in-progress work on the Claude Code CLI provider that was already in the working tree. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_016Np5Eu8A665zkGmLuPouUn --- .github/ISSUE_TEMPLATE/bug_report.yml | 14 +- .github/ISSUE_TEMPLATE/config.yml | 9 +- .github/ISSUE_TEMPLATE/docs_issue_report.yml | 7 +- .../actions/build-vscode-extension/action.yml | 12 +- .../actions/run-vscode-e2e-test/action.yml | 6 +- .github/pull_request_template.md | 9 +- .github/workflows/auto-fix-failed-tests.yml | 2 +- .github/workflows/cli-pr-checks.yml | 2 +- ...ue-agent.yml => run-shadow-code-agent.yml} | 6 +- ...inue-agents.yml => shadow-code-agents.yml} | 356 +- .github/workflows/snyk-agent.yaml | 2 +- .github/workflows/tidy-up-codebase.yml | 4 +- .gitignore | 9 +- .prettierignore | 4 +- .../agents/breaking-change-detector.md | 0 .../agents/dependency-security-review.md | 0 .../agents/error-message-quality.md | 0 .../agents/input-validation.md | 0 .../agents/test-coverage.md | 0 .../checks/anti-slop.md | 0 .../checks/react-best-practices.md | 0 .../checks/security-audit.md | 0 .../checks/setup-scripts.md | 0 .../checks/stale-comments.md | 0 .../checks/update-agents-md.md | 0 .../checks/update-continue-docs.md | 0 {.continue => .shadow-code}/environment.json | 0 .../prompts/core-unit-test.prompt | 78 +- .../prompts/sub-agent-background.md | 0 .../prompts/sub-agent-foreground.md | 0 .../prompts/update-llm-info.prompt | 158 +- .../rules/bigger-picture-description-rules.md | 0 {.continue => .shadow-code}/rules/colors.md | 0 .../rules/continue-specificity.md | 0 .../rules/css-units.md | 0 .../rules/dev-data-guide.md | 0 .../rules/documentation-description-rule.md | 0 .../rules/documentation-standards.md | 0 .../rules/github-pr-documentation-updater.md | 0 .../rules/gui-link-opening.md | 0 .../rules/intellij-plugin-test-execution.md | 0 .../rules/llm-specificity.md | 0 .../migrate-styled-components-to-tailwind.md | 0 .../rules/mintlify-formatting.md | 24 +- .../rules/navigating-responses.md | 0 .../rules/new-protocol-message.md | 0 .../rules/no-any-types.md | 0 .../rules/overeager.md | 0 .../rules/personality.md | 0 .../rules/programming-principles.md | 0 .../rules/pure-function-unit-tests.md | 0 .../rules/test-running-guide.md | 0 .../rules/typescript-enum-usage.md | 0 .../rules/unit-testing-rules.md | 0 .../vs-code-commands-helper-functions.md | 0 .continueignore => .shadow-codeignore | 16 +- .vscode/launch.json | 6 +- .vscode/settings.json | 2 +- BUILD_DEPENDENCIES.md | 10 +- CLA.md | 8 +- CLAUDE.md | 4 +- CONTRIBUTING.md | 62 +- README.md | 70 +- TESTING.md | 8 +- actions/README.md | 22 +- actions/general-review/action.yml | 56 +- .../general-review/scripts/writeMarkdown.js | 14 +- .../{.continueignore => .shadow-codeignore} | 0 binary/build.js | 2 +- binary/core-dev-server.js | 6 +- binary/package-lock.json | 23 +- binary/pkgJson/darwin-arm64/package.json | 2 +- binary/pkgJson/darwin-x64/package.json | 2 +- binary/pkgJson/linux-arm64/package.json | 2 +- binary/pkgJson/linux-x64/package.json | 2 +- binary/pkgJson/win32-arm64/package.json | 2 +- binary/pkgJson/win32-x64/package.json | 2 +- binary/prompt-logs.js | 2 +- binary/src/IpcMessenger.ts | 8 +- binary/src/index.ts | 4 +- binary/src/logging.ts | 2 +- binary/test/binary.test.ts | 16 +- core/agent/subagentRunner.ts | 397 ++ core/agent/subagentRunner.vitest.ts | 230 + core/agent/subagentSystemMessage.ts | 29 + .../filtering/test/filter.vitest.ts | 2 +- core/autocomplete/templating/validation.ts | 2 +- core/config/ConfigHandler.ts | 2 +- core/config/ConfigHandler.vitest.ts | 4 +- core/config/createNewAssistantFile.ts | 6 +- .../getWorkspaceContinueRuleDotFiles.ts | 4 +- core/config/json/loadRcConfigs.ts | 2 +- core/config/load.ts | 12 +- core/config/loadLocalAssistants.ts | 22 +- core/config/loadLocalAssistants.vitest.ts | 48 +- .../markdown/loadCodebaseRules.vitest.ts | 12 +- core/config/markdown/loadMarkdownRules.ts | 4 +- .../ruleCollocationApplication.vitest.ts | 2 +- core/config/markdown/utils.ts | 4 +- core/config/markdown/utils.vitest.ts | 6 +- .../profile/LocalProfileLoader.vitest.ts | 2 +- core/config/profile/doLoadConfig.ts | 4 +- core/config/profile/doLoadConfig.vitest.ts | 4 +- core/config/util.ts | 4 +- core/config/validation.ts | 2 +- core/config/workspace/workspaceBlocks.ts | 7 +- .../workspace/workspaceBlocks.vitest.ts | 38 +- core/config/yaml/LocalPlatformClient.ts | 6 +- .../config/yaml/LocalPlatformClient.vitest.ts | 32 +- core/config/yaml/loadYaml.ts | 2 +- core/config/yaml/models.vitest.ts | 10 +- core/context/mcp/json/loadJsonMcpConfigs.ts | 4 +- core/core.ts | 68 +- core/data/log.ts | 4 +- core/data/shadowChatDb.ts | 2 +- core/index.d.ts | 67 +- core/indexing/continueignore.ts | 2 +- core/indexing/ignore.ts | 4 +- core/indexing/ignore.vitest.ts | 2 +- core/indexing/shouldIgnore.test.ts | 10 +- core/indexing/walkDir.test.ts | 2 +- core/indexing/walkDir.ts | 10 +- core/llm/claudeCodeToolSupport.vitest.ts | 33 + core/llm/llms/ClaudeCodeCli.ts | 196 +- core/llm/llms/ClawRouter.ts | 4 +- core/llm/llms/ClawRouter.vitest.ts | 2 +- core/llm/openaiTypeConverters.ts | 3 + core/llm/rules/alwaysApply.vitest.ts | 10 +- core/llm/rules/alwaysApplyRules.vitest.ts | 2 +- core/llm/rules/getSystemMessageWithRules.ts | 6 +- .../rules/getSystemMessageWithRules.vitest.ts | 4 +- core/llm/rules/implicitGlobalRules.vitest.ts | 8 +- core/llm/rules/nestedDirectoryRules.vitest.ts | 2 +- core/llm/rules/ruleColocation.vitest.ts | 4 +- core/llm/rules/rules-utils.ts | 2 +- core/llm/tokenOptimizedChat.ts | 290 +- core/llm/tokenOptimizedChat.vitest.ts | 223 + core/llm/toolSupport.ts | 11 + core/mcp/shadowCodeToolsServer.ts | 257 +- core/mcp/verifyConcurrentSessions.mjs | 124 + core/package-lock.json | 2 +- core/package.json | 2 +- core/promptFiles/createNewPromptFile.ts | 2 +- core/promptFiles/getPromptFiles.ts | 2 +- core/promptFiles/index.ts | 6 +- core/promptFiles/initPrompt.ts | 4 +- core/protocol/core.ts | 4 + core/protocol/passThrough.ts | 2 + core/protocol/webview.ts | 15 +- core/test/jest.global-setup.ts | 10 +- core/test/jest.setup-after-env.js | 4 +- core/test/testEnv.test.ts | 6 +- core/test/vitest.global-setup.ts | 10 +- core/tools/builtIn.ts | 1 + core/tools/callTool.ts | 3 + core/tools/constants.ts | 10 + core/tools/definitions/createRuleBlock.ts | 2 +- core/tools/definitions/index.ts | 1 + core/tools/definitions/spawnSubagents.ts | 81 + .../implementations/createRuleBlock.test.ts | 2 +- core/tools/implementations/lsTool.vitest.ts | 2 +- core/tools/implementations/spawnSubagents.ts | 134 + core/tools/index.ts | 1 + core/util/constants.ts | 8 +- core/util/grepSearch.ts | 2 +- core/util/grepSearch.vitest.ts | 2 +- core/util/historyUtils.ts | 2 +- core/util/paths.ts | 21 +- docs-site/app/layout.tsx | 5 +- docs-site/lib/basePath.ts | 2 +- docs-site/lib/resolveHref.ts | 7 +- docs-site/package.json | 2 +- docs/autocomplete/how-to-use-it.mdx | 6 +- docs/chat/how-to-use-it.mdx | 6 +- docs/cli/configuration.mdx | 4 +- docs/cli/quickstart.mdx | 12 +- docs/cli/tool-permissions.mdx | 4 +- docs/cli/tui-mode.mdx | 10 +- docs/customize/custom-providers.mdx | 10 +- docs/customize/deep-dives/autocomplete.mdx | 32 +- docs/customize/deep-dives/configuration.mdx | 22 +- .../customize/deep-dives/custom-providers.mdx | 2 +- .../customize/deep-dives/development-data.mdx | 6 +- docs/customize/deep-dives/mcp.mdx | 20 +- .../deep-dives/model-capabilities.mdx | 30 +- docs/customize/deep-dives/prompts.mdx | 8 +- docs/customize/deep-dives/rules.mdx | 24 +- docs/customize/mcp-tools.mdx | 4 +- .../model-providers/more/SambaNova.mdx | 4 +- .../model-providers/more/asksage.mdx | 14 +- .../model-providers/more/cerebras.mdx | 4 +- .../model-providers/more/clawrouter.mdx | 12 +- .../model-providers/more/cloudflare.mdx | 4 +- .../customize/model-providers/more/cohere.mdx | 2 +- .../model-providers/more/deepinfra.mdx | 6 +- .../model-providers/more/flowise.mdx | 6 +- .../model-providers/more/function-network.mdx | 2 +- docs/customize/model-providers/more/groq.mdx | 2 +- .../model-providers/more/ipex_llm.mdx | 8 +- docs/customize/model-providers/more/kindo.mdx | 2 +- .../model-providers/more/lemonade.mdx | 20 +- .../model-providers/more/llamafile.mdx | 4 +- docs/customize/model-providers/more/mimo.mdx | 80 +- .../model-providers/more/minimax.mdx | 2 +- .../model-providers/more/mistral.mdx | 4 +- .../model-providers/more/moonshot.mdx | 2 +- docs/customize/model-providers/more/morph.mdx | 2 +- docs/customize/model-providers/more/msty.mdx | 6 +- .../model-providers/more/ncompass.mdx | 6 +- .../customize/model-providers/more/nebius.mdx | 2 +- .../customize/model-providers/more/novita.mdx | 6 +- .../more/openvino_model_server.mdx | 2 +- .../model-providers/more/ovhcloud.mdx | 2 +- .../customize/model-providers/more/relace.mdx | 4 +- .../model-providers/more/replicatellm.mdx | 6 +- .../model-providers/more/sagemaker.mdx | 2 +- .../model-providers/more/scaleway.mdx | 2 +- .../model-providers/more/siliconflow.mdx | 2 +- .../model-providers/more/tensorix.mdx | 4 +- .../model-providers/more/textgenwebui.mdx | 2 +- .../model-providers/more/together.mdx | 4 +- .../customize/model-providers/more/venice.mdx | 4 +- docs/customize/model-providers/more/vllm.mdx | 6 +- .../model-providers/more/watsonx.mdx | 6 +- docs/customize/model-providers/more/xAI.mdx | 4 +- docs/customize/model-providers/more/zai.mdx | 2 +- docs/customize/model-providers/overview.mdx | 6 +- .../model-providers/top-level/anthropic.mdx | 6 +- .../model-providers/top-level/azure.mdx | 2 +- .../model-providers/top-level/bedrock.mdx | 6 +- .../model-providers/top-level/gemini.mdx | 2 +- .../model-providers/top-level/inception.mdx | 6 +- .../model-providers/top-level/lmstudio.mdx | 4 +- .../model-providers/top-level/ollama.mdx | 8 +- .../model-providers/top-level/openai.mdx | 8 +- .../model-providers/top-level/openrouter.mdx | 8 +- .../tetrate_agent_router_service.mdx | 12 +- .../model-providers/top-level/vertexai.mdx | 2 +- docs/customize/model-roles.mdx | 2 +- docs/customize/model-roles/00-intro.mdx | 2 +- docs/customize/model-roles/apply.mdx | 4 +- docs/customize/model-roles/autocomplete.mdx | 8 +- docs/customize/model-roles/chat.mdx | 2 +- docs/customize/model-roles/edit.mdx | 4 +- docs/customize/model-roles/embeddings.mdx | 4 +- docs/customize/model-roles/intro.mdx | 2 +- docs/customize/model-roles/reranking.mdx | 2 +- docs/customize/models.mdx | 100 +- docs/customize/overview.mdx | 12 +- docs/customize/rules.mdx | 4 +- docs/faqs.mdx | 50 +- .../atlassian-mcp-continue-cookbook.mdx | 36 +- .../chrome-devtools-mcp-performance.mdx | 24 +- docs/guides/cli.mdx | 22 +- .../codebase-documentation-awareness.mdx | 10 +- .../guides/configuring-models-rules-tools.mdx | 16 +- docs/guides/continue-docs-mcp-cookbook.mdx | 106 +- docs/guides/custom-code-rag.mdx | 10 +- docs/guides/dlt-mcp-continue-cookbook.mdx | 52 +- docs/guides/doc-writing-agent-cli.mdx | 64 +- docs/guides/github-mcp-continue-cookbook.mdx | 32 +- docs/guides/github-pr-review-bot.mdx | 74 +- docs/guides/how-to-self-host-a-model.mdx | 4 +- docs/guides/instinct.mdx | 6 +- .../netlify-mcp-continuous-deployment.mdx | 22 +- docs/guides/notion-continue-guide.mdx | 34 +- docs/guides/ollama-guide.mdx | 34 +- docs/guides/overview.mdx | 20 +- docs/guides/plan-mode-guide.mdx | 4 +- docs/guides/posthog-github-continuous-ai.mdx | 50 +- .../running-continue-without-internet.mdx | 10 +- docs/guides/sanity-mcp-continue-cookbook.mdx | 42 +- docs/guides/sentry-mcp-error-monitoring.mdx | 50 +- docs/guides/snyk-mcp-continue-cookbook.mdx | 50 +- .../guides/supabase-mcp-database-workflow.mdx | 42 +- docs/guides/understanding-configs.mdx | 20 +- docs/home.mdx | 12 +- .../agent/context-selection.mdx | 2 +- docs/ide-extensions/agent/how-it-works.mdx | 8 +- .../ide-extensions/agent/how-to-customize.mdx | 4 +- docs/ide-extensions/agent/model-setup.mdx | 8 +- docs/ide-extensions/agent/plan-mode.mdx | 4 +- docs/ide-extensions/agent/quick-start.mdx | 6 +- .../autocomplete/context-selection.mdx | 2 +- .../autocomplete/how-it-works.mdx | 6 +- .../autocomplete/how-to-customize.mdx | 6 +- .../autocomplete/model-setup.mdx | 6 +- .../ide-extensions/autocomplete/next-edit.mdx | 22 +- .../autocomplete/quick-start.mdx | 8 +- .../ide-extensions/chat/context-selection.mdx | 2 +- docs/ide-extensions/chat/how-it-works.mdx | 4 +- docs/ide-extensions/chat/how-to-customize.mdx | 2 +- docs/ide-extensions/chat/model-setup.mdx | 4 +- docs/ide-extensions/chat/quick-start.mdx | 2 +- .../ide-extensions/edit/context-selection.mdx | 2 +- docs/ide-extensions/edit/how-it-works.mdx | 2 +- docs/ide-extensions/edit/how-to-customize.mdx | 2 +- docs/ide-extensions/edit/quick-start.mdx | 6 +- docs/ide-extensions/install.mdx | 16 +- docs/ide-extensions/plan/how-it-works.mdx | 6 +- docs/ide-extensions/plan/quick-start.mdx | 2 +- docs/ide-extensions/quick-start.mdx | 28 +- docs/index.mdx | 6 +- docs/overview.mdx | 6 +- docs/reference.mdx | 18 +- docs/reference/continue-mcp.mdx | 30 +- docs/reference/deprecated-codebase.mdx | 10 +- docs/reference/deprecated-docs.mdx | 10 +- docs/reference/json-reference.mdx | 14 +- docs/reference/yaml-migration.mdx | 10 +- docs/snippets/cli-install.mdx | 4 +- docs/troubleshooting.mdx | 28 +- extensions/cli/.gitignore | 2 +- extensions/cli/AGENTS.md | 10 +- extensions/cli/BUILD.md | 2 +- extensions/cli/README.md | 14 +- extensions/cli/docs/artifact-uploads.md | 4 +- extensions/cli/docs/storage-sync.md | 4 +- extensions/cli/package.json | 2 +- extensions/cli/spec/config-loading.md | 6 +- extensions/cli/spec/mcp.md | 10 +- extensions/cli/spec/onboarding.md | 6 +- extensions/cli/spec/otlp-metrics.md | 12 +- extensions/cli/spec/permissions.md | 6 +- extensions/cli/spec/tty-less-support.md | 4 +- extensions/cli/spec/tui.md | 4 +- extensions/cli/src/CLIPlatformClient.test.ts | 2 +- extensions/cli/src/CLIPlatformClient.ts | 4 +- extensions/cli/src/__mocks__/systemMessage.ts | 2 +- extensions/cli/src/asciiArt.test.ts | 14 +- extensions/cli/src/asciiArt.ts | 44 +- extensions/cli/src/commands/commands.ts | 4 +- .../cli/src/commands/devbox-entrypoint.md | 4 +- extensions/cli/src/commands/init.ts | 2 +- extensions/cli/src/commands/ls.ts | 2 +- extensions/cli/src/commands/review.ts | 4 +- .../cli/src/commands/review/renderReport.ts | 12 +- .../commands/review/resolveReviews.test.ts | 12 +- .../cli/src/commands/review/resolveReviews.ts | 10 +- extensions/cli/src/config.ts | 2 +- extensions/cli/src/configLoader.ts | 2 +- extensions/cli/src/continueSDK.ts | 10 +- .../e2e/headless-anthropic-api-key.test.ts | 4 +- .../e2e/headless-dynamic-responses.test.ts | 4 +- .../cli/src/e2e/headless-org-flag.test.ts | 2 +- .../src/e2e/local-config-switching.test.tsx | 8 +- extensions/cli/src/e2e/pipe-input-tui.test.ts | 2 +- extensions/cli/src/e2e/resume-flag.test.ts | 24 +- extensions/cli/src/e2e/spec.md | 6 +- extensions/cli/src/env.ts | 5 +- .../cli/src/environment/environmentHandler.ts | 2 +- extensions/cli/src/hooks/hookConfig.ts | 12 +- extensions/cli/src/hooks/hookRunner.ts | 2 +- extensions/cli/src/hooks/hooks.test.ts | 28 +- extensions/cli/src/hooks/types.ts | 2 +- extensions/cli/src/index.ts | 2 +- .../integration/model-persistence-e2e.test.ts | 8 +- .../model-persistence-unauthenticated.test.ts | 8 +- .../model-persistence-user-flow.test.ts | 8 +- .../src/integration/model-persistence.test.ts | 8 +- extensions/cli/src/onboarding.test.ts | 22 +- extensions/cli/src/onboarding.ts | 6 +- .../src/permissions/permissionsYamlLoader.ts | 2 +- .../cli/src/permissions/policyWriter.ts | 4 +- .../cli/src/permissions/precedenceResolver.ts | 6 +- .../cli/src/services/ApiClientService.ts | 2 +- .../services/GitAiIntegrationService.test.ts | 4 +- .../cli/src/services/StorageSyncService.ts | 2 +- .../cli/src/services/ToolPermissionService.ts | 4 +- extensions/cli/src/services/UpdateService.ts | 12 +- extensions/cli/src/session.test.ts | 2 +- extensions/cli/src/session.ts | 13 +- extensions/cli/src/slashCommands.test.ts | 8 +- extensions/cli/src/slashCommands.ts | 4 +- .../smoke-api/headless-continue-proxy.test.ts | 6 +- .../cli/src/smoke-api/smoke-api-helpers.ts | 8 +- extensions/cli/src/systemMessage.ts | 14 +- .../telemetryService.sessionMetadata.test.ts | 6 +- .../cli/src/telemetry/telemetryService.ts | 10 +- extensions/cli/src/test-helpers/README.md | 2 +- .../cli/src/test-helpers/cli-helpers.ts | 8 +- .../cli/src/test-helpers/mock-llm-server.ts | 2 +- extensions/cli/src/tools/fetch.ts | 2 +- extensions/cli/src/tools/readFile.ts | 4 +- .../cli/src/tools/runTerminalCommand.ts | 4 +- extensions/cli/src/tools/searchCode.ts | 4 +- extensions/cli/src/tools/skills.ts | 2 +- extensions/cli/src/tools/viewDiff.ts | 2 +- extensions/cli/src/ui/TUIChat.tsx | 2 +- extensions/cli/src/ui/TipsDisplay.test.tsx | 20 +- extensions/cli/src/ui/TipsDisplay.tsx | 12 +- extensions/cli/src/ui/UpdateNotification.tsx | 2 +- extensions/cli/src/ui/UpdateSelector.tsx | 2 +- extensions/cli/src/ui/__tests__/README.md | 2 +- .../src/ui/__tests__/TUIChat.testHelper.ts | 2 +- .../ui/components/ToolPermissionSelector.tsx | 2 +- extensions/cli/src/ui/hooks/useChat.ts | 2 +- .../cli/src/ui/utils/messageSplitting.ts | 2 +- extensions/cli/src/util/git.ts | 2 +- .../cli/src/util/loadMarkdownSkills.test.ts | 12 +- extensions/cli/src/util/loadMarkdownSkills.ts | 2 +- extensions/cli/src/util/stdin.test.ts | 18 +- extensions/cli/src/util/stdin.ts | 2 +- extensions/cli/src/util/uniqueId.ts | 6 +- extensions/cli/vitest.global-dir-setup.ts | 4 +- extensions/cli/vitest.setup.ts | 6 +- extensions/intellij/.gitignore | 2 +- extensions/intellij/.run/IDE Logs.run.xml | 2 +- .../.run/Start Core Dev Server.run.xml | 2 +- extensions/intellij/CONTRIBUTING.md | 2 +- extensions/intellij/README.md | 44 +- extensions/intellij/build.gradle.kts | 6 +- extensions/intellij/settings.gradle.kts | 2 +- .../actions/ContinuePluginActions.kt | 4 +- .../actions/ContinueToolbarAction.kt | 2 +- .../ContinuePluginStartupActivity.kt | 4 +- .../auth/ContinueAuthDialog.kt | 2 +- .../ContinueInlineCompletionProvider.kt | 2 +- .../constants/MessageTypes.kt | 4 +- .../constants/ServerConstants.kt | 2 +- .../ConfigJsonSchemaProviderFactory.kt | 2 +- .../ConfigRcJsonSchemaProviderFactory.kt | 10 +- .../continue/CoreMessengerManager.kt | 2 +- .../continue/Diffs.kt | 6 +- .../continue/IdeProtocolClient.kt | 2 +- .../continue/IntelliJIde.kt | 4 +- .../editor/DiffStreamHandler.kt | 4 +- .../editor/VerticalDiffBlock.kt | 4 +- .../license/AddLicenseKey.kt | 2 +- .../ContinueExtensionSettingsService.kt | 2 +- .../src/main/resources/META-INF/plugin.xml | 86 +- .../main/resources/META-INF/pluginIcon.svg | 16 +- .../resources/META-INF/pluginIcon_dark.svg | 16 +- .../src/main/resources/icons/continue.svg | 9 +- .../main/resources/icons/continue_dark.svg | 7 +- .../config.json | 0 extensions/vscode/.gitignore | 58 +- .../{.continueignore => .shadow-codeignore} | 12 +- extensions/vscode/CONTRIBUTING.md | 8 +- extensions/vscode/LICENSE.txt | 3 +- extensions/vscode/README.md | 61 +- extensions/vscode/config_schema.json | 96 +- extensions/vscode/e2e/TestUtils.ts | 2 +- .../e2e/actions/Autocomplete.actions.ts | 2 +- extensions/vscode/e2e/actions/GUI.actions.ts | 8 +- .../vscode/e2e/actions/Global.actions.ts | 8 +- .../vscode/e2e/actions/NextEdit.actions.ts | 8 +- extensions/vscode/e2e/get-latest-vsix.sh | 4 +- .../vscode/e2e/selectors/GUI.selectors.ts | 2 +- .../config.yaml | 156 +- .../config.json | 0 extensions/vscode/e2e/tests/GUI.test.ts | 4 +- extensions/vscode/media/icon.png | Bin 3826 -> 39684 bytes extensions/vscode/media/sidebar-icon.png | Bin 122935 -> 0 bytes extensions/vscode/media/sidebar-icon.svg | 6 + extensions/vscode/package-lock.json | 8 +- extensions/vscode/package.json | 470 +- extensions/vscode/rules.md | 6 +- .../vscode/scripts/generate-copy-config.js | 15 +- .../vscode/scripts/install-copy-nodemodule.js | 2 +- extensions/vscode/scripts/package.js | 2 +- .../scripts/prepackage-cross-platform.js | 2 +- extensions/vscode/scripts/prepackage.js | 6 +- extensions/vscode/scripts/utils.js | 2 +- ...ue_tutorial.py => shadow_code_tutorial.py} | 104 +- .../src/ContinueConsoleWebviewViewProvider.ts | 2 +- .../src/ContinueGUIWebviewViewProvider.ts | 2 +- .../vscode/src/activation/InlineTipManager.ts | 2 +- .../vscode/src/activation/JumpManager.ts | 10 +- .../src/activation/JumpManager.vitest.ts | 24 +- .../src/activation/NextEditWindowManager.ts | 12 +- extensions/vscode/src/activation/activate.ts | 6 +- .../RecentlyVisitedRangesService.ts | 2 +- .../src/autocomplete/completionProvider.ts | 18 +- .../vscode/src/autocomplete/statusBar.ts | 16 +- extensions/vscode/src/commands.ts | 155 +- .../vscode/src/diff/vertical/handler.ts | 2 +- .../vscode/src/diff/vertical/manager.ts | 26 +- extensions/vscode/src/extension.ts | 4 +- .../ConfigYamlDocumentLinkProvider.ts | 2 +- .../vscode/src/extension/VsCodeExtension.ts | 8 +- .../vscode/src/extension/VsCodeMessenger.ts | 10 +- .../ConfigJsonConverterCodeLensProvider.ts | 4 +- .../DownloadYamlExtensionCodeLensProvider.ts | 14 +- .../providers/QuickActionsCodeLensProvider.ts | 8 +- .../providers/SuggestionsCodeLensProvider.ts | 4 +- .../VerticalPerLineCodeLensProvider.ts | 4 +- .../src/quickEdit/EditDecorationManager.ts | 2 +- .../src/quickEdit/QuickEditQuickPick.ts | 6 +- extensions/vscode/src/stubs/SecretStorage.ts | 2 +- extensions/vscode/src/util/errorHandling.ts | 6 +- extensions/vscode/src/util/ideUtils.ts | 2 +- extensions/vscode/src/util/tutorial.ts | 2 +- extensions/vscode/src/util/util.ts | 2 +- extensions/vscode/src/util/vscode.ts | 3 +- extensions/vscode/src/util/workspaceConfig.ts | 2 +- extensions/vscode/src/webviewProtocol.ts | 6 +- extensions/vscode/vsc-extension-quickstart.md | 2 +- gui/index.html | 2 +- gui/package-lock.json | 2 +- gui/package.json | 2 +- gui/public/jetbrains_editorInset_index.html | 2 +- gui/public/jetbrains_index.html | 2 +- gui/src/App.tsx | 4 +- gui/src/components/DeprecationBanner.tsx | 2 +- gui/src/components/History/index.tsx | 4 +- .../components/OnboardingCardLanding.tsx | 4 +- .../components/config/FatalErrorNotice.tsx | 5 +- gui/src/components/dialogs/FeedbackDialog.tsx | 8 +- .../TipTapEditor/utils/getSuggestion.ts | 5 +- gui/src/components/svg/ShadowCodeLogo.tsx | 53 + gui/src/forms/AddModelForm.tsx | 15 +- ...ApprovalGate.tsx => AgentApprovalGate.tsx} | 31 +- .../pages/AddNewModel/configs/providers.ts | 12 +- .../features/indexing/IndexingProgress.tsx | 2 +- .../indexing/IndexingProgressErrorText.tsx | 6 +- .../features/keyboard/KeyboardShortcuts.tsx | 8 +- gui/src/pages/config/sections/HelpSection.tsx | 14 +- .../sections/IndexingSettingsSection.tsx | 2 +- .../pages/config/sections/ModelsSection.tsx | 19 +- .../config/sections/UserSettingsSection.tsx | 6 +- .../pages/gui/ToolCallDiv/MCPAppRenderer.tsx | 7 +- .../gui/ToolCallDiv/SubagentToolCall.test.tsx | 112 + .../gui/ToolCallDiv/SubagentToolCall.tsx | 117 + .../gui/ToolCallDiv/ToolCallStatusMessage.tsx | 2 +- gui/src/pages/gui/ToolCallDiv/index.tsx | 12 + gui/src/redux/slices/sessionSlice.ts | 20 +- gui/src/redux/thunks/callToolById.ts | 17 + gui/src/redux/thunks/cancelStream.ts | 15 + gui/src/util/errorAnalysis.ts | 2 +- gui/src/util/toolCallState.ts | 2 + packages/config-yaml/CHANGELOG.md | 4204 ++++++++--------- packages/continue-sdk/README.md | 8 +- packages/continue-sdk/python/README.md | 2 +- packages/continue-sdk/python/api/README.md | 6 +- packages/continue-sdk/typescript/README.md | 26 +- packages/llm-info/README.md | 2 +- .../src/apis/ClawRouter.test.ts | 2 +- .../openai-adapters/src/apis/ClawRouter.ts | 2 +- 539 files changed, 7224 insertions(+), 5364 deletions(-) rename .github/workflows/{run-continue-agent.yml => run-shadow-code-agent.yml} (90%) rename .github/workflows/{continue-agents.yml => shadow-code-agents.yml} (97%) rename {.continue => .shadow-code}/agents/breaking-change-detector.md (100%) rename {.continue => .shadow-code}/agents/dependency-security-review.md (100%) rename {.continue => .shadow-code}/agents/error-message-quality.md (100%) rename {.continue => .shadow-code}/agents/input-validation.md (100%) rename {.continue => .shadow-code}/agents/test-coverage.md (100%) rename {.continue => .shadow-code}/checks/anti-slop.md (100%) rename {.continue => .shadow-code}/checks/react-best-practices.md (100%) rename {.continue => .shadow-code}/checks/security-audit.md (100%) rename {.continue => .shadow-code}/checks/setup-scripts.md (100%) rename {.continue => .shadow-code}/checks/stale-comments.md (100%) rename {.continue => .shadow-code}/checks/update-agents-md.md (100%) rename {.continue => .shadow-code}/checks/update-continue-docs.md (100%) rename {.continue => .shadow-code}/environment.json (100%) rename {.continue => .shadow-code}/prompts/core-unit-test.prompt (98%) rename {.continue => .shadow-code}/prompts/sub-agent-background.md (100%) rename {.continue => .shadow-code}/prompts/sub-agent-foreground.md (100%) rename {.continue => .shadow-code}/prompts/update-llm-info.prompt (98%) rename {.continue => .shadow-code}/rules/bigger-picture-description-rules.md (100%) rename {.continue => .shadow-code}/rules/colors.md (100%) rename {.continue => .shadow-code}/rules/continue-specificity.md (100%) rename {.continue => .shadow-code}/rules/css-units.md (100%) rename {.continue => .shadow-code}/rules/dev-data-guide.md (100%) rename {.continue => .shadow-code}/rules/documentation-description-rule.md (100%) rename {.continue => .shadow-code}/rules/documentation-standards.md (100%) rename {.continue => .shadow-code}/rules/github-pr-documentation-updater.md (100%) rename {.continue => .shadow-code}/rules/gui-link-opening.md (100%) rename {.continue => .shadow-code}/rules/intellij-plugin-test-execution.md (100%) rename {.continue => .shadow-code}/rules/llm-specificity.md (100%) rename {.continue => .shadow-code}/rules/migrate-styled-components-to-tailwind.md (100%) rename {.continue => .shadow-code}/rules/mintlify-formatting.md (89%) rename {.continue => .shadow-code}/rules/navigating-responses.md (100%) rename {.continue => .shadow-code}/rules/new-protocol-message.md (100%) rename {.continue => .shadow-code}/rules/no-any-types.md (100%) rename {.continue => .shadow-code}/rules/overeager.md (100%) rename {.continue => .shadow-code}/rules/personality.md (100%) rename {.continue => .shadow-code}/rules/programming-principles.md (100%) rename {.continue => .shadow-code}/rules/pure-function-unit-tests.md (100%) rename {.continue => .shadow-code}/rules/test-running-guide.md (100%) rename {.continue => .shadow-code}/rules/typescript-enum-usage.md (100%) rename {.continue => .shadow-code}/rules/unit-testing-rules.md (100%) rename {.continue => .shadow-code}/rules/vs-code-commands-helper-functions.md (100%) rename .continueignore => .shadow-codeignore (92%) rename binary/{.continueignore => .shadow-codeignore} (100%) create mode 100644 core/agent/subagentRunner.ts create mode 100644 core/agent/subagentRunner.vitest.ts create mode 100644 core/agent/subagentSystemMessage.ts create mode 100644 core/llm/claudeCodeToolSupport.vitest.ts create mode 100644 core/llm/tokenOptimizedChat.vitest.ts create mode 100644 core/mcp/verifyConcurrentSessions.mjs create mode 100644 core/tools/definitions/spawnSubagents.ts create mode 100644 core/tools/implementations/spawnSubagents.ts rename extensions/intellij/src/testIntegration/kotlin/com/github/continuedev/continueintellijextension/{test-continue => test-shadow-code}/config.json (100%) rename extensions/vscode/{.continueignore => .shadow-codeignore} (93%) rename extensions/vscode/e2e/{test-continue-yaml => test-shadow-code-yaml}/config.yaml (94%) rename extensions/vscode/e2e/{test-continue => test-shadow-code}/config.json (100%) delete mode 100644 extensions/vscode/media/sidebar-icon.png create mode 100644 extensions/vscode/media/sidebar-icon.svg rename extensions/vscode/{continue_tutorial.py => shadow_code_tutorial.py} (89%) create mode 100644 gui/src/components/svg/ShadowCodeLogo.tsx rename gui/src/hooks/{ClaudeCodeCliApprovalGate.tsx => AgentApprovalGate.tsx} (76%) create mode 100644 gui/src/pages/gui/ToolCallDiv/SubagentToolCall.test.tsx create mode 100644 gui/src/pages/gui/ToolCallDiv/SubagentToolCall.tsx diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index d18506575bc..923d7e6e9d8 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -11,11 +11,11 @@ body: attributes: label: Before submitting your bug report options: - - label: I've tried finding an answer on the [Continue docs site](https://docs.continue.dev/) + - label: I've checked the docs for an answer required: false - - label: I'm not able to find an [open issue](https://github.com/continuedev/continue/issues?q=is%3Aopen+is%3Aissue) that reports the same bug + - label: I'm not able to find an open issue that reports the same bug required: false - - label: I've seen the [troubleshooting guide](https://docs.continue.dev/troubleshooting) on the Continue Docs + - label: I've seen the troubleshooting guide in the docs required: false - type: textarea attributes: @@ -24,20 +24,20 @@ body: Feel free to omit any info that is not relevant to your issue. - **OS**: macOS - - **Continue version**: v0.9.4 + - **Shadow Code version**: v0.9.4 - **IDE version**: VSCode 1.85.1 - Model: Claude Sonnet 4.5 - Agent configuration value: | - OS: - - Continue version: + - Shadow Code version: - IDE version: - Model: - config: ```yaml ``` - OR link to agent in Continue hub: + OR link to agent config: render: Markdown validations: required: false @@ -66,5 +66,5 @@ body: attributes: label: Log output description: | - Please refer to the [troubleshooting guide](https://docs.continue.dev/troubleshooting) in the Continue Docs for instructions on obtaining the logs. Copy either the relevant lines or the last 100 lines or so. + Please refer to the troubleshooting steps in the docs for instructions on obtaining the logs. Copy either the relevant lines or the last 100 lines or so. render: Shell diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 391b5c93f59..0086358db1e 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,8 +1 @@ -blank_issues_enabled: false -contact_links: - - name: Have a feature request? - url: https://github.com/continuedev/continue/discussions - about: Upvote existing suggestions or make a new one - - name: Have a question? - url: https://github.com/continuedev/continue/discussions - about: Search for existing questions or ask a new one +blank_issues_enabled: true diff --git a/.github/ISSUE_TEMPLATE/docs_issue_report.yml b/.github/ISSUE_TEMPLATE/docs_issue_report.yml index cef2fae0f47..6d06afbc2ee 100644 --- a/.github/ISSUE_TEMPLATE/docs_issue_report.yml +++ b/.github/ISSUE_TEMPLATE/docs_issue_report.yml @@ -11,10 +11,7 @@ body: We appreciate your feedback. Before submitting, please check if a similar issue already exists. ### Quick Contribution Tips: - - Click "Edit this page" at the bottom of any page on [docs.continue.dev](https://docs.continue.dev) to contribute directly. - - For local development, see [CONTRIBUTING.md](https://github.com/continuedev/continue/blob/main/CONTRIBUTING.md#-updating--improving-documentation). - - - type: dropdown + - type: dropdown id: doc-issue-category attributes: label: Issue Category @@ -34,7 +31,7 @@ body: attributes: label: Affected Documentation Page URL description: Provide the URL of the specific page where you encountered the issue. - placeholder: "https://docs.continue.dev/path/to/page" + placeholder: "path/to/page" validations: required: false diff --git a/.github/actions/build-vscode-extension/action.yml b/.github/actions/build-vscode-extension/action.yml index c09479697ed..b8187436a42 100644 --- a/.github/actions/build-vscode-extension/action.yml +++ b/.github/actions/build-vscode-extension/action.yml @@ -112,10 +112,10 @@ runs: - name: Prepackage the extension shell: bash env: - CONTINUE_VSCODE_TARGET: ${{ steps.set-target.outputs.target }} + SHADOW_CODE_VSCODE_TARGET: ${{ steps.set-target.outputs.target }} run: | cd extensions/vscode - export CONTINUE_VSCODE_TARGET="${{ steps.set-target.outputs.target }}" + export SHADOW_CODE_VSCODE_TARGET="${{ steps.set-target.outputs.target }}" npm run prepackage -- --target ${{ steps.set-target.outputs.target }} - name: Re-install esbuild @@ -150,19 +150,19 @@ runs: - name: Package extension (build artifacts) shell: bash env: - CONTINUE_VSCODE_TARGET: ${{ steps.set-target.outputs.target }} + SHADOW_CODE_VSCODE_TARGET: ${{ steps.set-target.outputs.target }} run: | cd extensions/vscode - export CONTINUE_VSCODE_TARGET="${{ steps.set-target.outputs.target }}" + export SHADOW_CODE_VSCODE_TARGET="${{ steps.set-target.outputs.target }}" npm run package -- --target ${{ steps.set-target.outputs.target }} - name: Package extension (.vsix files) shell: bash env: - CONTINUE_VSCODE_TARGET: ${{ steps.set-target.outputs.target }} + SHADOW_CODE_VSCODE_TARGET: ${{ steps.set-target.outputs.target }} run: | cd extensions/vscode - export CONTINUE_VSCODE_TARGET="${{ steps.set-target.outputs.target }}" + export SHADOW_CODE_VSCODE_TARGET="${{ steps.set-target.outputs.target }}" if [ "${{ inputs.pre-release }}" = "true" ]; then npx vsce package --pre-release --no-dependencies --target ${{ steps.set-target.outputs.target }} else diff --git a/.github/actions/run-vscode-e2e-test/action.yml b/.github/actions/run-vscode-e2e-test/action.yml index 73734eb1ca4..7b53bb71952 100644 --- a/.github/actions/run-vscode-e2e-test/action.yml +++ b/.github/actions/run-vscode-e2e-test/action.yml @@ -35,7 +35,7 @@ runs: path: extensions/vscode/node_modules key: ${{ runner.os }}-vscode-node-modules-${{ hashFiles('extensions/vscode/package-lock.json') }} - # We don't want to cache the Continue extension, so it is deleted at the end of the job + # We don't want to cache the Shadow Code extension, so it is deleted at the end of the job - uses: actions/cache@v4 id: test-extensions-cache with: @@ -104,11 +104,11 @@ runs: env: DISPLAY: :99 - - name: Delete continue from test extensions + - name: Delete Shadow Code from test extensions shell: bash run: | cd extensions/vscode - rm -rf e2e/.test-extensions/continue* + rm -rf e2e/.test-extensions/shadow-code* - name: Sanitize test file name id: sanitize_filename diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 281f61c1f43..053fd2e5bc2 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -2,20 +2,15 @@ [ What changed? Feel free to be brief. ] -## AI Code Review - -- **Team members only**: AI review runs automatically when PR is opened or marked ready for review -- Team members can also trigger a review by commenting `@continue-review` - ## Checklist -- [] I've read the [contributing guide](https://github.com/continuedev/continue/blob/main/CONTRIBUTING.md) +- [] I've read the contributing guide - [] The relevant docs, if any, have been updated or created - [] The relevant tests, if any, have been updated or created ## Screen recording or screenshot -[ When applicable, please include a short screen recording or screenshot - this makes it much easier for us as contributors to review and understand your changes. See [this PR](https://github.com/continuedev/continue/pull/6455) as a good example. ] +[ When applicable, please include a short screen recording or screenshot - this makes it much easier to review and understand your changes. ] ## Tests diff --git a/.github/workflows/auto-fix-failed-tests.yml b/.github/workflows/auto-fix-failed-tests.yml index fdfa8b95bd3..9ff03f74554 100644 --- a/.github/workflows/auto-fix-failed-tests.yml +++ b/.github/workflows/auto-fix-failed-tests.yml @@ -109,7 +109,7 @@ jobs: if: steps.workflow-details.outputs.has_failed_tests == 'true' id: remote-session env: - CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} + SHADOW_CODE_API_KEY: ${{ secrets.SHADOW_CODE_API_KEY }} run: | # Create a detailed prompt for fixing the failed tests cat > /tmp/fix_tests_prompt.txt << 'PROMPT_EOF' diff --git a/.github/workflows/cli-pr-checks.yml b/.github/workflows/cli-pr-checks.yml index f60a028821a..f359f85ec7b 100644 --- a/.github/workflows/cli-pr-checks.yml +++ b/.github/workflows/cli-pr-checks.yml @@ -160,7 +160,7 @@ jobs: - name: Run smoke API tests env: ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} + SHADOW_CODE_API_KEY: ${{ secrets.SHADOW_CODE_API_KEY }} run: | cd extensions/cli npm run test:smoke-api diff --git a/.github/workflows/run-continue-agent.yml b/.github/workflows/run-shadow-code-agent.yml similarity index 90% rename from .github/workflows/run-continue-agent.yml rename to .github/workflows/run-shadow-code-agent.yml index d208c09d684..eb10b21b853 100644 --- a/.github/workflows/run-continue-agent.yml +++ b/.github/workflows/run-shadow-code-agent.yml @@ -19,7 +19,7 @@ on: type: string default: "main" secrets: - CONTINUE_API_KEY: + SHADOW_CODE_API_KEY: required: true jobs: @@ -33,7 +33,7 @@ jobs: AGENT: ${{ inputs.agent }} BRANCH_NAME: ${{ inputs.branch_name }} REPO_URL: https://github.com/${{ github.repository }} - CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} + SHADOW_CODE_API_KEY: ${{ secrets.SHADOW_CODE_API_KEY }} run: | # Use jq to properly construct JSON with safe escaping json_body=$(jq -n \ @@ -45,7 +45,7 @@ jobs: response=$(curl -f -X POST https://api.continue.dev/agents \ -H "Content-Type: application/json" \ - -H "Authorization: Bearer $CONTINUE_API_KEY" \ + -H "Authorization: Bearer $SHADOW_CODE_API_KEY" \ -d "$json_body") id=$(echo $response | jq -r '.id') echo "https://continue.dev/hub?type=agents/$id" diff --git a/.github/workflows/continue-agents.yml b/.github/workflows/shadow-code-agents.yml similarity index 97% rename from .github/workflows/continue-agents.yml rename to .github/workflows/shadow-code-agents.yml index fd3fc42976c..5271c99175e 100644 --- a/.github/workflows/continue-agents.yml +++ b/.github/workflows/shadow-code-agents.yml @@ -1,178 +1,178 @@ -name: Continue Agents - -on: - workflow_call: - inputs: - agents-path: - description: 'Path to agents folder' - required: false - default: '.continue/agents' - type: string - secrets: - ANTHROPIC_API_KEY: - description: 'Anthropic API key for Claude' - required: true - -permissions: - contents: write - checks: write - pull-requests: write - -jobs: - discover: - runs-on: ubuntu-latest - outputs: - matrix: ${{ steps.discover.outputs.matrix }} - has-agents: ${{ steps.discover.outputs.has_agents }} - steps: - - uses: actions/checkout@v6 - - - name: Discover agents - id: discover - run: | - AGENTS_DIR="${{ inputs.agents-path }}" - if [ -d "$AGENTS_DIR" ]; then - # Use -sc for compact single-line JSON (required for GitHub Actions output) - FILES=$(find "$AGENTS_DIR" -name "*.md" -type f 2>/dev/null | jq -R . | jq -sc .) - COUNT=$(echo "$FILES" | jq 'length') - HAS_AGENTS=$([[ $COUNT -gt 0 ]] && echo "true" || echo "false") - else - FILES="[]" - COUNT=0 - HAS_AGENTS="false" - fi - - echo "matrix=$FILES" >> $GITHUB_OUTPUT - echo "has_agents=$HAS_AGENTS" >> $GITHUB_OUTPUT - echo "Found $COUNT agent(s)" - - run-agent: - needs: discover - if: needs.discover.outputs.has-agents == 'true' - runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - agent: ${{ fromJson(needs.discover.outputs.matrix) }} - steps: - - uses: actions/checkout@v6 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: '20' - - - name: Install Continue CLI - run: npm i -g @continuedev/cli - - - name: Extract agent name - id: agent-name - run: | - AGENT_FILE="${{ matrix.agent }}" - AGENT_NAME=$(basename "$AGENT_FILE" .md) - echo "name=$AGENT_NAME" >> $GITHUB_OUTPUT - - - name: Create Check Run - id: check - uses: actions/github-script@v8 - env: - AGENT_NAME: ${{ steps.agent-name.outputs.name }} - with: - script: | - const { data: check } = await github.rest.checks.create({ - owner: context.repo.owner, - repo: context.repo.repo, - name: `Continue: ${process.env.AGENT_NAME}`, - head_sha: context.sha, - status: 'in_progress', - started_at: new Date().toISOString(), - }); - core.setOutput('id', check.id); - - - name: Run agent - id: run - env: - ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} - GH_TOKEN: ${{ github.token }} - run: | - AGENT_FILE="${{ matrix.agent }}" - - # Run agent in non-interactive mode (-p flag) - if OUTPUT=$(cn -p --agent "$AGENT_FILE" 2>&1); then - echo "success=true" >> $GITHUB_OUTPUT - echo "output<> $GITHUB_OUTPUT - echo "$OUTPUT" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - echo "✅ Agent completed successfully" - echo "" - echo "--- Agent Output ---" - echo "$OUTPUT" - else - echo "success=false" >> $GITHUB_OUTPUT - echo "error<> $GITHUB_OUTPUT - echo "$OUTPUT" >> $GITHUB_OUTPUT - echo "EOF" >> $GITHUB_OUTPUT - echo "❌ Agent failed" - echo "" - echo "--- Agent Output ---" - echo "$OUTPUT" - fi - - - name: Write job summary - if: always() - env: - AGENT_OUTPUT: ${{ steps.run.outputs.output }} - AGENT_ERROR: ${{ steps.run.outputs.error }} - AGENT_SUCCESS: ${{ steps.run.outputs.success }} - AGENT_NAME: ${{ steps.agent-name.outputs.name }} - run: | - echo "## 🤖 Agent: $AGENT_NAME" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - if [ "$AGENT_SUCCESS" == "true" ]; then - echo "✅ **Status:** Completed successfully" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Output" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - printf '%s\n' "$AGENT_OUTPUT" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - else - echo "❌ **Status:** Failed" >> $GITHUB_STEP_SUMMARY - echo "" >> $GITHUB_STEP_SUMMARY - echo "### Error" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - printf '%s\n' "$AGENT_ERROR" >> $GITHUB_STEP_SUMMARY - echo '```' >> $GITHUB_STEP_SUMMARY - fi - - - name: Fail if agent failed - if: steps.run.outputs.success != 'true' - run: exit 1 - - - name: Update Check Run - if: always() - uses: actions/github-script@v8 - env: - AGENT_OUTPUT: ${{ steps.run.outputs.output }} - AGENT_ERROR: ${{ steps.run.outputs.error }} - AGENT_SUCCESS: ${{ steps.run.outputs.success }} - CHECK_RUN_ID: ${{ steps.check.outputs.id }} - with: - script: | - const success = process.env.AGENT_SUCCESS === 'true'; - const output = process.env.AGENT_OUTPUT || ''; - const error = process.env.AGENT_ERROR || ''; - - await github.rest.checks.update({ - owner: context.repo.owner, - repo: context.repo.repo, - check_run_id: parseInt(process.env.CHECK_RUN_ID, 10), - status: 'completed', - conclusion: success ? 'success' : 'failure', - completed_at: new Date().toISOString(), - output: { - title: success ? 'Agent completed' : 'Agent failed', - summary: success - ? `Agent completed successfully.\n\n
Output\n\n\`\`\`\n${output.slice(0, 60000)}\n\`\`\`\n
` - : `Agent failed.\n\n\`\`\`\n${error.slice(0, 60000)}\n\`\`\``, - }, - }); +name: Continue Agents + +on: + workflow_call: + inputs: + agents-path: + description: 'Path to agents folder' + required: false + default: '.continue/agents' + type: string + secrets: + ANTHROPIC_API_KEY: + description: 'Anthropic API key for Claude' + required: true + +permissions: + contents: write + checks: write + pull-requests: write + +jobs: + discover: + runs-on: ubuntu-latest + outputs: + matrix: ${{ steps.discover.outputs.matrix }} + has-agents: ${{ steps.discover.outputs.has_agents }} + steps: + - uses: actions/checkout@v6 + + - name: Discover agents + id: discover + run: | + AGENTS_DIR="${{ inputs.agents-path }}" + if [ -d "$AGENTS_DIR" ]; then + # Use -sc for compact single-line JSON (required for GitHub Actions output) + FILES=$(find "$AGENTS_DIR" -name "*.md" -type f 2>/dev/null | jq -R . | jq -sc .) + COUNT=$(echo "$FILES" | jq 'length') + HAS_AGENTS=$([[ $COUNT -gt 0 ]] && echo "true" || echo "false") + else + FILES="[]" + COUNT=0 + HAS_AGENTS="false" + fi + + echo "matrix=$FILES" >> $GITHUB_OUTPUT + echo "has_agents=$HAS_AGENTS" >> $GITHUB_OUTPUT + echo "Found $COUNT agent(s)" + + run-agent: + needs: discover + if: needs.discover.outputs.has-agents == 'true' + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + agent: ${{ fromJson(needs.discover.outputs.matrix) }} + steps: + - uses: actions/checkout@v6 + + - name: Setup Node.js + uses: actions/setup-node@v6 + with: + node-version: '20' + + - name: Install Continue CLI + run: npm i -g @continuedev/cli + + - name: Extract agent name + id: agent-name + run: | + AGENT_FILE="${{ matrix.agent }}" + AGENT_NAME=$(basename "$AGENT_FILE" .md) + echo "name=$AGENT_NAME" >> $GITHUB_OUTPUT + + - name: Create Check Run + id: check + uses: actions/github-script@v8 + env: + AGENT_NAME: ${{ steps.agent-name.outputs.name }} + with: + script: | + const { data: check } = await github.rest.checks.create({ + owner: context.repo.owner, + repo: context.repo.repo, + name: `Continue: ${process.env.AGENT_NAME}`, + head_sha: context.sha, + status: 'in_progress', + started_at: new Date().toISOString(), + }); + core.setOutput('id', check.id); + + - name: Run agent + id: run + env: + ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} + GH_TOKEN: ${{ github.token }} + run: | + AGENT_FILE="${{ matrix.agent }}" + + # Run agent in non-interactive mode (-p flag) + if OUTPUT=$(cn -p --agent "$AGENT_FILE" 2>&1); then + echo "success=true" >> $GITHUB_OUTPUT + echo "output<> $GITHUB_OUTPUT + echo "$OUTPUT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "✅ Agent completed successfully" + echo "" + echo "--- Agent Output ---" + echo "$OUTPUT" + else + echo "success=false" >> $GITHUB_OUTPUT + echo "error<> $GITHUB_OUTPUT + echo "$OUTPUT" >> $GITHUB_OUTPUT + echo "EOF" >> $GITHUB_OUTPUT + echo "❌ Agent failed" + echo "" + echo "--- Agent Output ---" + echo "$OUTPUT" + fi + + - name: Write job summary + if: always() + env: + AGENT_OUTPUT: ${{ steps.run.outputs.output }} + AGENT_ERROR: ${{ steps.run.outputs.error }} + AGENT_SUCCESS: ${{ steps.run.outputs.success }} + AGENT_NAME: ${{ steps.agent-name.outputs.name }} + run: | + echo "## 🤖 Agent: $AGENT_NAME" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + if [ "$AGENT_SUCCESS" == "true" ]; then + echo "✅ **Status:** Completed successfully" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Output" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + printf '%s\n' "$AGENT_OUTPUT" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Status:** Failed" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "### Error" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + printf '%s\n' "$AGENT_ERROR" >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + fi + + - name: Fail if agent failed + if: steps.run.outputs.success != 'true' + run: exit 1 + + - name: Update Check Run + if: always() + uses: actions/github-script@v8 + env: + AGENT_OUTPUT: ${{ steps.run.outputs.output }} + AGENT_ERROR: ${{ steps.run.outputs.error }} + AGENT_SUCCESS: ${{ steps.run.outputs.success }} + CHECK_RUN_ID: ${{ steps.check.outputs.id }} + with: + script: | + const success = process.env.AGENT_SUCCESS === 'true'; + const output = process.env.AGENT_OUTPUT || ''; + const error = process.env.AGENT_ERROR || ''; + + await github.rest.checks.update({ + owner: context.repo.owner, + repo: context.repo.repo, + check_run_id: parseInt(process.env.CHECK_RUN_ID, 10), + status: 'completed', + conclusion: success ? 'success' : 'failure', + completed_at: new Date().toISOString(), + output: { + title: success ? 'Agent completed' : 'Agent failed', + summary: success + ? `Agent completed successfully.\n\n
Output\n\n\`\`\`\n${output.slice(0, 60000)}\n\`\`\`\n
` + : `Agent failed.\n\n\`\`\`\n${error.slice(0, 60000)}\n\`\`\``, + }, + }); diff --git a/.github/workflows/snyk-agent.yaml b/.github/workflows/snyk-agent.yaml index 60bc09cab7a..3a9b3447c2c 100644 --- a/.github/workflows/snyk-agent.yaml +++ b/.github/workflows/snyk-agent.yaml @@ -30,4 +30,4 @@ jobs: run: cd extensions/cli && cn -p --agent continuedev/snyk-code-scan-agent "The current directory" env: SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }} - CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} + SHADOW_CODE_API_KEY: ${{ secrets.SHADOW_CODE_API_KEY }} diff --git a/.github/workflows/tidy-up-codebase.yml b/.github/workflows/tidy-up-codebase.yml index bd490022a55..232fd03d6c8 100644 --- a/.github/workflows/tidy-up-codebase.yml +++ b/.github/workflows/tidy-up-codebase.yml @@ -10,9 +10,9 @@ jobs: run-cn-task: # Only run this workflow on the main repository (continuedev/continue) if: github.repository == 'continuedev/continue' - uses: ./.github/workflows/run-continue-agent.yml + uses: ./.github/workflows/run-shadow-code-agent.yml with: agent: continuedev/tidy-up-markdown-agent branch_name: main secrets: - CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} + SHADOW_CODE_API_KEY: ${{ secrets.SHADOW_CODE_API_KEY }} diff --git a/.gitignore b/.gitignore index 60305a04b66..fa626c94c43 100644 --- a/.gitignore +++ b/.gitignore @@ -143,7 +143,7 @@ continue_server.dist Icon Icon? -.continuerc.json +.shadow-coderc.json .aider* *.notes.md @@ -151,7 +151,7 @@ notes.md *.notes.md manual-testing-sandbox/.idea/** -manual-testing-sandbox/.continue/** +manual-testing-sandbox/.shadow-code/** extensions/intellij/.idea/** **/.idea/workspace.xml @@ -163,6 +163,7 @@ extensions/intellij/.idea/** extensions/intellij/bin +extensions/.shadow-code-debug/ extensions/.continue-debug/ *.vsix @@ -170,8 +171,8 @@ extensions/.continue-debug/ # intellij module library files *.iml -.continuerules -**/.continue/assistants/ +.shadow-coderules +**/.shadow-code/assistants/ keys .channels_cache.json diff --git a/.prettierignore b/.prettierignore index 7b9235d9f3b..8c335692c0d 100644 --- a/.prettierignore +++ b/.prettierignore @@ -5,8 +5,8 @@ binary/tmp core/.continue-test docs/.docusaurus docs/**/*.mdx -extensions/.continue-debug -extensions/vscode/continue_rc_schema.json +extensions/.shadow-code-debug +extensions/vscode/shadow_code_rc_schema.json extensions/vscode/.vscode-test extensions/vscode/bin extensions/vscode/build diff --git a/.continue/agents/breaking-change-detector.md b/.shadow-code/agents/breaking-change-detector.md similarity index 100% rename from .continue/agents/breaking-change-detector.md rename to .shadow-code/agents/breaking-change-detector.md diff --git a/.continue/agents/dependency-security-review.md b/.shadow-code/agents/dependency-security-review.md similarity index 100% rename from .continue/agents/dependency-security-review.md rename to .shadow-code/agents/dependency-security-review.md diff --git a/.continue/agents/error-message-quality.md b/.shadow-code/agents/error-message-quality.md similarity index 100% rename from .continue/agents/error-message-quality.md rename to .shadow-code/agents/error-message-quality.md diff --git a/.continue/agents/input-validation.md b/.shadow-code/agents/input-validation.md similarity index 100% rename from .continue/agents/input-validation.md rename to .shadow-code/agents/input-validation.md diff --git a/.continue/agents/test-coverage.md b/.shadow-code/agents/test-coverage.md similarity index 100% rename from .continue/agents/test-coverage.md rename to .shadow-code/agents/test-coverage.md diff --git a/.continue/checks/anti-slop.md b/.shadow-code/checks/anti-slop.md similarity index 100% rename from .continue/checks/anti-slop.md rename to .shadow-code/checks/anti-slop.md diff --git a/.continue/checks/react-best-practices.md b/.shadow-code/checks/react-best-practices.md similarity index 100% rename from .continue/checks/react-best-practices.md rename to .shadow-code/checks/react-best-practices.md diff --git a/.continue/checks/security-audit.md b/.shadow-code/checks/security-audit.md similarity index 100% rename from .continue/checks/security-audit.md rename to .shadow-code/checks/security-audit.md diff --git a/.continue/checks/setup-scripts.md b/.shadow-code/checks/setup-scripts.md similarity index 100% rename from .continue/checks/setup-scripts.md rename to .shadow-code/checks/setup-scripts.md diff --git a/.continue/checks/stale-comments.md b/.shadow-code/checks/stale-comments.md similarity index 100% rename from .continue/checks/stale-comments.md rename to .shadow-code/checks/stale-comments.md diff --git a/.continue/checks/update-agents-md.md b/.shadow-code/checks/update-agents-md.md similarity index 100% rename from .continue/checks/update-agents-md.md rename to .shadow-code/checks/update-agents-md.md diff --git a/.continue/checks/update-continue-docs.md b/.shadow-code/checks/update-continue-docs.md similarity index 100% rename from .continue/checks/update-continue-docs.md rename to .shadow-code/checks/update-continue-docs.md diff --git a/.continue/environment.json b/.shadow-code/environment.json similarity index 100% rename from .continue/environment.json rename to .shadow-code/environment.json diff --git a/.continue/prompts/core-unit-test.prompt b/.shadow-code/prompts/core-unit-test.prompt similarity index 98% rename from .continue/prompts/core-unit-test.prompt rename to .shadow-code/prompts/core-unit-test.prompt index abf04b1b659..3ac040e6770 100644 --- a/.continue/prompts/core-unit-test.prompt +++ b/.shadow-code/prompts/core-unit-test.prompt @@ -1,39 +1,39 @@ -name: Write Core Unit Test -description: Generate unit tests for core utilities ---- -Write jest tests for the provided code. -Use jest version ^29 (e.g. jest 29.7.0) - -Use best practices. Be clear and concise. -Aim for 100% code coverage where reasonable. -Multiple tests can be written, split up tests for best clarity and readability. -Only use typescript, and if the file/code is not typescript, warn the user. -IMPORTANT Use ESM to import modules, do NOT use `require` anywhere -Tests are to be described in an adjacent file with a path identical except for a `.test.ts` rather than a `.ts` file extension -Use double quotes (or backticks if needed) for strings - -The code being tested is used in IDE extensions, and it: -- accesses code workspaces through the IDE ("workspace directories") -- persists extension-related data to the the local machine of the user ("global directory"), and -- uses configuration via a `ConfigHandler`, which is stored in the global directory - -Jest testing setup includes -- @core/test/jest.global-setup.ts initializes a temporary global directory, which is where files that store persisted extension data live. -- @core/test/testDir.ts provides utils for creating and working with the temporary workspace directory. Use `setUpTestDir` and `tearDownTestDir` explicitly in tests that work with workspace files -- @core/test/jest.setup-after-env.ts gives tests access to node and jest globals - -@core/test/fixtures.ts provides fixtures that should be used in tests to emulate extension behavior -- import `testIde` for IDE/workspace operations -- import `testConfigHandler` for any ConfigHandler needs -- import `ideSettingsPromise` for any IdeSettings needs -- import `testLLM` for any ILLM/BaseLLM needs. Set the `completion` property to the desired completion, e.g. `testLLM.completion = "Desired completion";` - -Do NOT write tests for any files in `core/test`, only use them as helpers for testing other files. If no other files are provided, warn the user and write no tests. - -IMPORTANT: Do NOT mock the fixtures above other than using `jest.spyOn`. DO mock 3rd party modules, etc. when sensible. -Instead, generate actual mock files and data for operations -Pure mocks should only be used to emulate specific network responses/error or hard-to-duplicate errors, or to prevent long-duration tests - -Additional types can be imported from @core/index.d.ts. If any needed types, functions, constants, or classes are still not found, warn the user and do not generate tests. - -Write the comment "// Generated by continue" at the top of the generated code/file (not the filepath) +name: Write Core Unit Test +description: Generate unit tests for core utilities +--- +Write jest tests for the provided code. +Use jest version ^29 (e.g. jest 29.7.0) + +Use best practices. Be clear and concise. +Aim for 100% code coverage where reasonable. +Multiple tests can be written, split up tests for best clarity and readability. +Only use typescript, and if the file/code is not typescript, warn the user. +IMPORTANT Use ESM to import modules, do NOT use `require` anywhere +Tests are to be described in an adjacent file with a path identical except for a `.test.ts` rather than a `.ts` file extension +Use double quotes (or backticks if needed) for strings + +The code being tested is used in IDE extensions, and it: +- accesses code workspaces through the IDE ("workspace directories") +- persists extension-related data to the the local machine of the user ("global directory"), and +- uses configuration via a `ConfigHandler`, which is stored in the global directory + +Jest testing setup includes +- @core/test/jest.global-setup.ts initializes a temporary global directory, which is where files that store persisted extension data live. +- @core/test/testDir.ts provides utils for creating and working with the temporary workspace directory. Use `setUpTestDir` and `tearDownTestDir` explicitly in tests that work with workspace files +- @core/test/jest.setup-after-env.ts gives tests access to node and jest globals + +@core/test/fixtures.ts provides fixtures that should be used in tests to emulate extension behavior +- import `testIde` for IDE/workspace operations +- import `testConfigHandler` for any ConfigHandler needs +- import `ideSettingsPromise` for any IdeSettings needs +- import `testLLM` for any ILLM/BaseLLM needs. Set the `completion` property to the desired completion, e.g. `testLLM.completion = "Desired completion";` + +Do NOT write tests for any files in `core/test`, only use them as helpers for testing other files. If no other files are provided, warn the user and write no tests. + +IMPORTANT: Do NOT mock the fixtures above other than using `jest.spyOn`. DO mock 3rd party modules, etc. when sensible. +Instead, generate actual mock files and data for operations +Pure mocks should only be used to emulate specific network responses/error or hard-to-duplicate errors, or to prevent long-duration tests + +Additional types can be imported from @core/index.d.ts. If any needed types, functions, constants, or classes are still not found, warn the user and do not generate tests. + +Write the comment "// Generated by continue" at the top of the generated code/file (not the filepath) diff --git a/.continue/prompts/sub-agent-background.md b/.shadow-code/prompts/sub-agent-background.md similarity index 100% rename from .continue/prompts/sub-agent-background.md rename to .shadow-code/prompts/sub-agent-background.md diff --git a/.continue/prompts/sub-agent-foreground.md b/.shadow-code/prompts/sub-agent-foreground.md similarity index 100% rename from .continue/prompts/sub-agent-foreground.md rename to .shadow-code/prompts/sub-agent-foreground.md diff --git a/.continue/prompts/update-llm-info.prompt b/.shadow-code/prompts/update-llm-info.prompt similarity index 98% rename from .continue/prompts/update-llm-info.prompt rename to .shadow-code/prompts/update-llm-info.prompt index 07c9b1b62fb..82128d33efc 100644 --- a/.continue/prompts/update-llm-info.prompt +++ b/.shadow-code/prompts/update-llm-info.prompt @@ -1,79 +1,79 @@ -name: Update LLM Info -description: Updates Gemini blocks with latest information -version: 2 ---- -I need you to update this repo with the latest information about large language models from certain providers. -The information is stored in the `packages/llm-info` directory, which operates as its own npm package. -Information is stored in a simple JSON format, with one file of models per "provider" (e.g. gemini, ollama, openai, etc). -For example, Gemini model information is stored in `packages/llm-info/src/providers/gemini.ts`. - -To make these updates for a given provider, perform the following steps one at a time: - -1. VIEW CURRENT INFORMATION - -To view the current llm info, read the file (e.g. `packages/llm-info/src/providers/gemini.ts`) using the read_file tool. -If you do not see a tool for reading files, let me know. - -For reference, here is the LLMInfo interface used to store the information: - -```typescript -export interface LlmInfo { - model: string; // the model name used to distinguish the model at the API layer, e.g. "gemini-2.5-pro-preview-05-06" - displayName?: string; // A fitting display name, e.g. "Gemini 2.5 Pro Preview" - description?: string; // A short description of the model, as similar as possible to descriptions found on the provider's website - contextLength?: number; // The size of the context window in tokens, often called "max input tokens" or "context length" - maxCompletionTokens?: number; // The maximum number of tokens the model can output - regex?: RegExp; // A regex expression to uniquely match the model name if it had some slight modifications/upgrades, e.g. /gemini-2\.5-pro-preview/i - mediaTypes?: MediaType[]; // Input/output types supported by the model, e.g. [MediaType.Text, MediaType.Image] -} - -export enum MediaType { - Text = "text", - Image = "image", - Audio = "audio", - Video = "video", -} - -export const AllMediaTypes = [ - MediaType.Text, - MediaType.Image, - MediaType.Audio, - MediaType.Video, -]; -``` - -2. EXTRACT NEW INFORMATION - -Fetch information from the internet using the search web and read url tools. -If you do not see either of these tools, stop and let me know. - -Retrieve the following sites per these providers, and consider these notes on how to extract the information: -- "gemini": https://ai.google.dev/gemini-api/docs/models - maxCompletionTokens is called "Output token limit", contextLength is called "Input token limit" -// TODO: add info for openai, ollama, etc - -For any other providers, search the web for "latest [provider] models" and find the most relevant model information using your best judgement. -Generally, ignore models that do not support text output. These models will be used in a coding agent client that only supports text/image input and text/output, so extraneous capabilities can be ignored. -Sometimes, "Experimental", "Preview", or similar versions of models are available. The latest-date version of each should have its own information maintained. - -3. UPDATE THE INFORMATION - -Use the edit file tool to submit updates to the relevant provider file (e.g. `packages/llm-info/src/providers/gemini.ts`). -Let me know if you do not see a tool for editing files. - -Generally, avoid making changes to existing information, as it is probably up to date. -Primarily, focus on adding new models or adding missing information to existing models. -NEVER delete any existing models, but let me know if you think one should be removed. -Stop to ask me if you are unsure about any changes, and let me know if there are updates that seem nuanced, complex, or extremely notable. -Provide a summary of changes after the fact. - -4. ASK IF I WANT TO PUBLISH TO NPM -After you are done updating everything, remind me that I need to publish the changes to npm. - -// TODO too sensitive for now -// If yes, update `packages/llm-info/package.json` with the new version number and then -// - use the terminal tool to commit the changes -// - use the terminal tool to publish changes to npm -// If you do not see a tool for running terminal commands, let me know. - -// User input -Please update LLM Info for the following providers. If "all", update all providers mentioned above: +name: Update LLM Info +description: Updates Gemini blocks with latest information +version: 2 +--- +I need you to update this repo with the latest information about large language models from certain providers. +The information is stored in the `packages/llm-info` directory, which operates as its own npm package. +Information is stored in a simple JSON format, with one file of models per "provider" (e.g. gemini, ollama, openai, etc). +For example, Gemini model information is stored in `packages/llm-info/src/providers/gemini.ts`. + +To make these updates for a given provider, perform the following steps one at a time: + +1. VIEW CURRENT INFORMATION + +To view the current llm info, read the file (e.g. `packages/llm-info/src/providers/gemini.ts`) using the read_file tool. +If you do not see a tool for reading files, let me know. + +For reference, here is the LLMInfo interface used to store the information: + +```typescript +export interface LlmInfo { + model: string; // the model name used to distinguish the model at the API layer, e.g. "gemini-2.5-pro-preview-05-06" + displayName?: string; // A fitting display name, e.g. "Gemini 2.5 Pro Preview" + description?: string; // A short description of the model, as similar as possible to descriptions found on the provider's website + contextLength?: number; // The size of the context window in tokens, often called "max input tokens" or "context length" + maxCompletionTokens?: number; // The maximum number of tokens the model can output + regex?: RegExp; // A regex expression to uniquely match the model name if it had some slight modifications/upgrades, e.g. /gemini-2\.5-pro-preview/i + mediaTypes?: MediaType[]; // Input/output types supported by the model, e.g. [MediaType.Text, MediaType.Image] +} + +export enum MediaType { + Text = "text", + Image = "image", + Audio = "audio", + Video = "video", +} + +export const AllMediaTypes = [ + MediaType.Text, + MediaType.Image, + MediaType.Audio, + MediaType.Video, +]; +``` + +2. EXTRACT NEW INFORMATION + +Fetch information from the internet using the search web and read url tools. +If you do not see either of these tools, stop and let me know. + +Retrieve the following sites per these providers, and consider these notes on how to extract the information: +- "gemini": https://ai.google.dev/gemini-api/docs/models - maxCompletionTokens is called "Output token limit", contextLength is called "Input token limit" +// TODO: add info for openai, ollama, etc + +For any other providers, search the web for "latest [provider] models" and find the most relevant model information using your best judgement. +Generally, ignore models that do not support text output. These models will be used in a coding agent client that only supports text/image input and text/output, so extraneous capabilities can be ignored. +Sometimes, "Experimental", "Preview", or similar versions of models are available. The latest-date version of each should have its own information maintained. + +3. UPDATE THE INFORMATION + +Use the edit file tool to submit updates to the relevant provider file (e.g. `packages/llm-info/src/providers/gemini.ts`). +Let me know if you do not see a tool for editing files. + +Generally, avoid making changes to existing information, as it is probably up to date. +Primarily, focus on adding new models or adding missing information to existing models. +NEVER delete any existing models, but let me know if you think one should be removed. +Stop to ask me if you are unsure about any changes, and let me know if there are updates that seem nuanced, complex, or extremely notable. +Provide a summary of changes after the fact. + +4. ASK IF I WANT TO PUBLISH TO NPM +After you are done updating everything, remind me that I need to publish the changes to npm. + +// TODO too sensitive for now +// If yes, update `packages/llm-info/package.json` with the new version number and then +// - use the terminal tool to commit the changes +// - use the terminal tool to publish changes to npm +// If you do not see a tool for running terminal commands, let me know. + +// User input +Please update LLM Info for the following providers. If "all", update all providers mentioned above: diff --git a/.continue/rules/bigger-picture-description-rules.md b/.shadow-code/rules/bigger-picture-description-rules.md similarity index 100% rename from .continue/rules/bigger-picture-description-rules.md rename to .shadow-code/rules/bigger-picture-description-rules.md diff --git a/.continue/rules/colors.md b/.shadow-code/rules/colors.md similarity index 100% rename from .continue/rules/colors.md rename to .shadow-code/rules/colors.md diff --git a/.continue/rules/continue-specificity.md b/.shadow-code/rules/continue-specificity.md similarity index 100% rename from .continue/rules/continue-specificity.md rename to .shadow-code/rules/continue-specificity.md diff --git a/.continue/rules/css-units.md b/.shadow-code/rules/css-units.md similarity index 100% rename from .continue/rules/css-units.md rename to .shadow-code/rules/css-units.md diff --git a/.continue/rules/dev-data-guide.md b/.shadow-code/rules/dev-data-guide.md similarity index 100% rename from .continue/rules/dev-data-guide.md rename to .shadow-code/rules/dev-data-guide.md diff --git a/.continue/rules/documentation-description-rule.md b/.shadow-code/rules/documentation-description-rule.md similarity index 100% rename from .continue/rules/documentation-description-rule.md rename to .shadow-code/rules/documentation-description-rule.md diff --git a/.continue/rules/documentation-standards.md b/.shadow-code/rules/documentation-standards.md similarity index 100% rename from .continue/rules/documentation-standards.md rename to .shadow-code/rules/documentation-standards.md diff --git a/.continue/rules/github-pr-documentation-updater.md b/.shadow-code/rules/github-pr-documentation-updater.md similarity index 100% rename from .continue/rules/github-pr-documentation-updater.md rename to .shadow-code/rules/github-pr-documentation-updater.md diff --git a/.continue/rules/gui-link-opening.md b/.shadow-code/rules/gui-link-opening.md similarity index 100% rename from .continue/rules/gui-link-opening.md rename to .shadow-code/rules/gui-link-opening.md diff --git a/.continue/rules/intellij-plugin-test-execution.md b/.shadow-code/rules/intellij-plugin-test-execution.md similarity index 100% rename from .continue/rules/intellij-plugin-test-execution.md rename to .shadow-code/rules/intellij-plugin-test-execution.md diff --git a/.continue/rules/llm-specificity.md b/.shadow-code/rules/llm-specificity.md similarity index 100% rename from .continue/rules/llm-specificity.md rename to .shadow-code/rules/llm-specificity.md diff --git a/.continue/rules/migrate-styled-components-to-tailwind.md b/.shadow-code/rules/migrate-styled-components-to-tailwind.md similarity index 100% rename from .continue/rules/migrate-styled-components-to-tailwind.md rename to .shadow-code/rules/migrate-styled-components-to-tailwind.md diff --git a/.continue/rules/mintlify-formatting.md b/.shadow-code/rules/mintlify-formatting.md similarity index 89% rename from .continue/rules/mintlify-formatting.md rename to .shadow-code/rules/mintlify-formatting.md index 4df9bdb9146..8ff97e361ed 100644 --- a/.continue/rules/mintlify-formatting.md +++ b/.shadow-code/rules/mintlify-formatting.md @@ -20,10 +20,11 @@ When working with Mintlify documentation components (Card, Info, Tip, Note, Warn ```mdx - This is the content with proper formatting: - - First bullet point - - Second bullet point - - Third bullet point +This is the content with proper formatting: + +- First bullet point +- Second bullet point +- Third bullet point ``` @@ -31,10 +32,11 @@ When working with Mintlify documentation components (Card, Info, Tip, Note, Warn ```mdx - Important information here: - - Point one - - Point two - - Point three +Important information here: + +- Point one +- Point two +- Point three ``` @@ -57,6 +59,7 @@ When working with Mintlify documentation components (Card, Info, Tip, Note, Warn ### Links in Lists When including links in bullet points: + ```mdx - [Link Text](url): Description of the link ``` @@ -64,6 +67,7 @@ When including links in bullet points: ### Nested Components For nested components, maintain proper indentation levels: + ```mdx @@ -87,6 +91,7 @@ For nested components, maintain proper indentation levels: ## Application These rules apply to all `.mdx` files in the `docs/` directory, particularly: + - Guide documents - Cookbook documents - Reference documentation @@ -95,6 +100,7 @@ These rules apply to all `.mdx` files in the `docs/` directory, particularly: ## Automation Note When using Continue or other AI assistants to generate or modify documentation: + - Always format Mintlify components according to these rules - Review generated content for proper formatting -- Apply these rules consistently across all documentation \ No newline at end of file +- Apply these rules consistently across all documentation diff --git a/.continue/rules/navigating-responses.md b/.shadow-code/rules/navigating-responses.md similarity index 100% rename from .continue/rules/navigating-responses.md rename to .shadow-code/rules/navigating-responses.md diff --git a/.continue/rules/new-protocol-message.md b/.shadow-code/rules/new-protocol-message.md similarity index 100% rename from .continue/rules/new-protocol-message.md rename to .shadow-code/rules/new-protocol-message.md diff --git a/.continue/rules/no-any-types.md b/.shadow-code/rules/no-any-types.md similarity index 100% rename from .continue/rules/no-any-types.md rename to .shadow-code/rules/no-any-types.md diff --git a/.continue/rules/overeager.md b/.shadow-code/rules/overeager.md similarity index 100% rename from .continue/rules/overeager.md rename to .shadow-code/rules/overeager.md diff --git a/.continue/rules/personality.md b/.shadow-code/rules/personality.md similarity index 100% rename from .continue/rules/personality.md rename to .shadow-code/rules/personality.md diff --git a/.continue/rules/programming-principles.md b/.shadow-code/rules/programming-principles.md similarity index 100% rename from .continue/rules/programming-principles.md rename to .shadow-code/rules/programming-principles.md diff --git a/.continue/rules/pure-function-unit-tests.md b/.shadow-code/rules/pure-function-unit-tests.md similarity index 100% rename from .continue/rules/pure-function-unit-tests.md rename to .shadow-code/rules/pure-function-unit-tests.md diff --git a/.continue/rules/test-running-guide.md b/.shadow-code/rules/test-running-guide.md similarity index 100% rename from .continue/rules/test-running-guide.md rename to .shadow-code/rules/test-running-guide.md diff --git a/.continue/rules/typescript-enum-usage.md b/.shadow-code/rules/typescript-enum-usage.md similarity index 100% rename from .continue/rules/typescript-enum-usage.md rename to .shadow-code/rules/typescript-enum-usage.md diff --git a/.continue/rules/unit-testing-rules.md b/.shadow-code/rules/unit-testing-rules.md similarity index 100% rename from .continue/rules/unit-testing-rules.md rename to .shadow-code/rules/unit-testing-rules.md diff --git a/.continue/rules/vs-code-commands-helper-functions.md b/.shadow-code/rules/vs-code-commands-helper-functions.md similarity index 100% rename from .continue/rules/vs-code-commands-helper-functions.md rename to .shadow-code/rules/vs-code-commands-helper-functions.md diff --git a/.continueignore b/.shadow-codeignore similarity index 92% rename from .continueignore rename to .shadow-codeignore index 50684c18754..2889e1d823e 100644 --- a/.continueignore +++ b/.shadow-codeignore @@ -1,9 +1,9 @@ -**/*.run.xml -docs/docs/languages -.changes/ -.idea/ -.vscode/ -.archive/ -**/*.scm -**/*.diff +**/*.run.xml +docs/docs/languages +.changes/ +.idea/ +.vscode/ +.archive/ +**/*.scm +**/*.diff .continue/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index 7072874e07f..2f51cd4f5b4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,8 +21,8 @@ "preLaunchTask": "vscode-extension:build-with-packages", "env": { // "CONTROL_PLANE_ENV": "local", - "CONTINUE_GLOBAL_DIR": "${workspaceFolder}/extensions/.continue-debug" - // "staging" for the preview deployment "CONTINUE_GLOBAL_DIR": "${workspaceFolder}/extensions/.continue-debug" + "SHADOW_CODE_GLOBAL_DIR": "${workspaceFolder}/extensions/.shadow-code-debug" + // "staging" for the preview deployment "SHADOW_CODE_GLOBAL_DIR": "${workspaceFolder}/extensions/.shadow-code-debug" // "local" for entirely local development of control plane/proxy } }, @@ -40,7 +40,7 @@ "env": { // "CONTROL_PLANE_ENV": "test", "CONTINUE_DEVELOPMENT": "true", - "CONTINUE_GLOBAL_DIR": "${workspaceFolder}/extensions/.continue-debug" + "SHADOW_CODE_GLOBAL_DIR": "${workspaceFolder}/extensions/.shadow-code-debug" } }, { diff --git a/.vscode/settings.json b/.vscode/settings.json index 0e2cdfd7e27..9fb7bd5f009 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -43,7 +43,7 @@ "extensions/vscode/e2e/_output": true, "extensions/vscode/e2e/storage": true, "extensions/vscode/e2e/vsix": true, - "extensions/.continue-debug": true, + "extensions/.shadow-code-debug": true, "extensions/cli/dist/**": true, "packages/config-yaml/dist/**": true // "sync/**": true diff --git a/BUILD_DEPENDENCIES.md b/BUILD_DEPENDENCIES.md index 3bd4600cbf4..ea07a39a79e 100644 --- a/BUILD_DEPENDENCIES.md +++ b/BUILD_DEPENDENCIES.md @@ -30,10 +30,10 @@ This document catalogs all build dependencies, secrets, and environment variable ## CLI -| Variable | Purpose | Referenced In | -| ------------------- | ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | -| `CONTINUE_API_BASE` | Base URL for the Continue API (defaults to `https://api.continue.dev/`) | `extensions/cli/.env.example` | -| `CONTINUE_API_KEY` | API key for Continue authentication | `extensions/cli/.env.example`, `packages/continue-sdk/typescript/.env.example`, multiple workflows | +| Variable | Purpose | Referenced In | +| ------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- | +| `CONTINUE_API_BASE` | Base URL for the Shadow Code API (defaults to `https://api.continue.dev/`) | `extensions/cli/.env.example` | +| `CONTINUE_API_KEY` | API key for Shadow Code authentication | `extensions/cli/.env.example`, `packages/continue-sdk/typescript/.env.example`, multiple workflows | --- @@ -73,7 +73,7 @@ Used for integration tests in PR checks and package releases. | ------------------ | --------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------- | | `GITHUB_TOKEN` | Default GitHub Actions token (automatic) | Many workflows | | `CI_GITHUB_TOKEN` | Elevated GitHub PAT for cross-repo operations and PR management | `jetbrains-release.yaml`, `preview.yaml`, `main.yaml`, `pr-checks.yaml`, `auto-assign-issue.yaml` | -| `CONTINUE_API_KEY` | Continue platform API key for agent workflows | `run-continue-agent.yml`, `tidy-up-codebase.yml`, `snyk-agent.yaml`, `auto-fix-failed-tests.yml`, `cli-pr-checks.yml` | +| `CONTINUE_API_KEY` | Shadow Code platform API key for agent workflows | `run-continue-agent.yml`, `tidy-up-codebase.yml`, `snyk-agent.yaml`, `auto-fix-failed-tests.yml`, `cli-pr-checks.yml` | | `RUNLOOP_API_KEY` | Runloop API key for uploading sandbox blueprints | `stable-release.yml`, `upload-runloop-blueprint.yml` | | `SNYK_TOKEN` | Snyk security scanning token | `snyk-agent.yaml` | diff --git a/CLA.md b/CLA.md index a23e12a1ea0..e49bb58d020 100644 --- a/CLA.md +++ b/CLA.md @@ -1,10 +1,10 @@ -# Individual Contributor License Agreement (v1.0, Continue) +# Individual Contributor License Agreement (v1.0, Shadow Code) _Based on the Apache Software Foundation Individual CLA v 2.2._ By commenting **“I have read the CLA Document and I hereby sign the CLA”** on a Pull Request, **you (“Contributor”) agree to the following terms** for any -past and future “Contributions” submitted to **Continue (the “Project”)**. +past and future “Contributions” submitted to **Shadow Code (the “Project”)**. --- @@ -17,14 +17,14 @@ past and future “Contributions” submitted to **Continue (the “Project”)* ## 2. Copyright License -You grant **Continue Dev, Inc.** and all recipients of software distributed by the +You grant **Shadow Code Dev, Inc.** and all recipients of software distributed by the Project a perpetual, worldwide, non‑exclusive, royalty‑free, irrevocable license to reproduce, prepare derivative works of, publicly display, publicly perform, sublicense, and distribute Your Contributions and derivative works. ## 3. Patent License -You grant **Continue Dev, Inc.** and all recipients of the Project a perpetual, +You grant **Shadow Code Dev, Inc.** and all recipients of the Project a perpetual, worldwide, non‑exclusive, royalty‑free, irrevocable (except as below) patent license to make, have made, use, sell, offer to sell, import, and otherwise transfer Your Contributions alone or in combination with the Project. diff --git a/CLAUDE.md b/CLAUDE.md index defa28fddc2..a857c8b99e8 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -4,7 +4,7 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co ## What this project is -shadow-code is a fork of [Continue](https://github.com/continuedev/continue) (the open-source AI coding agent - VS Code extension, JetBrains plugin, and CLI; upstream is now read-only/unmaintained, so this fork is the active codebase going forward). +shadow-code is a fork of Continue (the open-source AI coding agent - VS Code extension, JetBrains plugin, and CLI; upstream is now read-only/unmaintained, so this fork is the active codebase going forward). The fork exists to add one capability Continue doesn't have: **using coding-agent CLIs the user already has a subscription for (Claude Code CLI first, later Codex CLI / GitHub Copilot CLI) as the model-execution backend, instead of paying per-token for an API key.** The design constraint driving all of this is that **Continue's own harness stays authoritative** - its own system prompt, its own tool definitions, its own permission/approval flow. The CLI is used purely for authenticated model execution, never for its own built-in agent loop (its Read/Write/Edit/Bash tools are explicitly disabled on every invocation). @@ -100,7 +100,7 @@ Standard Continue layout - four packages talk to each other over a typed message **Provider registration** (`core/llm/llms/index.ts`): providers are plain classes with a static `providerName`, collected into the `LLMClasses` array and matched by that string in `llmFromDescription`. Adding a provider is additive - `class Foo extends BaseLLM { static providerName = "..." }`, implement `_streamChat`, add to the array. -## Conventions (from `.continue/rules/`) +## Conventions (from `.shadow-code/rules/`) - Prefer functional programming; modifying existing classes or a singleton is fine when that's genuinely the right shape, but default to functions. - Prefer `enum` over string-literal unions in TypeScript where reasonable. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index dae75a8523e..798e70d9b6c 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,11 +1,11 @@ -# Contributing to Continue +# Contributing to Shadow Code ## Table of Contents -- [Contributing to Continue](#contributing-to-continue) +- [Contributing to Shadow Code](#contributing-to-shadow-code) - [Table of Contents](#table-of-contents) - [❤️ Ways to Contribute](#️-ways-to-contribute) - - [👋 Continue Contribution Ideas](#-continue-contribution-ideas) + - [👋 Shadow Code Contribution Ideas](#-shadow-code-contribution-ideas) - [🐛 Report Bugs](#-report-bugs) - [✨ Suggest Enhancements](#-suggest-enhancements) - [📖 Updating / Improving Documentation](#-updating--improving-documentation) @@ -15,7 +15,7 @@ - [🧑‍💻 Contributing Code](#-contributing-code) - [Environment Setup](#environment-setup) - [Pre-requisites](#pre-requisites) - - [Fork the Continue Repository](#fork-the-continue-repository) + - [Fork the Shadow Code Repository](#fork-the-shadow-code-repository) - [VS Code](#vs-code) - [Debugging](#debugging) - [JetBrains](#jetbrains) @@ -29,23 +29,23 @@ - [Contributing new LLM Providers/Models](#contributing-new-llm-providersmodels) - [Adding an LLM Provider](#adding-an-llm-provider) - [Adding Models](#adding-models) - - [📐 Continue Architecture](#-continue-architecture) - - [Continue VS Code Extension](#continue-vs-code-extension) - - [Continue JetBrains Extension](#continue-jetbrains-extension) + - [📐 Shadow Code Architecture](#-shadow-code-architecture) + - [Shadow Code VS Code Extension](#shadow-code-vs-code-extension) + - [Shadow Code JetBrains Extension](#shadow-code-jetbrains-extension) - [Contributor License Agreement](#contributor-license-agreement-cla) # ❤️ Ways to Contribute -## 👋 Continue Contribution Ideas +## 👋 Shadow Code Contribution Ideas -[This GitHub project board](https://github.com/orgs/continuedev/projects/2) is a list of ideas for how you can -contribute to Continue. These aren't the only ways, but are a great starting point if you are new to the project. You +The project board is a list of ideas for how you can +contribute to Shadow Code. These aren't the only ways, but are a great starting point if you are new to the project. You can also browse the list -of [good first issues](https://github.com/continuedev/continue/issues?q=is:issue%20state:open%20label:good-first-issue). +of good first issues. ## 🐛 Report Bugs -If you find a bug, please [create an issue](https://github.com/continuedev/continue/issues) to report it! A great bug +If you find a bug, please create an issue to report it! A great bug report includes: - A description of the bug @@ -56,22 +56,22 @@ report includes: ## ✨ Suggest Enhancements -Continue is quickly adding features, and we'd love to hear which are the most important to you. The best ways to suggest +Shadow Code is quickly adding features, and we'd love to hear which are the most important to you. The best ways to suggest an enhancement are: - Create an issue - First, check whether a similar proposal has already been made - - If not, [create an issue](https://github.com/continuedev/continue/issues) + - If not, create an issue - Please describe the enhancement in as much detail as you can, and why it would be useful -- Join the [GitHub Discussions](https://github.com/continuedev/continue/discussions) and tell us about your idea +- Join the GitHub Discussions and tell us about your idea ## 📖 Updating / Improving Documentation -Continue is continuously improving, but a feature isn't complete until it is reflected in the documentation! If you see +Shadow Code is continuously improving, but a feature isn't complete until it is reflected in the documentation! If you see something out-of-date or missing, you can help by clicking "Edit this page" at the bottom of any page -on [docs.continue.dev](https://docs.continue.dev). +in the docs. ### Running the Documentation Server Locally @@ -129,11 +129,11 @@ Then, install Vite globally npm i -g vite ``` -#### Fork the Continue Repository +#### Fork the Shadow Code Repository -1. Go to the [Continue GitHub repository](https://github.com/continuedev/continue) and fork it to your GitHub account. +1. Go to the Shadow Code GitHub repository and fork it to your GitHub account. -2. Clone your forked repository to your local machine. Use: `git clone https://github.com/YOUR_USERNAME/continue.git` +2. Clone your forked repository to your local machine. Use: `git clone https://github.com/YOUR_USERNAME/shadow-code.git` 3. Navigate to the cloned directory and make sure you are on the main branch. Create your feature/fix branch from there, like so: `git checkout -b 123-my-feature-branch` @@ -155,7 +155,7 @@ npm i -g vite 2. The window you started debugging from is referred to as the _Main VS Code_ 3. To package the extension, run `npm run package` in the `extensions/vscode` directory, select `Tasks: Run Task` and - then select `vscode-extension:package`. This will generate `extensions/vscode/build/continue-{VERSION}.vsix`, which + then select `vscode-extension:package`. This will generate `extensions/vscode/build/shadow-code-{VERSION}.vsix`, which you can install by right-clicking and selecting "Install Extension VSIX". ##### Debugging @@ -185,24 +185,24 @@ is well explained by . ### What makes a good PR? -To keep the Continue codebase clean and maintainable, we expect the following from our own team and all contributors: +To keep the Shadow Code codebase clean and maintainable, we expect the following from our own team and all contributors: - Open a new issue or comment on an existing one before writing code. This ensures your proposed changes are aligned with the project direction - Keep changes focused. Multiple unrelated fixes should be opened as separate PRs - Write or update tests for new functionality - Update relevant documentation in the `docs` folder -- **For new features**: Include a short screen recording or screenshot demonstrating the new functionality. This makes it much easier for us as contributors to review and understand your changes. See [this PR](https://github.com/continuedev/continue/pull/6455) as a good example +- **For new features**: Include a short screen recording or screenshot demonstrating the new functionality. This makes it much easier for us as contributors to review and understand your changes. See this PR as a good example - Open a PR against the `main` branch. Make sure to fill in the PR template ### Formatting -Continue uses [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to format +Shadow Code uses [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) to format JavaScript/TypeScript. Please install the Prettier extension in VS Code and enable "Format on Save" in your settings. ### Theme Colors -Continue has a set of named theme colors that we map to extension colors and tailwind classes, which can be found in [gui/src/styles/theme.ts](gui/src/styles/theme.ts) +Shadow Code has a set of named theme colors that we map to extension colors and tailwind classes, which can be found in [gui/src/styles/theme.ts](gui/src/styles/theme.ts) Guidelines for using theme colors: @@ -233,15 +233,15 @@ When contributing, please update or create the appropriate tests to help verify ### Getting Help -Join the [GitHub Discussions](https://github.com/continuedev/continue/discussions) to engage with maintainers and other contributors. +Join the GitHub Discussions to engage with maintainers and other contributors. ## Contributing New LLM Providers/Models ### Adding an LLM Provider -Continue has support for more than a dozen different LLM "providers", making it easy to use models running on OpenAI, +Shadow Code has support for more than a dozen different LLM "providers", making it easy to use models running on OpenAI, Ollama, Together, LM Studio, Msty, and more. You can find all of the existing -providers [here](https://github.com/continuedev/continue/tree/main/core/llm/llms), and if you see one missing, you can +providers here, and if you see one missing, you can add it with the following steps: 1. Create a new file in the `core/llm/llms` directory. The name of the file should be the name of the provider, and it @@ -257,7 +257,7 @@ add it with the following steps: ### Adding Models -While any model that works with a supported provider can be used with Continue, we keep a list of recommended models +While any model that works with a supported provider can be used with Shadow Code, we keep a list of recommended models that can be automatically configured from the UI or `config.json`. The following files should be updated when adding a model: @@ -268,13 +268,13 @@ model: 2. Add the model within its provider's array to [configs/providers.ts](./gui/src/pages/AddNewModel/configs/providers.ts) (add provider if needed) - LLM Providers: Since many providers use their own custom strings to identify models, you'll have to add the - translation from Continue's model name (the one you added to `index.d.ts`) and the model string for each of these + translation from Shadow Code's model name (the one you added to `index.d.ts`) and the model string for each of these providers: [Ollama](./core/llm/llms/Ollama.ts), [Together](./core/llm/llms/Together.ts), and [Replicate](./core/llm/llms/Replicate.ts). You can find their full model lists here: [Ollama](https://ollama.ai/library), [Together](https://docs.together.ai/docs/inference-models), [Replicate](https://replicate.com/collections/streaming-language-models). - [Prompt Templates](./core/llm/autodetect.ts) - In this file you'll find the `autodetectTemplateType` function. Make sure that for the model name you just added, this function returns the correct template type. This is assuming that - the chat template for that model is already built in Continue. If not, you will have to add the template type and + the chat template for that model is already built in Shadow Code. If not, you will have to add the template type and corresponding edit and chat templates. ## Contributor License Agreement (CLA) diff --git a/README.md b/README.md index 66477d92b4c..b5fd08ad0da 100644 --- a/README.md +++ b/README.md @@ -1,63 +1,45 @@ -

Continue

+

Shadow Code

-

Pioneering open-source coding agent

+

AI code agent — VS Code extension, JetBrains plugin, and CLI

- -
-

- Banner -

+## What is Shadow Code? -## What is Continue? +Shadow Code is an AI coding agent available as a [VS Code extension](extensions/vscode), +a [JetBrains plugin](extensions/intellij), and a [CLI](extensions/cli). It can chat about +your codebase, make multi-file edits, run an autonomous agent with tool access, and +provide inline autocomplete. -> _Note: The `continuedev/continue` repository is no longer actively maintained and is read-only for all users._ +It is a fork of the Continue open-source project, with a focus on using coding-agent CLIs +you already subscribe to (Claude Code, etc.) as the model backend instead of paying +per-token for an API key. -Continue is a coding agent available as a [CLI](#cli), [VS Code extension](#vs-code), and [JetBrains plugin](#jetbrains). +## Repository layout -## Documentation +| Path | What it is | +| ---------------------- | ------------------------------------------------------------ | +| `core/` | Shared engine — config, LLM providers, tools, indexing | +| `gui/` | React webview UI | +| `extensions/vscode/` | VS Code extension host | +| `extensions/intellij/` | JetBrains plugin | +| `extensions/cli/` | Command-line interface | +| `binary/` | Core packaged as a standalone executable (used by JetBrains) | +| `packages/` | Independently published support packages | -To learn how to configure Continue, how it works, and how to customize it, check out the [Continue Docs](https://docs.continue.dev). +## Configuration -## Final 2.0.0 Release +Global config lives in `~/.shadow-code/config.yaml`. Per-workspace rules, prompts, and +assistants live in a `.shadow-code/` folder in your project. -We polished Continue and did a final 2.0.0 release of the VS Code extension, CLI, and JetBrains plugin. +## Contributing -This included removing anonymous telemetry, pulling out authentication, squashing bugs, and more. - -### VS Code - -[![VS Code Marketplace](https://img.shields.io/badge/VS_Code_Marketplace-007ACC?logo=visualstudiocode&logoColor=white)](https://marketplace.visualstudio.com/items?itemName=Continue.continue) [![OpenVSX Registry](https://img.shields.io/badge/OpenVSX_Registry-C160EF?logo=eclipseide&logoColor=white)](https://open-vsx.org/extension/Continue/continue) [![View source](https://img.shields.io/badge/View_source-181717?logo=github&logoColor=white)](extensions/vscode) - -### CLI - -[![npm](https://img.shields.io/badge/npm-CB3837?logo=npm&logoColor=white)](https://www.npmjs.com/package/@continuedev/cli) [![View source](https://img.shields.io/badge/View_source-181717?logo=github&logoColor=white)](extensions/cli) - -### JetBrains - -> _Note: We recommend using the Continue CLI instead of the JetBrains plugin._ - -[![GitHub Releases](https://img.shields.io/badge/GitHub_Releases-181717?logo=github&logoColor=white)](https://github.com/continuedev/continue/releases) [![View source](https://img.shields.io/badge/View_source-181717?logo=github&logoColor=white)](extensions/intellij) - -## Contributors - -Thank you to the entire Continue community for helping us create a pioneering coding agent. - -What we built together pushed the boundaries of what AI developer tooling could be. - -We hope this codebase continues to serve as a foundation for others. - -## Code friends - - - - +See [CONTRIBUTING.md](CONTRIBUTING.md). ## License -Apache 2.0 © 2023-2026 Continue Dev, Inc. +[Apache-2.0](LICENSE). Shadow Code is a fork of the Continue open-source project. diff --git a/TESTING.md b/TESTING.md index 96fe5e6c284..ea1b0b9ef56 100644 --- a/TESTING.md +++ b/TESTING.md @@ -2,10 +2,10 @@ ## Critical -- [x] **Extension cold start** — Launch VS Code with the extension. No errors in Output panel ("Continue" channel) or Dev Tools console. _(Found and fixed `message.includes` crash in `webviewProtocol.ts` + removed dead proxy-server error handling block.)_ -- [x] **Fresh install onboarding** — Delete/rename `~/.continue/config.yaml`, restart. Onboarding card shows "Configure your models" (no Hub sign-in). _(Removed "Credits" tab, fixed Ollama link padding, title font sizes, and "Google Gemini API API key" duplicate.)_ +- [x] **Extension cold start** — Launch VS Code with the extension. No errors in Output panel ("Shadow Code" channel) or Dev Tools console. _(Found and fixed `message.includes` crash in `webviewProtocol.ts` + removed dead proxy-server error handling block.)_ +- [x] **Fresh install onboarding** — Delete/rename `~/.shadow-code/config.yaml`, restart. Onboarding card shows "Configure your models" (no Hub sign-in). _(Removed "Credits" tab, fixed Ollama link padding, title font sizes, and "Google Gemini API API key" duplicate.)_ - [x] **Existing config loads** — With existing `config.yaml`, models/context providers/MCP servers all load. -- [x] **API key resolution from `.env`** — Models using secrets from `~/.continue/.env` or workspace `.env` authenticate and respond. +- [x] **API key resolution from `.env`** — Models using secrets from `~/.shadow-code/.env` or workspace `.env` authenticate and respond. - [x] **Config reload** — Edit `config.yaml` while running, changes picked up without restart. ## High Priority @@ -19,7 +19,7 @@ ## Medium Priority - [x] **MCP servers connect** — Configured MCP servers connect and tools appear. -- [x] **Local blocks in YAML** — Local model block files in `.continue/models/` auto-merge into config correctly. +- [x] **Local blocks in YAML** — Local model block files in `.shadow-code/models/` auto-merge into config correctly. - [x] **Background mode view** — N/A, component removed from UI routing. No way to navigate to it. - [x] **Keyboard shortcut `Cmd+Shift+'`** — Toggles between configs without errors. - [x] **Config settings page** — Settings page renders cleanly, no Account dropdown or Organizations tab. _(Removed GitHub issue/community links.)_ diff --git a/actions/README.md b/actions/README.md index 1625bee4121..60ace6e20da 100644 --- a/actions/README.md +++ b/actions/README.md @@ -1,6 +1,6 @@ # Continue PR Review Actions -GitHub Actions that provide automated code reviews for pull requests using Continue CLI. +GitHub Actions that provide automated code reviews for pull requests using Shadow Code CLI. ## Available Actions @@ -38,8 +38,8 @@ jobs: steps: - uses: continuedev/continue/actions/general-review@main with: - continue-api-key: ${{ secrets.CONTINUE_API_KEY }} - continue-org: "your-org-name" + shadow-code-api-key: ${{ secrets.SHADOW_CODE_API_KEY }} + shadow-code-org: "your-org-name" continue-config: "your-org-name/review-bot" ``` @@ -47,22 +47,22 @@ jobs: The action accepts the following inputs: -| Input | Description | Required | -| ------------------ | -------------------------------------- | -------- | -| `continue-api-key` | API key for Continue service | Yes | -| `continue-org` | Organization for Continue config | Yes | -| `continue-config` | Config path (e.g., "myorg/review-bot") | Yes | +| Input | Description | Required | +| --------------------- | -------------------------------------- | -------- | +| `shadow-code-api-key` | API key for Continue service | Yes | +| `shadow-code-org` | Organization for Continue config | Yes | +| `continue-config` | Config path (e.g., "myorg/review-bot") | Yes | ## Setup Requirements ### 1. Continue API Key -Add your Continue API key as a secret named `CONTINUE_API_KEY` in your repository: +Add your Continue API key as a secret named `SHADOW_CODE_API_KEY` in your repository: 1. Go to your repository's Settings 2. Navigate to Secrets and variables → Actions 3. Click "New repository secret" -4. Name: `CONTINUE_API_KEY` +4. Name: `SHADOW_CODE_API_KEY` 5. Value: Your Continue API key ### 2. Continue Configuration @@ -110,7 +110,7 @@ The general review provides a structured comment that includes: 1. Checks out repository code 2. Fetches PR diff using GitHub CLI 3. Generates a comprehensive review prompt -4. Runs Continue CLI with specified configuration +4. Runs Shadow Code CLI with specified configuration 5. Posts review as a PR comment ## Versioning diff --git a/actions/general-review/action.yml b/actions/general-review/action.yml index 109378e53c2..56081deef53 100644 --- a/actions/general-review/action.yml +++ b/actions/general-review/action.yml @@ -1,15 +1,15 @@ name: "Continue PR Review" -description: "Automated code review for pull requests using Continue CLI" +description: "Automated code review for pull requests using Shadow Code CLI" author: "Continue Dev, Inc." inputs: - continue-api-key: + shadow-code-api-key: description: "API key for Continue service" required: true - continue-org: + shadow-code-org: description: "Organization for Continue config" required: true - continue-agent: + shadow-code-agent: description: 'Agent path to use (e.g., "myorg/review-bot")' required: true @@ -110,7 +110,7 @@ runs: with: node-version: 20 - - name: Install Continue CLI + - name: Install Shadow Code CLI if: env.SHOULD_RUN == 'true' shell: bash run: npm install -g @continuedev/cli@latest @@ -160,7 +160,7 @@ runs: uses: actions/github-script@v7 with: script: | - const marker = ''; + const marker = ''; // Get PR number based on event type let prNumber; @@ -260,16 +260,16 @@ runs: node .continue-action-scripts/buildPrompt.js "$PR_NUMBER" rm -f pr_data.json - - name: Run Continue CLI Review + - name: Run Shadow Code CLI Review if: env.SHOULD_RUN == 'true' shell: bash env: - CONTINUE_API_KEY: ${{ inputs.continue-api-key }} - CONTINUE_ORG: ${{ inputs.continue-org }} - CONTINUE_AGENT: ${{ inputs.continue-agent }} + SHADOW_CODE_API_KEY: ${{ inputs.shadow-code-api-key }} + SHADOW_CODE_ORG: ${{ inputs.shadow-code-org }} + SHADOW_CODE_AGENT: ${{ inputs.shadow-code-agent }} GITHUB_TOKEN: ${{ github.token }} run: | - echo "Running Continue CLI with prompt:" + echo "Running Shadow Code CLI with prompt:" echo "==================================" head -n 100 review_prompt.txt echo "... [truncated] ..." @@ -277,8 +277,8 @@ runs: echo "" # Validate API key - if [ -z "$CONTINUE_API_KEY" ]; then - echo "Warning: CONTINUE_API_KEY environment variable is not set" + if [ -z "$SHADOW_CODE_API_KEY" ]; then + echo "Warning: SHADOW_CODE_API_KEY environment variable is not set" # Create fallback review and continue node .continue-action-scripts/writeMarkdown.js code_review.md missing_api_key echo "SKIP_CLI=true" >> $GITHUB_ENV @@ -287,32 +287,32 @@ runs: fi # Validate inputs to prevent command injection - if [[ ! "$CONTINUE_ORG" =~ ^[a-zA-Z0-9_-]+$ ]]; then + if [[ ! "$SHADOW_CODE_ORG" =~ ^[a-zA-Z0-9_-]+$ ]]; then echo "Error: Invalid organization name. Must contain only alphanumeric characters, hyphens, and underscores." exit 1 fi - if [[ ! "$CONTINUE_AGENT" =~ ^[a-zA-Z0-9_/-]+$ ]]; then + if [[ ! "$SHADOW_CODE_AGENT" =~ ^[a-zA-Z0-9_/-]+$ ]]; then echo "Error: Invalid config path. Must contain only alphanumeric characters, hyphens, underscores, and forward slashes." exit 1 fi - # Test Continue CLI availability + # Test Shadow Code CLI availability if [ "$SKIP_CLI" != "true" ]; then - echo "Testing Continue CLI..." + echo "Testing Shadow Code CLI..." if ! which cn > /dev/null 2>&1; then - echo "Warning: Continue CLI not found or not working" + echo "Warning: Shadow Code CLI not found or not working" node .continue-action-scripts/writeMarkdown.js code_review.md cli_install_failed echo "SKIP_CLI=true" >> $GITHUB_ENV else - echo "Continue CLI found at: $(which cn)" - echo "Continue CLI version: $(cn --version 2>/dev/null || echo 'version check failed')" + echo "Shadow Code CLI found at: $(which cn)" + echo "Shadow Code CLI version: $(cn --version 2>/dev/null || echo 'version check failed')" fi fi # Run the CLI with validated config and error handling if [ "$SKIP_CLI" != "true" ]; then - echo "Executing Continue CLI with config: $CONTINUE_ORG/$CONTINUE_AGENT" + echo "Executing Shadow Code CLI with config: $SHADOW_CODE_ORG/$SHADOW_CODE_AGENT" # Write prompt to temp file for headless mode PROMPT_FILE="/tmp/continue-review-$RANDOM.txt" @@ -321,10 +321,10 @@ runs: echo "Prompt length: $(wc -c < "$PROMPT_FILE") characters" # Use timeout to prevent hanging (360 seconds = 6 minutes) - echo "Executing command: cn --agent $CONTINUE_ORG/$CONTINUE_AGENT -p @$PROMPT_FILE --allow Bash" + echo "Executing command: sc --agent $SHADOW_CODE_ORG/$SHADOW_CODE_AGENT -p @$PROMPT_FILE --allow Bash" - if timeout 360 cn --agent "$CONTINUE_ORG/$CONTINUE_AGENT" -p "@$PROMPT_FILE" --allow Bash > code_review_raw.md 2>cli_error.log; then - echo "Continue CLI completed successfully" + if timeout 360 sc --agent "$SHADOW_CODE_ORG/$SHADOW_CODE_AGENT" -p "@$PROMPT_FILE" --allow Bash > code_review_raw.md 2>cli_error.log; then + echo "Shadow Code CLI completed successfully" echo "Raw output length: $(wc -c < code_review_raw.md) characters" # Clean up ANSI codes if any @@ -337,11 +337,11 @@ runs: # Check if output is empty if [ ! -s code_review.md ]; then - echo "Warning: Continue CLI returned empty output" + echo "Warning: Shadow Code CLI returned empty output" node .continue-action-scripts/writeMarkdown.js code_review.md empty_output fi else - echo "Error: Continue CLI command failed with exit code $?" + echo "Error: Shadow Code CLI command failed with exit code $?" echo "CLI error log:" cat cli_error.log @@ -389,7 +389,7 @@ runs: } else { // Build direct link to workflow logs const workflowRunUrl = `https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`; - reviewContent = `⚠️ AI review completed but no review output was generated.\n\n**Likely cause:** Expired CONTINUE_API_KEY or missing continuedev/review-bot assistant\n\n[📋 View workflow logs](${workflowRunUrl}) for details.`; + reviewContent = `⚠️ AI review completed but no review output was generated.\n\n**Likely cause:** Expired SHADOW_CODE_API_KEY or missing review-bot assistant\n\n[📋 View workflow logs](${workflowRunUrl}) for details.`; } // Get PR number based on event type @@ -411,7 +411,7 @@ runs: reviewContent += `\n\n---\n`; // Look for existing review comment to update (sticky comment) - const marker = ''; + const marker = ''; // Try to get comment_id from the initial comment step const initialCommentId = '${{ steps.initial-comment.outputs.comment_id }}'; diff --git a/actions/general-review/scripts/writeMarkdown.js b/actions/general-review/scripts/writeMarkdown.js index 8ada50bbdee..4908e50967b 100644 --- a/actions/general-review/scripts/writeMarkdown.js +++ b/actions/general-review/scripts/writeMarkdown.js @@ -4,15 +4,15 @@ const path = require("path"); const messages = { missing_api_key: `## Code Review Summary -⚠️ AI review skipped: CONTINUE_API_KEY not configured. +⚠️ AI review skipped: SHADOW_CODE_API_KEY not configured. ### Configuration Required -- Please set the CONTINUE_API_KEY secret in repository settings +- Please set the SHADOW_CODE_API_KEY secret in repository settings - Verify that the organization and config path are valid `, cli_install_failed: `## Code Review Summary -⚠️ AI review skipped: Continue CLI installation failed. +⚠️ AI review skipped: Shadow Code CLI installation failed. ### Troubleshooting - Check that npm installation succeeded @@ -20,11 +20,11 @@ const messages = { `, empty_output: `## Code Review Summary -⚠️ Continue CLI returned an empty response. Please check the configuration. +⚠️ Shadow Code CLI returned an empty response. Please check the configuration. `, cli_not_found: `## Code Review Summary -⚠️ Continue CLI is not properly installed. Please ensure @continuedev/cli is installed globally. +⚠️ Shadow Code CLI is not properly installed. Please ensure @continuedev/cli is installed globally. `, config_error: `## Code Review Summary @@ -32,14 +32,14 @@ const messages = { `, auth_error: `## Code Review Summary -⚠️ Continue API authentication failed. Please check your CONTINUE_API_KEY. +⚠️ Shadow Code API authentication failed. Please check your SHADOW_CODE_API_KEY. `, generic_failure: `## Code Review Summary ⚠️ AI review failed. Please check the Continue API key and configuration. ### Troubleshooting -- Verify the CONTINUE_API_KEY secret is set correctly +- Verify the SHADOW_CODE_API_KEY secret is set correctly - Check that the organization and config path are valid - Ensure the Continue service is accessible `, diff --git a/binary/.continueignore b/binary/.shadow-codeignore similarity index 100% rename from binary/.continueignore rename to binary/.shadow-codeignore diff --git a/binary/build.js b/binary/build.js index a8713961195..df25bf84f0a 100644 --- a/binary/build.js +++ b/binary/build.js @@ -93,7 +93,7 @@ async function buildWithEsbuild() { { name: "binary", version: "1.0.0", - author: "Continue Dev, Inc", + author: "Shadow Code", license: "Apache-2.0", }, undefined, diff --git a/binary/core-dev-server.js b/binary/core-dev-server.js index 07d38a5f4d3..6999a6ccb09 100644 --- a/binary/core-dev-server.js +++ b/binary/core-dev-server.js @@ -1,10 +1,10 @@ const path = require("path"); -process.env.CONTINUE_DEVELOPMENT = true; +process.env.SHADOW_CODE_DEVELOPMENT = true; -process.env.CONTINUE_GLOBAL_DIR = path.join( +process.env.SHADOW_CODE_GLOBAL_DIR = path.join( process.env.PROJECT_DIR, "extensions", - ".continue-debug", + ".shadow-code-debug", ); require("./out/index.js"); diff --git a/binary/package-lock.json b/binary/package-lock.json index d768d163a90..c8531ea4c0f 100644 --- a/binary/package-lock.json +++ b/binary/package-lock.json @@ -103,7 +103,7 @@ "shell-quote": "^1.8.3", "socket.io-client": "^4.7.3", "sqlite": "^5.1.1", - "sqlite3": "^5.1.7", + "sqlite3": "^6.0.1", "system-ca": "^1.0.3", "tar": "^7.5.13", "tree-sitter-wasms": "^0.1.11", @@ -2122,8 +2122,7 @@ "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" - }, - "peerDependencies": {} + } }, "node_modules/@types/babel__template": { "version": "7.4.4", @@ -2144,8 +2143,7 @@ "license": "MIT", "dependencies": { "@babel/types": "^7.28.2" - }, - "peerDependencies": {} + } }, "node_modules/@types/command-line-args": { "version": "5.2.0", @@ -2217,8 +2215,7 @@ "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" - }, - "peerDependencies": {} + } }, "node_modules/@types/node": { "version": "20.3.0", @@ -2255,8 +2252,7 @@ "license": "MIT", "dependencies": { "@types/yargs-parser": "*" - }, - "peerDependencies": {} + } }, "node_modules/@types/yargs-parser": { "version": "21.0.3", @@ -6414,8 +6410,7 @@ }, "engines": { "node": "*" - }, - "optionalDependencies": {} + } }, "node_modules/sprintf-js": { "version": "1.0.3", @@ -6881,8 +6876,7 @@ }, "engines": { "node": "*" - }, - "optionalDependencies": {} + } }, "node_modules/type-detect": { "version": "4.0.8", @@ -7167,8 +7161,7 @@ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-3.2.1.tgz", "integrity": "sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A==", "license": "MIT", - "optional": true, - "optionalDependencies": {} + "optional": true }, "node_modules/wordwrap": { "version": "1.0.0", diff --git a/binary/pkgJson/darwin-arm64/package.json b/binary/pkgJson/darwin-arm64/package.json index 885f2f21547..f6dd0a0b769 100644 --- a/binary/pkgJson/darwin-arm64/package.json +++ b/binary/pkgJson/darwin-arm64/package.json @@ -1,5 +1,5 @@ { - "name": "continue-binary", + "name": "shadow-code-binary", "version": "1.0.0", "description": "", "bin": "../../out/index.js", diff --git a/binary/pkgJson/darwin-x64/package.json b/binary/pkgJson/darwin-x64/package.json index eba774c718a..0f44e9475a6 100644 --- a/binary/pkgJson/darwin-x64/package.json +++ b/binary/pkgJson/darwin-x64/package.json @@ -1,5 +1,5 @@ { - "name": "continue-binary", + "name": "shadow-code-binary", "version": "1.0.0", "description": "", "bin": "../../out/index.js", diff --git a/binary/pkgJson/linux-arm64/package.json b/binary/pkgJson/linux-arm64/package.json index a3db5104088..ffaedfb5085 100644 --- a/binary/pkgJson/linux-arm64/package.json +++ b/binary/pkgJson/linux-arm64/package.json @@ -1,5 +1,5 @@ { - "name": "continue-binary", + "name": "shadow-code-binary", "version": "1.0.0", "description": "", "bin": "../../out/index.js", diff --git a/binary/pkgJson/linux-x64/package.json b/binary/pkgJson/linux-x64/package.json index f23f4ce494a..4f710d64878 100644 --- a/binary/pkgJson/linux-x64/package.json +++ b/binary/pkgJson/linux-x64/package.json @@ -1,5 +1,5 @@ { - "name": "continue-binary", + "name": "shadow-code-binary", "version": "1.0.0", "description": "", "bin": "../../out/index.js", diff --git a/binary/pkgJson/win32-arm64/package.json b/binary/pkgJson/win32-arm64/package.json index 6134fb8feb9..d7befac921a 100644 --- a/binary/pkgJson/win32-arm64/package.json +++ b/binary/pkgJson/win32-arm64/package.json @@ -1,5 +1,5 @@ { - "name": "continue-binary", + "name": "shadow-code-binary", "version": "1.0.0", "description": "", "bin": "../../out/index.js", diff --git a/binary/pkgJson/win32-x64/package.json b/binary/pkgJson/win32-x64/package.json index be714529abd..e9b54683e37 100644 --- a/binary/pkgJson/win32-x64/package.json +++ b/binary/pkgJson/win32-x64/package.json @@ -1,5 +1,5 @@ { - "name": "continue-binary", + "name": "shadow-code-binary", "version": "1.0.0", "description": "", "bin": "../../out/index.js", diff --git a/binary/prompt-logs.js b/binary/prompt-logs.js index db75109467c..75e195ebdde 100644 --- a/binary/prompt-logs.js +++ b/binary/prompt-logs.js @@ -5,7 +5,7 @@ const logDirPath = path.join( __dirname, "..", "extensions", - ".continue-debug", + ".shadow-code-debug", "logs", ); const logFilePath = path.join(logDirPath, "prompt.log"); diff --git a/binary/src/IpcMessenger.ts b/binary/src/IpcMessenger.ts index b92a8e6a238..2769db7efbb 100644 --- a/binary/src/IpcMessenger.ts +++ b/binary/src/IpcMessenger.ts @@ -193,12 +193,12 @@ export class IpcMessenger< }); process.stdout.on("close", () => { fs.writeFileSync("./error.log", `${new Date().toISOString()}\n`); - console.log("[info] Exiting Continue core..."); + console.log("[info] Exiting Shadow Code core..."); process.exit(1); }); process.stdin.on("close", () => { fs.writeFileSync("./error.log", `${new Date().toISOString()}\n`); - console.log("[info] Exiting Continue core..."); + console.log("[info] Exiting Shadow Code core..."); process.exit(1); }); } @@ -231,10 +231,10 @@ export class CoreBinaryMessenger< this._handleData(data); }); this.subprocess.stdout.on("close", () => { - console.log("[info] Continue core exited"); + console.log("[info] Shadow Code core exited"); }); this.subprocess.stdin.on("close", () => { - console.log("[info] Continue core exited"); + console.log("[info] Shadow Code core exited"); }); } diff --git a/binary/src/index.ts b/binary/src/index.ts index dce9a25ab2b..f4a90210fbb 100644 --- a/binary/src/index.ts +++ b/binary/src/index.ts @@ -12,14 +12,14 @@ import { setupCoreLogging } from "./logging"; import { TcpMessenger } from "./TcpMessenger"; const logFilePath = getCoreLogsPath(); -fs.appendFileSync(logFilePath, "[info] Starting Continue core...\n"); +fs.appendFileSync(logFilePath, "[info] Starting Shadow Code core...\n"); const program = new Command(); program.action(async () => { try { let messenger: IMessenger; - if (process.env.CONTINUE_DEVELOPMENT === "true") { + if (process.env.SHADOW_CODE_DEVELOPMENT === "true") { messenger = new TcpMessenger(); console.log("[binary] Waiting for connection"); await ( diff --git a/binary/src/logging.ts b/binary/src/logging.ts index b6dbfba2cea..017d8cff85f 100644 --- a/binary/src/logging.ts +++ b/binary/src/logging.ts @@ -12,5 +12,5 @@ export function setupCoreLogging() { console.error = logger; console.warn = logger; console.debug = logger; - console.log("[info] Starting Continue core..."); + console.log("[info] Starting Shadow Code core..."); } diff --git a/binary/test/binary.test.ts b/binary/test/binary.test.ts index b68c2f17113..9d663a942c7 100644 --- a/binary/test/binary.test.ts +++ b/binary/test/binary.test.ts @@ -158,11 +158,11 @@ function autodetectPlatformAndArch() { return [platform, arch]; } -const CONTINUE_GLOBAL_DIR = path.join(__dirname, "..", ".continue"); -if (fs.existsSync(CONTINUE_GLOBAL_DIR)) { - fs.rmSync(CONTINUE_GLOBAL_DIR, { recursive: true, force: true }); +const SHADOW_CODE_GLOBAL_DIR = path.join(__dirname, "..", ".shadow-code"); +if (fs.existsSync(SHADOW_CODE_GLOBAL_DIR)) { + fs.rmSync(SHADOW_CODE_GLOBAL_DIR, { recursive: true, force: true }); } -fs.mkdirSync(CONTINUE_GLOBAL_DIR); +fs.mkdirSync(SHADOW_CODE_GLOBAL_DIR); describe("Test Suite", () => { let messenger: IMessenger; @@ -223,7 +223,7 @@ describe("Test Suite", () => { } else { try { subprocess = spawn(binaryPath, { - env: { ...process.env, CONTINUE_GLOBAL_DIR }, + env: { ...process.env, SHADOW_CODE_GLOBAL_DIR }, }); console.log("Successfully spawned subprocess"); } catch (error) { @@ -278,8 +278,8 @@ describe("Test Suite", () => { expect(resp).toBe("pong"); }); - it("should create .continue directory at the specified location with expected files", async () => { - expect(fs.existsSync(CONTINUE_GLOBAL_DIR)).toBe(true); + it("should create .shadow-code directory at the specified location with expected files", async () => { + expect(fs.existsSync(SHADOW_CODE_GLOBAL_DIR)).toBe(true); // Many of the files are only created when trying to load the config await request("config/getSerializedProfileInfo", undefined); @@ -287,7 +287,7 @@ describe("Test Suite", () => { const expectedFiles = ["logs/core.log", "index/autocompleteCache.sqlite"]; const missingFiles = expectedFiles.filter((file) => { - const filePath = path.join(CONTINUE_GLOBAL_DIR, file); + const filePath = path.join(SHADOW_CODE_GLOBAL_DIR, file); return !fs.existsSync(filePath); }); diff --git a/core/agent/subagentRunner.ts b/core/agent/subagentRunner.ts new file mode 100644 index 00000000000..1cb95354278 --- /dev/null +++ b/core/agent/subagentRunner.ts @@ -0,0 +1,397 @@ +import { randomUUID } from "node:crypto"; + +import { + ChatMessage, + ContinueConfig, + IDE, + ILLM, + Tool, + ToolApprovalRequest, + ToolCall, + ToolCallDelta, + ToolExtras, +} from ".."; +import { SUBAGENT_MAX_ITERATIONS } from "../tools/constants"; +import { BuiltInToolNames, SHADOW_TOOL_NAMES } from "../tools/builtIn"; +import { callTool } from "../tools/callTool"; +import { renderContextItems } from "../util/messageContent"; +import { subagentSystemMessage } from "./subagentSystemMessage"; + +export interface SubagentTask { + description: string; + prompt: string; + allowed_tools?: string[]; + model?: string; +} + +export type SubagentStatus = "running" | "done" | "errored" | "canceled"; + +export interface SubagentProgress { + status: SubagentStatus; + /** Model round-trips completed so far. */ + iterations: number; + /** Markdown transcript shown when the user expands this subagent's card. */ + transcript: string; + /** The final report; only meaningful once status is "done". */ + report?: string; +} + +export interface RunSubagentOptions { + task: SubagentTask; + llm: ILLM; + config: ContinueConfig; + ide: IDE; + fetch: ToolExtras["fetch"]; + codeBaseIndexer?: ToolExtras["codeBaseIndexer"]; + /** Parent conversation id, for tool-result bookkeeping. */ + sessionId?: string; + signal?: AbortSignal; + requestApproval?: ToolExtras["requestApproval"]; + onProgress?: (progress: SubagentProgress) => void; +} + +/** + * Tools a subagent may use when the task didn't name any. Read-only tools only: + * subagents run concurrently, so anything that writes could collide with a + * sibling. `spawn_subagents` is excluded here AND independently by the depth + * guard in its implementation, and the shadow_* tools are excluded because a + * subagent has no conversation history of its own to search. + */ +export function resolveSubagentTools( + config: ContinueConfig, + allowedToolNames?: string[], +): Tool[] { + const selectable = config.tools.filter( + (t) => + t.function.name !== BuiltInToolNames.SpawnSubagents && + !SHADOW_TOOL_NAMES.has(t.function.name), + ); + + if (allowedToolNames?.length) { + const requested = new Set(allowedToolNames); + return selectable.filter((t) => requested.has(t.function.name)); + } + + return selectable.filter((t) => t.readonly); +} + +/** + * Resolves a task's optional `model` against the configured chat models. Falls + * back to the parent's model rather than erroring: a mistyped name should + * degrade to "same model as the main chat", not fail the whole task. + */ +export function resolveSubagentModel( + config: ContinueConfig, + fallback: ILLM, + modelName?: string, +): ILLM { + if (!modelName) { + return fallback; + } + const candidates = config.modelsByRole?.chat ?? []; + return ( + candidates.find((m) => m.model === modelName || m.title === modelName) ?? + fallback + ); +} + +function toolCallsFromChunks(chunks: ChatMessage[]): ToolCall[] { + const calls = new Map< + string | number, + { id: string; name: string; args: string } + >(); + const order: (string | number)[] = []; + let currentKey: string | number | undefined; + + for (const chunk of chunks) { + if (chunk.role !== "assistant" || !chunk.toolCalls?.length) continue; + for (const delta of chunk.toolCalls as ToolCallDelta[]) { + const key = + typeof delta.index === "number" + ? delta.index + : (delta.id ?? currentKey); + if (key === undefined) continue; + currentKey = key; + + let call = calls.get(key); + if (!call) { + call = { id: delta.id ?? "", name: "", args: "" }; + calls.set(key, call); + order.push(key); + } + if (delta.id && !call.id) call.id = delta.id; + if (delta.function?.name) call.name += delta.function.name; + if (delta.function?.arguments) call.args += delta.function.arguments; + } + } + + return order + .map((key) => calls.get(key)!) + .filter((c) => c.name) + .map((c) => ({ + id: c.id || randomUUID(), + type: "function" as const, + function: { name: c.name, arguments: c.args || "{}" }, + })); +} + +function textFromChunks(chunks: ChatMessage[]): string { + return chunks + .filter((c) => c.role === "assistant" && typeof c.content === "string") + .map((c) => c.content as string) + .join(""); +} + +/** + * Runs one subagent to completion and returns its report. + * + * Two execution modes. For providers we drive ourselves this is a bounded + * tool-calling loop. For `claudecode` it is a single call: that provider only + * ever forwards `[systemMessage, lastUserMessage]` to the CLI and discards the + * rest, so iterating against it would silently lose the subagent's own tool + * results between turns. The CLI runs its own loop against our MCP server + * instead, which is the same arrangement the main chat already uses. + */ +export async function runSubagent( + options: RunSubagentOptions, +): Promise { + const { + task, + config, + ide, + fetch, + codeBaseIndexer, + sessionId, + signal, + requestApproval, + onProgress, + } = options; + + const llm = resolveSubagentModel(config, options.llm, task.model); + const tools = resolveSubagentTools(config, task.allowed_tools); + const agentRunId = `subagent-${randomUUID()}`; + + const lines: string[] = []; + let iterations = 0; + + const emit = (status: SubagentStatus, report?: string) => { + const progress: SubagentProgress = { + status, + iterations, + transcript: lines.join("\n"), + report, + }; + onProgress?.(progress); + return progress; + }; + + const canceled = () => { + lines.push("\n_Canceled._"); + return emit("canceled"); + }; + + if (signal?.aborted) { + return canceled(); + } + + const messages: ChatMessage[] = [ + { role: "system", content: subagentSystemMessage(tools) }, + { role: "user", content: task.prompt }, + ]; + + const completionOptions = { + tools, + agentRunId, + agentLabel: task.description, + shadowSessionId: sessionId, + }; + + // Never let one subagent's failure take down the whole spawn call. + try { + if (llm.providerName === "claudecode") { + const chunks: ChatMessage[] = []; + for await (const chunk of llm.streamChat( + messages, + signal ?? new AbortController().signal, + completionOptions, + )) { + chunks.push(chunk); + if (chunk.role === "assistant" && chunk.toolCalls?.length) { + for (const tc of chunk.toolCalls as ToolCallDelta[]) { + if (tc.function?.name) { + lines.push(`- \`${tc.function.name}\``); + } + } + emit("running"); + } + } + if (signal?.aborted) { + return canceled(); + } + const report = textFromChunks(chunks).trim(); + lines.push(report); + return emit("done", report); + } + + while (iterations < SUBAGENT_MAX_ITERATIONS) { + if (signal?.aborted) { + return canceled(); + } + iterations += 1; + + const chunks: ChatMessage[] = []; + for await (const chunk of llm.streamChat( + messages, + signal ?? new AbortController().signal, + completionOptions, + )) { + chunks.push(chunk); + } + + if (signal?.aborted) { + return canceled(); + } + + const text = textFromChunks(chunks).trim(); + const toolCalls = toolCallsFromChunks(chunks); + + if (text) { + lines.push(text); + } + + if (toolCalls.length === 0) { + return emit("done", text); + } + + messages.push({ + role: "assistant", + content: text, + toolCalls, + }); + emit("running"); + + // Sequential, not parallel: a subagent's own calls are usually dependent + // reads, and serializing them keeps approval prompts in a sane order. + for (const toolCall of toolCalls) { + if (signal?.aborted) { + return canceled(); + } + + const resultText = await executeSubagentToolCall({ + toolCall, + tools, + config, + ide, + llm, + fetch, + codeBaseIndexer, + sessionId, + signal, + requestApproval, + agentLabel: task.description, + onLine: (line) => lines.push(line), + }); + + messages.push({ + role: "tool", + content: resultText, + toolCallId: toolCall.id, + }); + } + + emit("running"); + } + + // Ran out of iterations. Report what we have rather than nothing. + const partial = lines.join("\n"); + lines.push( + `\n_Stopped after ${SUBAGENT_MAX_ITERATIONS} iterations without reaching a conclusion._`, + ); + return emit( + "done", + `${partial}\n\n(This subagent hit its ${SUBAGENT_MAX_ITERATIONS}-iteration limit before finishing. The above is incomplete.)`, + ); + } catch (e) { + if (signal?.aborted) { + return canceled(); + } + const message = e instanceof Error ? e.message : String(e); + lines.push(`\n**Error:** ${message}`); + return emit("errored", `This subagent failed: ${message}`); + } +} + +async function executeSubagentToolCall(params: { + toolCall: ToolCall; + tools: Tool[]; + config: ContinueConfig; + ide: IDE; + llm: ILLM; + fetch: ToolExtras["fetch"]; + codeBaseIndexer?: ToolExtras["codeBaseIndexer"]; + sessionId?: string; + signal?: AbortSignal; + requestApproval?: ToolExtras["requestApproval"]; + agentLabel: string; + onLine: (line: string) => void; +}): Promise { + const { toolCall, tools, agentLabel, onLine } = params; + const name = toolCall.function.name; + + const tool = tools.find((t) => t.function.name === name); + if (!tool) { + onLine(`- \`${name}\` — not available to this subagent`); + return `Tool "${name}" is not available to you. Use only the tools listed in your instructions.`; + } + + let parsedArgs: Record = {}; + try { + parsedArgs = JSON.parse(toolCall.function.arguments || "{}"); + } catch { + // Leave empty; the tool's own arg validation will produce the error. + } + + // Subagent tool calls bypass the GUI's streaming loop, so they'd otherwise + // skip the policy gate entirely - Core executes tools unconditionally. + if (params.requestApproval) { + const approval: ToolApprovalRequest = { + approvalId: randomUUID(), + toolCallId: toolCall.id, + sessionId: params.sessionId, + toolName: name, + args: parsedArgs, + displayTitle: tool.displayTitle, + wouldLikeTo: tool.wouldLikeTo, + agentLabel, + }; + const { approved } = await params.requestApproval(approval); + if (!approved) { + onLine(`- \`${name}\` — denied by user`); + return `The user denied this tool call. Do not retry it; work with what you have or explain what you could not determine.`; + } + } + + const { contextItems, errorMessage } = await callTool(tool, toolCall, { + config: params.config, + ide: params.ide, + llm: params.llm, + fetch: params.fetch, + tool, + toolCallId: toolCall.id, + codeBaseIndexer: params.codeBaseIndexer, + sessionId: params.sessionId, + signal: params.signal, + requestApproval: params.requestApproval, + // Everything a subagent calls runs at depth 1, which is what makes + // spawn_subagents refuse to run from inside a subagent. + subagentDepth: 1, + }); + + if (errorMessage) { + onLine(`- \`${name}\` — error: ${errorMessage}`); + return `Tool "${name}" failed: ${errorMessage}`; + } + + onLine(`- \`${name}\``); + return renderContextItems(contextItems) || "(no output)"; +} diff --git a/core/agent/subagentRunner.vitest.ts b/core/agent/subagentRunner.vitest.ts new file mode 100644 index 00000000000..006681ec708 --- /dev/null +++ b/core/agent/subagentRunner.vitest.ts @@ -0,0 +1,230 @@ +import { expect, test, vi } from "vitest"; + +import { + ChatMessage, + ContinueConfig, + ILLM, + Tool, + ToolApprovalRequest, +} from ".."; +import { BuiltInToolNames } from "../tools/builtIn"; +import { SUBAGENT_MAX_ITERATIONS } from "../tools/constants"; +import { + resolveSubagentModel, + resolveSubagentTools, + runSubagent, +} from "./subagentRunner"; + +function tool(name: string, readonly: boolean): Tool { + return { + type: "function", + displayTitle: name, + readonly, + group: "Built-In", + function: { name, description: "", parameters: { type: "object" } }, + }; +} + +const TOOLS: Tool[] = [ + tool(BuiltInToolNames.ReadFile, true), + tool(BuiltInToolNames.GrepSearch, true), + tool(BuiltInToolNames.RunTerminalCommand, false), + tool(BuiltInToolNames.SpawnSubagents, false), + tool(BuiltInToolNames.ShadowGetChatHistory, true), +]; + +function fakeConfig(overrides: Partial = {}): ContinueConfig { + return { tools: TOOLS, ...overrides } as ContinueConfig; +} + +/** An ILLM whose every turn yields the given chunks, in order. */ +function fakeLlm(turns: ChatMessage[][], providerName = "anthropic"): ILLM { + let turnIndex = 0; + return { + providerName, + model: "fake-model", + title: "Fake", + streamChat: async function* () { + const chunks = turns[Math.min(turnIndex, turns.length - 1)]; + turnIndex += 1; + for (const chunk of chunks) { + yield chunk; + } + }, + } as unknown as ILLM; +} + +function baseOptions(llm: ILLM, config = fakeConfig()) { + return { + task: { description: "Test task", prompt: "do the thing" }, + llm, + config, + ide: {} as any, + fetch: (async () => new Response("")) as any, + }; +} + +test("resolveSubagentTools never includes spawn_subagents", () => { + const names = resolveSubagentTools(fakeConfig()).map((t) => t.function.name); + expect(names).not.toContain(BuiltInToolNames.SpawnSubagents); +}); + +test("resolveSubagentTools excludes spawn_subagents even when explicitly requested", () => { + const names = resolveSubagentTools(fakeConfig(), [ + BuiltInToolNames.SpawnSubagents, + BuiltInToolNames.ReadFile, + ]).map((t) => t.function.name); + + expect(names).toEqual([BuiltInToolNames.ReadFile]); +}); + +test("resolveSubagentTools defaults to readonly tools only", () => { + const names = resolveSubagentTools(fakeConfig()).map((t) => t.function.name); + expect(names).toContain(BuiltInToolNames.ReadFile); + expect(names).toContain(BuiltInToolNames.GrepSearch); + expect(names).not.toContain(BuiltInToolNames.RunTerminalCommand); + // A subagent has no conversation history of its own to search. + expect(names).not.toContain(BuiltInToolNames.ShadowGetChatHistory); +}); + +test("resolveSubagentTools honors allowed_tools", () => { + const names = resolveSubagentTools(fakeConfig(), [ + BuiltInToolNames.RunTerminalCommand, + ]).map((t) => t.function.name); + + expect(names).toEqual([BuiltInToolNames.RunTerminalCommand]); +}); + +test("resolveSubagentModel falls back to the chat model when the name is unknown", () => { + const fallback = fakeLlm([[]]); + const other = { model: "cheap-model" } as ILLM; + const config = fakeConfig({ modelsByRole: { chat: [other] } } as any); + + expect(resolveSubagentModel(config, fallback, "nope")).toBe(fallback); + expect(resolveSubagentModel(config, fallback, undefined)).toBe(fallback); + expect(resolveSubagentModel(config, fallback, "cheap-model")).toBe(other); +}); + +test("runSubagent returns the final text when the model makes no tool calls", async () => { + const llm = fakeLlm([[{ role: "assistant", content: "the answer" }]]); + const result = await runSubagent(baseOptions(llm)); + + expect(result.status).toBe("done"); + expect(result.report).toBe("the answer"); + expect(result.iterations).toBe(1); +}); + +test("runSubagent stops at SUBAGENT_MAX_ITERATIONS", async () => { + // Always asks for another tool call, so it can only end by hitting the cap. + const llm = fakeLlm([ + [ + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "call-1", + index: 0, + type: "function", + function: { name: "nonexistent_tool", arguments: "{}" }, + }, + ], + }, + ], + ]); + + const result = await runSubagent(baseOptions(llm)); + + expect(result.iterations).toBe(SUBAGENT_MAX_ITERATIONS); + expect(result.report).toContain("iteration limit"); +}); + +test("runSubagent returns canceled when the signal is already aborted", async () => { + const controller = new AbortController(); + controller.abort(); + const llm = fakeLlm([[{ role: "assistant", content: "unreachable" }]]); + + const result = await runSubagent({ + ...baseOptions(llm), + signal: controller.signal, + }); + + expect(result.status).toBe("canceled"); +}); + +test("runSubagent returns canceled when the signal aborts mid-loop", async () => { + const controller = new AbortController(); + const llm = fakeLlm([ + [ + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "call-1", + index: 0, + type: "function", + function: { name: BuiltInToolNames.ReadFile, arguments: "{}" }, + }, + ], + }, + ], + ]); + + const result = await runSubagent({ + ...baseOptions(llm), + signal: controller.signal, + // Abort while the subagent waits for approval of its first tool call. + requestApproval: async () => { + controller.abort(); + return { approved: false }; + }, + }); + + expect(result.status).toBe("canceled"); +}); + +test("runSubagent reports errors instead of throwing", async () => { + const llm = { + providerName: "anthropic", + streamChat: async function* () { + throw new Error("model exploded"); + }, + } as unknown as ILLM; + + const result = await runSubagent(baseOptions(llm)); + + expect(result.status).toBe("errored"); + expect(result.report).toContain("model exploded"); +}); + +test("runSubagent asks for approval before running a subagent tool call", async () => { + const requestApproval = vi.fn(async (_params: ToolApprovalRequest) => ({ + approved: false, + })); + const llm = fakeLlm([ + [ + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "call-1", + index: 0, + type: "function", + function: { name: BuiltInToolNames.ReadFile, arguments: "{}" }, + }, + ], + }, + ], + [{ role: "assistant", content: "gave up" }], + ]); + + await runSubagent({ ...baseOptions(llm), requestApproval }); + + expect(requestApproval).toHaveBeenCalledTimes(1); + expect(requestApproval.mock.calls[0][0]).toMatchObject({ + toolName: BuiltInToolNames.ReadFile, + agentLabel: "Test task", + }); +}); diff --git a/core/agent/subagentSystemMessage.ts b/core/agent/subagentSystemMessage.ts new file mode 100644 index 00000000000..1b6b17c0f3a --- /dev/null +++ b/core/agent/subagentSystemMessage.ts @@ -0,0 +1,29 @@ +import { Tool } from ".."; + +/** + * A subagent's entire context is this message plus its task prompt. It cannot + * see the parent conversation and cannot ask the user anything, so the prompt + * leans hard on "your last message is the deliverable". + */ +export function subagentSystemMessage(tools: Tool[]): string { + const toolList = tools.length + ? tools.map((t) => `- ${t.function.name}: ${t.displayTitle}`).join("\n") + : "- (none)"; + + return `You are a subagent working on one delegated task for a coding agent. + +You are running autonomously. There is no user to talk to: you cannot ask questions, request clarification, or wait for confirmation. If something is ambiguous, choose the most reasonable interpretation, act on it, and say what you assumed in your report. + +You can see only the task below — not the conversation it came from. Do not assume any shared context. + +Tools available to you: +${toolList} + +Work by investigating first and concluding second. Use your tools to gather real evidence rather than guessing; when you have enough to answer, stop and report. + +Your final message is the entire deliverable — it is the only thing the agent that spawned you will ever see, so it must stand alone. In it: +- Answer the task directly, in the first sentence. +- Cite concrete evidence: file paths, \`file:line\` references, exact names, real snippets. +- Report what you actually found. If you could not determine something, say so plainly rather than filling the gap with a plausible guess. +- Skip preamble, progress narration, and offers of further help.`; +} diff --git a/core/autocomplete/filtering/test/filter.vitest.ts b/core/autocomplete/filtering/test/filter.vitest.ts index 887236f8adf..0d177e395b8 100644 --- a/core/autocomplete/filtering/test/filter.vitest.ts +++ b/core/autocomplete/filtering/test/filter.vitest.ts @@ -23,7 +23,7 @@ describe("Autocomplete filtering tests", () => { beforeAll(async () => { tearDownTestDir(); setUpTestDir(); - addToTestDir([".continueignore"]); + addToTestDir([".shadow-codeignore"]); }); afterAll(async () => { diff --git a/core/autocomplete/templating/validation.ts b/core/autocomplete/templating/validation.ts index 84cffb3e0b7..708eebae126 100644 --- a/core/autocomplete/templating/validation.ts +++ b/core/autocomplete/templating/validation.ts @@ -28,7 +28,7 @@ export const isValidSnippet = (snippet: AutocompleteSnippet): boolean => { if ( (snippet as AutocompleteCodeSnippet).filepath?.startsWith( - "output:extension-output-Continue.continue", + "output:extension-output-shadow-code.shadow-code", ) ) { return false; diff --git a/core/config/ConfigHandler.ts b/core/config/ConfigHandler.ts index 6bebc42a18d..a62ee9b6c08 100644 --- a/core/config/ConfigHandler.ts +++ b/core/config/ConfigHandler.ts @@ -160,7 +160,7 @@ export class ConfigHandler { async getLocalProfiles(options: LoadAssistantFilesOptions) { /** - * Users can define as many local agents as they want in a `.continue/agents` (or previous .continue/assistants) folder + * Users can define as many local agents as they want in a `.shadow-code/agents` (or previous .shadow-code/assistants) folder */ const localProfiles: ProfileLifecycleManager[] = []; diff --git a/core/config/ConfigHandler.vitest.ts b/core/config/ConfigHandler.vitest.ts index 4bb2a7e8554..561ae0effe5 100644 --- a/core/config/ConfigHandler.vitest.ts +++ b/core/config/ConfigHandler.vitest.ts @@ -36,9 +36,9 @@ describe.skip("Test the ConfigHandler and E2E config loading", () => { expect(config.systemMessage).toBe("SYSTEM"); }); - test("should acknowledge override from .continuerc.json", async () => { + test("should acknowledge override from .shadow-coderc.json", async () => { fs.writeFileSync( - path.join(TEST_DIR, ".continuerc.json"), + path.join(TEST_DIR, ".shadow-coderc.json"), JSON.stringify({ systemMessage: "SYSTEM2" }), ); const config = await testConfigHandler.reloadConfig("test"); diff --git a/core/config/createNewAssistantFile.ts b/core/config/createNewAssistantFile.ts index acbc275eca7..672d542b2b0 100644 --- a/core/config/createNewAssistantFile.ts +++ b/core/config/createNewAssistantFile.ts @@ -2,14 +2,14 @@ import { IDE } from ".."; import { joinPathsToUri } from "../util/uri"; const DEFAULT_ASSISTANT_FILE = `# This is an example configuration file -# To learn more, see the full config.yaml reference: https://docs.continue.dev/reference + name: Example Config version: 1.0.0 schema: v1 # Define which models can be used -# https://docs.continue.dev/customization/models + models: - name: my gpt-5 provider: openai @@ -52,7 +52,7 @@ export async function createNewAssistantFile( const baseDirUri = joinPathsToUri( workspaceDirs[0], - assistantPath ?? ".continue/agents", + assistantPath ?? ".shadow-code/agents", ); // Find the first available filename diff --git a/core/config/getWorkspaceContinueRuleDotFiles.ts b/core/config/getWorkspaceContinueRuleDotFiles.ts index 52733e9d75c..2748e7317cd 100644 --- a/core/config/getWorkspaceContinueRuleDotFiles.ts +++ b/core/config/getWorkspaceContinueRuleDotFiles.ts @@ -1,7 +1,7 @@ import { ConfigValidationError } from "@continuedev/config-yaml"; import { IDE, RuleWithSource } from ".."; import { joinPathsToUri } from "../util/uri"; -export const SYSTEM_PROMPT_DOT_FILE = ".continuerules"; +export const SYSTEM_PROMPT_DOT_FILE = ".shadow-coderules"; export async function getWorkspaceContinueRuleDotFiles(ide: IDE) { const dirs = await ide.getWorkspaceDirs(); @@ -17,7 +17,7 @@ export async function getWorkspaceContinueRuleDotFiles(ide: IDE) { rules.push({ rule: content, sourceFile: dotFile, - source: ".continuerules", + source: ".shadow-coderules", }); } } catch (e) { diff --git a/core/config/json/loadRcConfigs.ts b/core/config/json/loadRcConfigs.ts index ceb7de77b54..a3f1be2b978 100644 --- a/core/config/json/loadRcConfigs.ts +++ b/core/config/json/loadRcConfigs.ts @@ -15,7 +15,7 @@ export async function getWorkspaceRcConfigs( (entry) => (entry[1] === (1 as FileType.File) || entry[1] === (64 as FileType.SymbolicLink)) && - entry[0].endsWith(".continuerc.json"), + entry[0].endsWith(".shadow-coderc.json"), ) .map((entry) => joinPathsToUri(dir, entry[0])); return await Promise.all(rcFiles.map(ide.readFile)); diff --git a/core/config/load.ts b/core/config/load.ts index b85150752a2..de07ba9def9 100644 --- a/core/config/load.ts +++ b/core/config/load.ts @@ -669,31 +669,31 @@ async function handleEsbuildInstallation( _ideType: IdeType, ): Promise { // Only check when config.ts is going to be used; never auto-install. - const installCmd = "npm i esbuild@x.x.x --prefix ~/.continue"; + const installCmd = "npm i esbuild@x.x.x --prefix ~/.shadow-code"; // Try to detect a user-installed esbuild (normal resolution) try { await import("esbuild"); return true; // available } catch { - // Try resolving from ~/.continue/node_modules as a courtesy + // Try resolving from ~/.shadow-code/node_modules as a courtesy try { const userEsbuild = path.join( os.homedir(), - ".continue", + ".shadow-code", "node_modules", "esbuild", ); const candidate = require.resolve("esbuild", { paths: [userEsbuild] }); // eslint-disable-next-line @typescript-eslint/no-var-requires require(candidate); - return true; // available via ~/.continue + return true; // available via ~/.shadow-code } catch { // Not available → show friendly instructions and opt out of building await ide.showToast( "error", [ - "config.ts has been deprecated and esbuild is no longer automatically installed by Continue.", + "config.ts has been deprecated and esbuild is no longer automatically installed by Shadow Code.", "To use config.ts, install esbuild manually:", "", ` ${installCmd}`, @@ -713,7 +713,7 @@ async function tryBuildConfigTs() { } } catch (e) { console.log( - `Build error. Please check your ~/.continue/config.ts file: ${e}`, + `Build error. Please check your ~/.shadow-code/config.ts file: ${e}`, ); } } diff --git a/core/config/loadLocalAssistants.ts b/core/config/loadLocalAssistants.ts index 0f7e8e3ffc5..89f7a8cdf7d 100644 --- a/core/config/loadLocalAssistants.ts +++ b/core/config/loadLocalAssistants.ts @@ -15,16 +15,16 @@ import { SYSTEM_PROMPT_DOT_FILE } from "./getWorkspaceContinueRuleDotFiles"; import { SUPPORTED_AGENT_FILES } from "./markdown"; export function isContinueConfigRelatedUri(uri: string): boolean { return ( - uri.endsWith(".continuerc.json") || + uri.endsWith(".shadow-coderc.json") || uri.endsWith(".prompt") || !!SUPPORTED_AGENT_FILES.find((file) => uri.endsWith(`/${file}`)) || uri.endsWith(SYSTEM_PROMPT_DOT_FILE) || - (uri.includes(".continue") && + (uri.includes(".shadow-code") && (uri.endsWith(".yaml") || uri.endsWith(".yml") || uri.endsWith(".json"))) || [...BLOCK_TYPES, "agents", "assistants", "configs"].some((blockType) => - uri.includes(`.continue/${blockType}`), + uri.includes(`.shadow-code/${blockType}`), ) ); } @@ -37,9 +37,9 @@ export function isContinueAgentConfigFile(uri: string): boolean { const normalizedUri = URI.normalize(uri); return ( - normalizedUri.includes(`/.continue/agents/`) || - normalizedUri.includes(`/.continue/assistants/`) || - normalizedUri.includes(`/.continue/configs/`) + normalizedUri.includes(`/.shadow-code/agents/`) || + normalizedUri.includes(`/.shadow-code/assistants/`) || + normalizedUri.includes(`/.shadow-code/configs/`) ); } @@ -109,14 +109,14 @@ export function getDotContinueSubDirs( ): string[] { let fullDirs: string[] = []; - // Workspace .continue/ + // Workspace .shadow-code/ if (options.includeWorkspace) { fullDirs = workspaceDirs.map((dir) => - joinPathsToUri(dir, ".continue", subDirName), + joinPathsToUri(dir, ".shadow-code", subDirName), ); } - // ~/.continue/ + // ~/.shadow-code/ if (options.includeGlobal) { fullDirs.push(localPathToUri(getGlobalFolderWithName(subDirName))); } @@ -125,8 +125,8 @@ export function getDotContinueSubDirs( } /** - * This method searches in both ~/.continue and workspace .continue - * for all YAML/Markdown files in the specified subdirectory, for example .continue/assistants or .continue/prompts + * This method searches in both ~/.shadow-code and workspace .shadow-code + * for all YAML/Markdown files in the specified subdirectory, for example .shadow-code/assistants or .shadow-code/prompts */ export async function getAllDotContinueDefinitionFiles( ide: IDE, diff --git a/core/config/loadLocalAssistants.vitest.ts b/core/config/loadLocalAssistants.vitest.ts index 316687db1d1..1266bf495c2 100644 --- a/core/config/loadLocalAssistants.vitest.ts +++ b/core/config/loadLocalAssistants.vitest.ts @@ -13,13 +13,13 @@ describe("ASSISTANTS getAllDotContinueDefinitionFiles with fileExtType option", // Add test files to the test directory addToTestDir([ - ".continue/assistants/", - [".continue/assistants/assistant1.yaml", "yaml content 1"], - [".continue/assistants/assistant2.yml", "yaml content 2"], - [".continue/assistants/assistant3.md", "markdown content 1"], - [".continue/assistants/assistant4.txt", "txt content"], - [".continue/assistants/config.yaml", "txt content"], - [".continue/assistants/config.yml", "txt content"], + ".shadow-code/assistants/", + [".shadow-code/assistants/assistant1.yaml", "yaml content 1"], + [".shadow-code/assistants/assistant2.yml", "yaml content 2"], + [".shadow-code/assistants/assistant3.md", "markdown content 1"], + [".shadow-code/assistants/assistant4.txt", "txt content"], + [".shadow-code/assistants/config.yaml", "txt content"], + [".shadow-code/assistants/config.yml", "txt content"], ]); }); @@ -156,9 +156,9 @@ describe("ASSISTANTS getAllDotContinueDefinitionFiles with fileExtType option", walkDirCache.invalidate(); setUpTestDir(); addToTestDir([ - ".continue/assistants/", - [".continue/assistants/nonmatch1.txt", "txt content"], - [".continue/assistants/nonmatch2.json", "json content"], + ".shadow-code/assistants/", + [".shadow-code/assistants/nonmatch1.txt", "txt content"], + [".shadow-code/assistants/nonmatch2.json", "json content"], ]); const options: LoadAssistantFilesOptions = { @@ -215,9 +215,9 @@ describe("ASSISTANTS getAllDotContinueDefinitionFiles with fileExtType option", it("should filter by file extension case sensitively", async () => { // Add files with uppercase extensions addToTestDir([ - [".continue/assistants/assistant5.YAML", "uppercase yaml"], - [".continue/assistants/assistant6.YML", "uppercase yml"], - [".continue/assistants/assistant7.MD", "uppercase md"], + [".shadow-code/assistants/assistant5.YAML", "uppercase yaml"], + [".shadow-code/assistants/assistant6.YML", "uppercase yml"], + [".shadow-code/assistants/assistant7.MD", "uppercase md"], ]); const yamlOptions: LoadAssistantFilesOptions = { @@ -273,11 +273,11 @@ describe("AGENTS getAllDotContinueDefinitionFiles with fileExtType option", () = // Add test files to the test directory addToTestDir([ - ".continue/agents/", - [".continue/agents/agent1.yaml", "yaml content 1"], - [".continue/agents/agent2.yml", "yaml content 2"], - [".continue/agents/agent3.md", "markdown content 1"], - [".continue/agents/agent4.txt", "txt content"], + ".shadow-code/agents/", + [".shadow-code/agents/agent1.yaml", "yaml content 1"], + [".shadow-code/agents/agent2.yml", "yaml content 2"], + [".shadow-code/agents/agent3.md", "markdown content 1"], + [".shadow-code/agents/agent4.txt", "txt content"], ]); }); @@ -390,9 +390,9 @@ describe("AGENTS getAllDotContinueDefinitionFiles with fileExtType option", () = walkDirCache.invalidate(); setUpTestDir(); addToTestDir([ - ".continue/agents/", - [".continue/agents/nonmatch1.txt", "txt content"], - [".continue/agents/nonmatch2.json", "json content"], + ".shadow-code/agents/", + [".shadow-code/agents/nonmatch1.txt", "txt content"], + [".shadow-code/agents/nonmatch2.json", "json content"], ]); const options: LoadAssistantFilesOptions = { @@ -449,9 +449,9 @@ describe("AGENTS getAllDotContinueDefinitionFiles with fileExtType option", () = it("should filter by file extension case sensitively", async () => { // Add files with uppercase extensions addToTestDir([ - [".continue/agents/agent5.YAML", "uppercase yaml"], - [".continue/agents/agent6.YML", "uppercase yml"], - [".continue/agents/agent7.MD", "uppercase md"], + [".shadow-code/agents/agent5.YAML", "uppercase yaml"], + [".shadow-code/agents/agent6.YML", "uppercase yml"], + [".shadow-code/agents/agent7.MD", "uppercase md"], ]); const yamlOptions: LoadAssistantFilesOptions = { diff --git a/core/config/markdown/loadCodebaseRules.vitest.ts b/core/config/markdown/loadCodebaseRules.vitest.ts index 81364830acf..351102dedbd 100644 --- a/core/config/markdown/loadCodebaseRules.vitest.ts +++ b/core/config/markdown/loadCodebaseRules.vitest.ts @@ -33,7 +33,7 @@ describe("loadCodebaseRules", () => { '---\nglobs: "**/*.{ts,tsx}"\n---\n# Redux Rules\nUse Redux Toolkit', "file:///workspace/src/components/rules.md": '---\nglobs: ["**/*.tsx", "**/*.jsx"]\n---\n# Component Rules\nUse functional components', - "file:///workspace/.continue/rules.md": + "file:///workspace/.shadow-code/rules.md": "# Global Rules\nFollow project guidelines", }; @@ -59,11 +59,11 @@ describe("loadCodebaseRules", () => { source: "colocated-markdown", sourceFile: "file:///workspace/src/components/rules.md", }, - "file:///workspace/.continue/rules.md": { + "file:///workspace/.shadow-code/rules.md": { name: "Global Rules", rule: "Follow project guidelines", source: "colocated-markdown", - sourceFile: "file:///workspace/.continue/rules.md", + sourceFile: "file:///workspace/.shadow-code/rules.md", }, }; @@ -114,7 +114,7 @@ describe("loadCodebaseRules", () => { "file:///workspace/src/components/rules.md", ); expect(mockIde.readFile).toHaveBeenCalledWith( - "file:///workspace/.continue/rules.md", + "file:///workspace/.shadow-code/rules.md", ); // Should convert all rules @@ -132,7 +132,7 @@ describe("loadCodebaseRules", () => { mockConvertedRules["file:///workspace/src/components/rules.md"], ); expect(rules).toContainEqual( - mockConvertedRules["file:///workspace/.continue/rules.md"], + mockConvertedRules["file:///workspace/.shadow-code/rules.md"], ); // Should not have errors @@ -159,7 +159,7 @@ describe("loadCodebaseRules", () => { mockConvertedRules["file:///workspace/src/components/rules.md"], ); expect(rules).toContainEqual( - mockConvertedRules["file:///workspace/.continue/rules.md"], + mockConvertedRules["file:///workspace/.shadow-code/rules.md"], ); // Should have one error diff --git a/core/config/markdown/loadMarkdownRules.ts b/core/config/markdown/loadMarkdownRules.ts index d77313b9255..586161744af 100644 --- a/core/config/markdown/loadMarkdownRules.ts +++ b/core/config/markdown/loadMarkdownRules.ts @@ -9,7 +9,7 @@ import { getAllDotContinueDefinitionFiles } from "../loadLocalAssistants"; export const SUPPORTED_AGENT_FILES = ["AGENTS.md", "AGENT.md", "CLAUDE.md"]; /** - * Loads rules from markdown files in the .continue/rules and .continue/prompts directories + * Loads rules from markdown files in the .shadow-code/rules and .shadow-code/prompts directories * and agent files (AGENTS.md, AGENT.md, CLAUDE.md) at workspace root */ export async function loadMarkdownRules(ide: IDE): Promise<{ @@ -54,7 +54,7 @@ export async function loadMarkdownRules(ide: IDE): Promise<{ } } - // Load markdown files from both .continue/rules and .continue/prompts + // Load markdown files from both .shadow-code/rules and .shadow-code/prompts const dirsToCheck = [RULES_DIR_NAME, PROMPTS_DIR_NAME]; for (const dirName of dirsToCheck) { diff --git a/core/config/markdown/ruleCollocationApplication.vitest.ts b/core/config/markdown/ruleCollocationApplication.vitest.ts index 439d337cacf..a95154b9b4b 100644 --- a/core/config/markdown/ruleCollocationApplication.vitest.ts +++ b/core/config/markdown/ruleCollocationApplication.vitest.ts @@ -17,7 +17,7 @@ describe("Rule Colocation Application", () => { name: "Root Rule", rule: "Follow project standards", source: "colocated-markdown", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }, // Nested directory rule without globs - should only apply to files in that directory diff --git a/core/config/markdown/utils.ts b/core/config/markdown/utils.ts index df64a75be7b..95c3c51cebb 100644 --- a/core/config/markdown/utils.ts +++ b/core/config/markdown/utils.ts @@ -6,7 +6,7 @@ import { joinPathsToUri } from "../../util/uri"; function createRelativeRuleFilePathParts(ruleName: string): string[] { const safeRuleName = sanitizeRuleName(ruleName); - return [".continue", "rules", `${safeRuleName}.${RULE_FILE_EXTENSION}`]; + return [".shadow-code", "rules", `${safeRuleName}.${RULE_FILE_EXTENSION}`]; } export function createRelativeRuleFilePath(ruleName: string): string { @@ -14,7 +14,7 @@ export function createRelativeRuleFilePath(ruleName: string): string { } /** - * Creates the file path for a rule in the workspace .continue/rules directory + * Creates the file path for a rule in the workspace .shadow-code/rules directory */ export function createRuleFilePath( workspaceDir: string, diff --git a/core/config/markdown/utils.vitest.ts b/core/config/markdown/utils.vitest.ts index 57f20abe7de..486f398ae2e 100644 --- a/core/config/markdown/utils.vitest.ts +++ b/core/config/markdown/utils.vitest.ts @@ -4,16 +4,16 @@ import { createRuleFilePath } from "./utils"; describe("createRuleFilePath", () => { it("should create correct rule file path", () => { const result = createRuleFilePath("/workspace", "My Test Rule"); - expect(result).toBe("/workspace/.continue/rules/my-test-rule.md"); + expect(result).toBe("/workspace/.shadow-code/rules/my-test-rule.md"); }); it("should handle special characters in rule name", () => { const result = createRuleFilePath("/home/user", "Rule with @#$% chars"); - expect(result).toBe("/home/user/.continue/rules/rule-with-chars.md"); + expect(result).toBe("/home/user/.shadow-code/rules/rule-with-chars.md"); }); it("should handle edge case rule names", () => { const result = createRuleFilePath("/test", " Multiple Spaces "); - expect(result).toBe("/test/.continue/rules/multiple-spaces.md"); + expect(result).toBe("/test/.shadow-code/rules/multiple-spaces.md"); }); }); diff --git a/core/config/profile/LocalProfileLoader.vitest.ts b/core/config/profile/LocalProfileLoader.vitest.ts index d8f11861bba..4e230238123 100644 --- a/core/config/profile/LocalProfileLoader.vitest.ts +++ b/core/config/profile/LocalProfileLoader.vitest.ts @@ -20,7 +20,7 @@ describe("LocalProfileLoader", () => { it("should pass pre-read content in packageIdentifier for override files", async () => { const overrideFile = { - path: "vscode-remote://wsl+Ubuntu/home/user/.continue/agents/test.yaml", + path: "vscode-remote://wsl+Ubuntu/home/user/.shadow-code/agents/test.yaml", content: "name: Test\nversion: 1.0.0\nschema: v1\n", }; diff --git a/core/config/profile/doLoadConfig.ts b/core/config/profile/doLoadConfig.ts index b2fd4ed0891..119b98d7390 100644 --- a/core/config/profile/doLoadConfig.ts +++ b/core/config/profile/doLoadConfig.ts @@ -41,13 +41,13 @@ async function loadRules(ide: IDE) { const rules: RuleWithSource[] = []; const errors = []; - // Add rules from .continuerules files + // Add rules from .shadow-coderules files const { rules: yamlRules, errors: continueRulesErrors } = await getWorkspaceContinueRuleDotFiles(ide); rules.unshift(...yamlRules); errors.push(...continueRulesErrors); - // Add rules from markdown files in .continue/rules + // Add rules from markdown files in .shadow-code/rules const { rules: markdownRules, errors: markdownRulesErrors } = await loadMarkdownRules(ide); rules.unshift(...markdownRules); diff --git a/core/config/profile/doLoadConfig.vitest.ts b/core/config/profile/doLoadConfig.vitest.ts index 5ff8512e6bd..e11b5878c8e 100644 --- a/core/config/profile/doLoadConfig.vitest.ts +++ b/core/config/profile/doLoadConfig.vitest.ts @@ -115,7 +115,7 @@ describe("doLoadConfig pre-read content bypass", () => { const packageIdentifier: PackageIdentifier = { uriType: "file", fileUri: - "vscode-remote://wsl+Ubuntu/home/user/.continue/agents/test.yaml", + "vscode-remote://wsl+Ubuntu/home/user/.shadow-code/agents/test.yaml", content: "name: Test\nversion: 1.0.0\nschema: v1\n", }; @@ -140,7 +140,7 @@ describe("doLoadConfig pre-read content bypass", () => { const packageIdentifier: PackageIdentifier = { uriType: "file", fileUri: - "vscode-remote://wsl+Ubuntu/home/user/.continue/agents/test.yaml", + "vscode-remote://wsl+Ubuntu/home/user/.shadow-code/agents/test.yaml", }; await doLoadConfig({ diff --git a/core/config/util.ts b/core/config/util.ts index 28536a06d37..e8063e91806 100644 --- a/core/config/util.ts +++ b/core/config/util.ts @@ -184,9 +184,7 @@ async function showUnsupportedCpuToast(ide: IDE) { ); if (shouldOpenLink) { - void ide.openUrl( - "https://docs.continue.dev/troubleshooting#i-received-a-codebase-indexing-disabled---your-linux-system-lacks-required-cpu-features-avx2-fma-notification", - ); + void ide.openUrl(""); } } diff --git a/core/config/validation.ts b/core/config/validation.ts index 228ee1c5ceb..6d58d18c810 100644 --- a/core/config/validation.ts +++ b/core/config/validation.ts @@ -64,7 +64,7 @@ export function validateConfig(config: SerializedContinueConfig) { ) { errors.push({ fatal: false, - message: `${modelDescription.model} is not trained for tab-autocomplete, and will result in low-quality suggestions. See the docs to learn more about why: https://docs.continue.dev/features/tab-autocomplete#i-want-better-completions-should-i-use-gpt-4`, + message: `${modelDescription.model} is not trained for tab-autocomplete, and will result in low-quality suggestions.`, }); } } diff --git a/core/config/workspace/workspaceBlocks.ts b/core/config/workspace/workspaceBlocks.ts index 6dfe09a4ce3..93830cb4f0b 100644 --- a/core/config/workspace/workspaceBlocks.ts +++ b/core/config/workspace/workspaceBlocks.ts @@ -56,7 +56,7 @@ function getContentsForNewBlock(blockType: BlockType): ConfigYaml { configYaml.docs = [ { name: "New docs", - startUrl: "https://docs.continue.dev", + startUrl: "https://example.com", }, ]; break; @@ -166,7 +166,10 @@ export async function createNewWorkspaceBlockFile( ); } - const baseDirUri = joinPathsToUri(workspaceDirs[0], `.continue/${blockType}`); + const baseDirUri = joinPathsToUri( + workspaceDirs[0], + `.shadow-code/${blockType}`, + ); const fileUri = await findAvailableFilename( baseDirUri, diff --git a/core/config/workspace/workspaceBlocks.vitest.ts b/core/config/workspace/workspaceBlocks.vitest.ts index 2961b53d7f8..2b876d7aab6 100644 --- a/core/config/workspace/workspaceBlocks.vitest.ts +++ b/core/config/workspace/workspaceBlocks.vitest.ts @@ -27,7 +27,7 @@ describe("getFileContent", () => { const docsResult = getFileContent("docs"); expect(docsResult).toContain("name: New doc"); expect(docsResult).toContain("docs:"); - expect(docsResult).toContain("startUrl: https://docs.continue.dev"); + expect(docsResult).toContain("startUrl: https://example.com"); const promptsResult = getFileContent("prompts"); expect(promptsResult).toContain("name: New prompt"); @@ -48,33 +48,33 @@ describe("findAvailableFilename", () => { const mockFileExists = async (uri: string) => false; const result = await findAvailableFilename( - "/workspace/.continue/models", + "/workspace/.shadow-code/models", "models", mockFileExists, ); - expect(result).toBe("/workspace/.continue/models/new-model.yaml"); + expect(result).toBe("/workspace/.shadow-code/models/new-model.yaml"); }); test("returns filename with counter when base exists", async () => { const mockFileExists = async (uri: string) => { - return uri === "/workspace/.continue/models/new-model.yaml"; + return uri === "/workspace/.shadow-code/models/new-model.yaml"; }; const result = await findAvailableFilename( - "/workspace/.continue/models", + "/workspace/.shadow-code/models", "models", mockFileExists, ); - expect(result).toBe("/workspace/.continue/models/new-model-1.yaml"); + expect(result).toBe("/workspace/.shadow-code/models/new-model-1.yaml"); }); test("increments counter until available filename is found", async () => { const existingFiles = new Set([ - "/workspace/.continue/context/new-context.yaml", - "/workspace/.continue/context/new-context-1.yaml", - "/workspace/.continue/context/new-context-2.yaml", + "/workspace/.shadow-code/context/new-context.yaml", + "/workspace/.shadow-code/context/new-context-1.yaml", + "/workspace/.shadow-code/context/new-context-2.yaml", ]); const mockFileExists = async (uri: string) => { @@ -82,12 +82,12 @@ describe("findAvailableFilename", () => { }; const result = await findAvailableFilename( - "/workspace/.continue/context", + "/workspace/.shadow-code/context", "context", mockFileExists, ); - expect(result).toBe("/workspace/.continue/context/new-context-3.yaml"); + expect(result).toBe("/workspace/.shadow-code/context/new-context-3.yaml"); }); test("handles different block types correctly with proper extensions", async () => { @@ -127,8 +127,8 @@ describe("findAvailableFilename", () => { test("handles rules markdown files with counter", async () => { const existingFiles = new Set([ - `/workspace/.continue/rules/new-rule.${RULE_FILE_EXTENSION}`, - `/workspace/.continue/rules/new-rule-1.${RULE_FILE_EXTENSION}`, + `/workspace/.shadow-code/rules/new-rule.${RULE_FILE_EXTENSION}`, + `/workspace/.shadow-code/rules/new-rule-1.${RULE_FILE_EXTENSION}`, ]); const mockFileExists = async (uri: string) => { @@ -136,13 +136,13 @@ describe("findAvailableFilename", () => { }; const result = await findAvailableFilename( - "/workspace/.continue/rules", + "/workspace/.shadow-code/rules", "rules", mockFileExists, ); expect(result).toBe( - `/workspace/.continue/rules/new-rule-2.${RULE_FILE_EXTENSION}`, + `/workspace/.shadow-code/rules/new-rule-2.${RULE_FILE_EXTENSION}`, ); }); @@ -150,8 +150,8 @@ describe("findAvailableFilename", () => { const existingFiles = new Set( Array.from({ length: 100 }, (_, i) => i === 0 - ? "/workspace/.continue/prompts/new-prompt.md" - : `/workspace/.continue/prompts/new-prompt-${i}.md`, + ? "/workspace/.shadow-code/prompts/new-prompt.md" + : `/workspace/.shadow-code/prompts/new-prompt-${i}.md`, ), ); @@ -160,11 +160,11 @@ describe("findAvailableFilename", () => { }; const result = await findAvailableFilename( - "/workspace/.continue/prompts", + "/workspace/.shadow-code/prompts", "prompts", mockFileExists, ); - expect(result).toBe("/workspace/.continue/prompts/new-prompt-100.md"); + expect(result).toBe("/workspace/.shadow-code/prompts/new-prompt-100.md"); }); }); diff --git a/core/config/yaml/LocalPlatformClient.ts b/core/config/yaml/LocalPlatformClient.ts index c03ce387bb1..d1a2b0b0c09 100644 --- a/core/config/yaml/LocalPlatformClient.ts +++ b/core/config/yaml/LocalPlatformClient.ts @@ -13,7 +13,7 @@ export class LocalPlatformClient implements PlatformClient { constructor(private readonly ide: IDE) {} /** - * searches for the first valid secret file in order of ~/.continue/.env, /.continue/.env, /.env + * searches for the first valid secret file in order of ~/.shadow-code/.env, /.shadow-code/.env, /.env */ private async findSecretInEnvFiles( fqsn: FQSN, @@ -43,7 +43,7 @@ export class LocalPlatformClient implements PlatformClient { return dotEnv[fqsn.secretName]; } catch (error) { console.warn( - `Error reading ~/.continue/.env file: ${error instanceof Error ? error.message : String(error)}`, + `Error reading ~/.shadow-code/.env file: ${error instanceof Error ? error.message : String(error)}`, ); return undefined; } @@ -58,7 +58,7 @@ export class LocalPlatformClient implements PlatformClient { for (const folder of workspaceDirs) { const envFilePath = joinPathsToUri( folder, - insideContinue ? ".continue" : "", + insideContinue ? ".shadow-code" : "", ".env", ); try { diff --git a/core/config/yaml/LocalPlatformClient.vitest.ts b/core/config/yaml/LocalPlatformClient.vitest.ts index c058e0f570a..f3f3d6f863c 100644 --- a/core/config/yaml/LocalPlatformClient.vitest.ts +++ b/core/config/yaml/LocalPlatformClient.vitest.ts @@ -81,7 +81,7 @@ describe("LocalPlatformClient", () => { utilPaths.getContinueDotEnv = getContinueDotEnv; }); - test("should be able to get secrets from ~/.continue/.env files", async () => { + test("should be able to get secrets from ~/.shadow-code/.env files", async () => { const localPlatformClient = new LocalPlatformClient(testIde); const resolvedFQSNs = await localPlatformClient.resolveFQSNs([testFQSN]); expect(getContinueDotEnv).toHaveBeenCalled(); @@ -93,7 +93,7 @@ describe("LocalPlatformClient", () => { }); describe("should be able to get secrets from workspace .env files", () => { - test("should get secrets from /.continue/.env and /.env", async () => { + test("should get secrets from /.shadow-code/.env and /.env", async () => { const originalIdeFileExists = testIde.fileExists; testIde.fileExists = vi.fn(async (fileUri: string) => fileUri.includes(".env") ? true : originalIdeFileExists(fileUri), @@ -106,14 +106,14 @@ describe("LocalPlatformClient", () => { "dotenv-" + Math.floor(Math.random() * 100); testIde.readFile = vi.fn(async (fileUri: string) => { - // fileUri should contain .continue/.env and not .env - if (fileUri.match(/.*\.continue\/\.env.*/gi)?.length) { + // fileUri should contain .shadow-code/.env and not .env + if (fileUri.match(/.*\.shadow-code\/\.env.*/gi)?.length) { return ( envKeyValuesString.split("\n")[0] + randomValueForContinueDirDotEnv ); } - // filUri should contain .env and not .continue/.env - else if (fileUri.match(/.*(? { expect(dotEnvSecretValue).toContain(randomValueForWorkspaceDotEnv); }); - test("should first get secrets from /.continue/.env and then /.env", async () => { + test("should first get secrets from /.shadow-code/.env and then /.env", async () => { const originalIdeFileExists = testIde.fileExists; testIde.fileExists = vi.fn(async (fileUri: string) => fileUri.includes(".env") ? true : originalIdeFileExists(fileUri), @@ -156,14 +156,14 @@ describe("LocalPlatformClient", () => { const originalIdeReadFile = testIde.readFile; testIde.readFile = vi.fn(async (fileUri: string) => { - // fileUri should contain .continue/.env and not .env - if (fileUri.match(/.*\.continue\/\.env.*/gi)?.length) { + // fileUri should contain .shadow-code/.env and not .env + if (fileUri.match(/.*\.shadow-code\/\.env.*/gi)?.length) { return ( envKeyValuesString.split("\n")[0] + randomValueForContinueDirDotEnv ); } - // filUri should contain .env and not .continue/.env - else if (fileUri.match(/.*(? { expect( (resolvedFQSNs[0] as SecretResult & { value: unknown })?.value, ).toContain(secretValue); - // we check that workspace .continue/.env does not override the /.env secret + // we check that workspace .shadow-code/.env does not override the /.env secret expect( (resolvedFQSNs[0] as SecretResult & { value: unknown })?.value, ).toContain(randomValueForContinueDirDotEnv); @@ -244,7 +244,7 @@ describe("LocalPlatformClient", () => { expect(resolvedFQSNs[0]).toBeUndefined(); }); - test("should prioritize local ~/.continue/.env file over process.env", async () => { + test("should prioritize local ~/.shadow-code/.env file over process.env", async () => { const localEnvFileValue = "secret-from-local-dot-continue-env"; const utilPaths = await import("../../util/paths"); utilPaths.getContinueDotEnv = vi.fn(() => ({ @@ -269,11 +269,11 @@ describe("LocalPlatformClient", () => { test("should prioritize workspace .env files over process.env", async () => { const workspaceContinueEnvValue = "secret-from-workspace-continue-env"; testIde.fileExists = vi.fn(async (fileUri: string) => - // Only mock existence for /.continue/.env - fileUri.includes(".continue/.env"), + // Only mock existence for /.shadow-code/.env + fileUri.includes(".shadow-code/.env"), ); testIde.readFile = vi.fn(async (fileUri: string) => { - if (fileUri.includes(".continue/.env")) { + if (fileUri.includes(".shadow-code/.env")) { return `${testFQSN.secretName}=${workspaceContinueEnvValue}`; } return ""; diff --git a/core/config/yaml/loadYaml.ts b/core/config/yaml/loadYaml.ts index b60a7290603..75ce10e5ccc 100644 --- a/core/config/yaml/loadYaml.ts +++ b/core/config/yaml/loadYaml.ts @@ -51,7 +51,7 @@ async function loadConfigYaml(options: { }): Promise> { const { overrideConfigYaml, ideSettings, ide, packageIdentifier } = options; - // Add local .continue blocks + // Add local .shadow-code blocks // Use "content" field to pass pre-read content directly, avoiding // fs.readFileSync which fails for vscode-remote:// URIs in WSL (#6242, #7810) const localBlockPromises = BLOCK_TYPES.map(async (blockType) => { diff --git a/core/config/yaml/models.vitest.ts b/core/config/yaml/models.vitest.ts index 0e99448d51d..0692f3f3469 100644 --- a/core/config/yaml/models.vitest.ts +++ b/core/config/yaml/models.vitest.ts @@ -52,7 +52,7 @@ describe("llmsFromModelConfig requestOptions merging", () => { requestOptions: { timeout: 30000, headers: { - "user-agent": "Continue/1.0.0", + "user-agent": "ShadowCode/1.0.0", }, proxy: "global-proxy", }, @@ -114,7 +114,7 @@ describe("llmsFromModelConfig requestOptions merging", () => { expect(llm.requestOptions).toEqual({ timeout: 60000, // model-specific takes precedence headers: { - "user-agent": "Continue/1.0.0", // from global request options + "user-agent": "ShadowCode/1.0.0", // from global request options Authorization: "Bearer token123", // from model }, proxy: "model-proxy", // model-specific takes precedence @@ -238,7 +238,7 @@ describe("llmsFromModelConfig requestOptions merging", () => { expect(llm.requestOptions).toEqual({ timeout: 120000, // model-specific takes precedence headers: { - "user-agent": "Continue/1.0.0", // from global request options + "user-agent": "ShadowCode/1.0.0", // from global request options "X-Custom": "autodetect", // from model }, proxy: "global-proxy", // from global request options @@ -304,7 +304,7 @@ describe("llmsFromModelConfig requestOptions merging", () => { expect(llm.requestOptions).toEqual({ timeout: 90000, headers: { - "user-agent": "Continue/1.0.0", + "user-agent": "ShadowCode/1.0.0", }, proxy: "global-proxy", }); @@ -347,7 +347,7 @@ describe("llmsFromModelConfig requestOptions merging", () => { const llm = result[0]; expect(llm.requestOptions?.headers).toEqual({ - "user-agent": "Continue/1.0.0", // from global request options + "user-agent": "ShadowCode/1.0.0", // from global request options "Cache-Control": "no-cache", // from global request options Authorization: "Bearer model-token", // from model Accept: "application/json", // from model (overrides config) diff --git a/core/context/mcp/json/loadJsonMcpConfigs.ts b/core/context/mcp/json/loadJsonMcpConfigs.ts index 5600a27eec4..06011ddad2a 100644 --- a/core/context/mcp/json/loadJsonMcpConfigs.ts +++ b/core/context/mcp/json/loadJsonMcpConfigs.ts @@ -22,7 +22,7 @@ import { localPathToUri } from "../../../util/pathToUri"; import { getUriPathBasename, joinPathsToUri } from "../../../util/uri"; /** - * Loads MCP configs from JSON files in ~/.continue/mcpServers and workspace .continue/mcpServers + * Loads MCP configs from JSON files in ~/.shadow-code/mcpServers and workspace .shadow-code/mcpServers */ export async function loadJsonMcpConfigs( ide: IDE, @@ -37,7 +37,7 @@ export async function loadJsonMcpConfigs( // Get dirs const workspaceDirs = await ide.getWorkspaceDirs(); const mcpDirs = workspaceDirs.map((dir) => - joinPathsToUri(dir, ".continue", "mcpServers"), + joinPathsToUri(dir, ".shadow-code", "mcpServers"), ); if (includeGlobal) { mcpDirs.push(localPathToUri(getGlobalFolderWithName("mcpServers"))); diff --git a/core/core.ts b/core/core.ts index e8f323e13f4..7daf2296bb4 100644 --- a/core/core.ts +++ b/core/core.ts @@ -113,6 +113,15 @@ export class Core { this.messageAbortControllers.get(messageId)?.abort(); } + // `tools/call` is a plain request rather than a generator, so it never gets a + // controller from addMessageAbortController and the GUI's `abort` message + // can't reach it. Long-running tools (the subagent runner especially) need + // their own kill switch, keyed by tool call id and driven by `tools/cancel`. + private toolCallAbortControllers = new Map(); + private abortToolCall(toolCallId: string) { + this.toolCallAbortControllers.get(toolCallId)?.abort(); + } + invoke( messageType: T, data: ToCoreProtocol[T][0], @@ -135,7 +144,7 @@ export class Core { private readonly ide: IDE, ) { try { - // Ensure .continue directory is created + // Ensure .shadow-code directory is created migrateV1DevDataFiles(); const ideInfoPromise = messenger.request("getIdeInfo", undefined); @@ -147,7 +156,7 @@ export class Core { executeTool: (toolCall, sessionId) => this.handleToolCall(toolCall, sessionId), requestApproval: (params) => - this.messenger.request("claudeCodeCli/authorizeToolCall", params), + this.messenger.request("agent/authorizeToolCall", params), }); setShadowCodeToolsMcpServer(this.shadowCodeToolsMcpServer); void this.shadowCodeToolsMcpServer.ensureStarted().catch((e) => { @@ -884,7 +893,7 @@ export class Core { await this.configHandler.refreshAll("Local config file created"); } else if (nonColocatedRuleUris.some(isContinueConfigRelatedUri)) { await this.configHandler.reloadConfig( - ".continue config-related file created", + ".shadow-code config-related file created", ); } }); @@ -916,7 +925,7 @@ export class Core { await this.configHandler.refreshAll("Local config file deleted"); } else if (nonColocatedRuleUris.some(isContinueConfigRelatedUri)) { await this.configHandler.reloadConfig( - ".continue config-related file deleted", + ".shadow-code config-related file deleted", ); } }); @@ -1065,6 +1074,10 @@ export class Core { this.handleToolCall(toolCall, sessionId), ); + on("tools/cancel", ({ data: { toolCallId } }) => { + this.abortToolCall(toolCallId); + }); + on( "tools/evaluatePolicy", async ({ data: { toolName, basePolicy, parsedArgs, processedArgs } }) => { @@ -1164,7 +1177,11 @@ export class Core { }); } - private async handleToolCall(toolCall: ToolCall, sessionId?: string) { + private async handleToolCall( + toolCall: ToolCall, + sessionId?: string, + subagentDepth = 0, + ) { const { config } = await this.configHandler.loadConfig(); if (!config) { throw new Error("Config not loaded"); @@ -1190,20 +1207,33 @@ export class Core { this.messenger.send("toolCallPartialOutput", params); }; - const result = await callTool(tool, toolCall, { - config, - ide: this.ide, - llm: config.selectedModelByRole.chat, - fetch: (url, init) => - fetchwithRequestOptions(url, init, config.requestOptions), - tool, - toolCallId: toolCall.id, - onPartialOutput, - codeBaseIndexer: this.codeBaseIndexer, - sessionId, - }); + const abortController = new AbortController(); + if (toolCall.id) { + this.toolCallAbortControllers.set(toolCall.id, abortController); + } - return result; + try { + return await callTool(tool, toolCall, { + config, + ide: this.ide, + llm: config.selectedModelByRole.chat, + fetch: (url, init) => + fetchwithRequestOptions(url, init, config.requestOptions), + tool, + toolCallId: toolCall.id, + onPartialOutput, + codeBaseIndexer: this.codeBaseIndexer, + sessionId, + signal: abortController.signal, + requestApproval: (params) => + this.messenger.request("agent/authorizeToolCall", params), + subagentDepth, + }); + } finally { + if (toolCall.id) { + this.toolCallAbortControllers.delete(toolCall.id); + } + } } private async isItemTooBig(item: ContextItemWithId) { @@ -1302,7 +1332,7 @@ export class Core { "Local config-related file updated", ); } else if ( - uri.endsWith(".continueignore") || + uri.endsWith(".shadow-codeignore") || uri.endsWith(".gitignore") ) { // Reindex the workspaces diff --git a/core/data/log.ts b/core/data/log.ts index 57e00e4bfbe..d5b0a736555 100644 --- a/core/data/log.ts +++ b/core/data/log.ts @@ -54,8 +54,8 @@ export class DataLogger { } if ("userAgent" in zodSchema.shape) { newBody.userAgent = ideInfo - ? `${ideInfo.name}/${ideInfo.version} (Continue/${ideInfo.extensionVersion})` - : "Unknown/Unknown (Continue/Unknown)"; + ? `${ideInfo.name}/${ideInfo.version} (ShadowCode/${ideInfo.extensionVersion})` + : "Unknown/Unknown (ShadowCode/Unknown)"; } if ("selectedProfileId" in zodSchema.shape) { newBody.selectedProfileId = diff --git a/core/data/shadowChatDb.ts b/core/data/shadowChatDb.ts index 9219ae4bc63..f544417ab09 100644 --- a/core/data/shadowChatDb.ts +++ b/core/data/shadowChatDb.ts @@ -9,7 +9,7 @@ import { DatabaseConnection } from "../indexing/refreshIndex.js"; import { ChatMessage } from "../index.js"; function getShadowChatDbPath(): string { - const devDataDir = path.join(os.homedir(), ".continue", "devdata"); + const devDataDir = path.join(os.homedir(), ".shadow-code", "devdata"); if (!fs.existsSync(devDataDir)) { fs.mkdirSync(devDataDir, { recursive: true }); } diff --git a/core/index.d.ts b/core/index.d.ts index 2f320b78b27..b6bc6db6ff3 100644 --- a/core/index.d.ts +++ b/core/index.d.ts @@ -364,6 +364,13 @@ export interface ToolCall { export interface ToolCallDelta { id?: string; + /** + * Position of this call within the assistant turn. OpenAI-style streams send + * `id` only on the delta that opens each tool call; every later argument + * fragment identifies its call by index alone. Without this, parallel tool + * calls get merged into whichever id was seen most recently. + */ + index?: number; type?: "function"; function?: { name?: string; @@ -515,6 +522,12 @@ interface McpUiState { // Will exist only on "assistant" messages with tool calls interface ToolCallState { toolCallId: string; + /** + * Position of this call within its assistant turn, from ToolCallDelta.index. + * Needed to route argument fragments that arrive with an index but no id - + * see applyToolCallDelta. Absent for providers that don't send an index. + */ + streamIndex?: number; toolCall: ToolCall; status: ToolStatus; parsedArgs: any; @@ -1108,6 +1121,25 @@ export interface Prediction { }[]; } +/** + * A request to the GUI to resolve a tool call's policy and, if the policy + * requires it, prompt the user. Used by anything executing tool calls that did + * NOT originate from the GUI's own streaming loop - the Claude Code CLI's MCP + * server and the subagent runner - since Core's handleToolCall otherwise + * executes unconditionally and the GUI is normally the only gate. + */ +export interface ToolApprovalRequest { + approvalId: string; + toolCallId: string; + sessionId: string | undefined; + toolName: string; + args: Record; + displayTitle?: string; + wouldLikeTo?: string; + /** Which agent is asking, e.g. a subagent's task description. */ + agentLabel?: string; +} + export interface ToolExtras { ide: IDE; llm: ILLM; @@ -1121,6 +1153,23 @@ export interface ToolExtras { config: ContinueConfig; codeBaseIndexer?: CodebaseIndexer; sessionId?: string; + /** + * Aborted when the user presses Stop. Long-running implementations (the + * subagent runner above all) must honor this - `tools/call` is a plain + * request with no cancellation of its own, so without this a tool keeps + * running after Stop. + */ + signal?: AbortSignal; + /** See ToolApprovalRequest. Absent when no GUI is attached. */ + requestApproval?: ( + params: ToolApprovalRequest, + ) => Promise<{ approved: boolean }>; + /** + * 0 for a tool call made by the main chat, 1 for one made by a subagent. + * spawn_subagents refuses to run at depth > 0, which is what makes subagent + * recursion structurally impossible. + */ + subagentDepth?: number; } export interface McpToolMeta { @@ -1224,6 +1273,22 @@ export interface BaseCompletionOptions { * tool calls) use the real id rather than re-deriving a different one. */ shadowSessionId?: string; + /** + * Identifies one agent run (the main chat turn, or a single subagent spawned + * by spawn_subagents) so providers keeping per-run side-channel state can key + * on it instead of on shadowSessionId. Several subagents share a + * shadowSessionId with their parent but each needs its own state - notably + * ClaudeCodeCli, where the MCP tool registration and the temp --mcp-config + * file would otherwise be overwritten/unlinked by whichever run finishes + * first. Defaults to shadowSessionId when unset. + */ + agentRunId?: string; + /** + * Human-readable name for the agent making this call (a subagent's task + * description), shown on tool-approval prompts so the user can tell which + * agent is asking. + */ + agentLabel?: string; } export interface ModelCapability { @@ -1925,7 +1990,7 @@ export type RuleSource = | "rules-block" | "colocated-markdown" | "json-systemMessage" - | ".continuerules" + | ".shadow-coderules" | "agentFile"; export interface RuleMetadata { diff --git a/core/indexing/continueignore.ts b/core/indexing/continueignore.ts index a1beffb3816..62e2664dd1d 100644 --- a/core/indexing/continueignore.ts +++ b/core/indexing/continueignore.ts @@ -14,7 +14,7 @@ export const getWorkspaceContinueIgArray = async (ide: IDE) => { async (accPromise, dir) => { const acc = await accPromise; try { - const contents = await ide.readFile(`${dir}/.continueignore`); + const contents = await ide.readFile(`${dir}/.shadow-codeignore`); return [...acc, ...gitIgArrayFromFile(contents)]; } catch (err) { console.error(err); diff --git a/core/indexing/ignore.ts b/core/indexing/ignore.ts index e431e24eed9..a96d1903163 100644 --- a/core/indexing/ignore.ts +++ b/core/indexing/ignore.ts @@ -148,7 +148,7 @@ export const ADDITIONAL_INDEXING_IGNORE_FILETYPES = [ "go.sum", "*.gitignore", "*.gitkeep", - "*.continueignore", + "*.shadow-codeignore", "*.csv", "*.uasset", "*.pdb", @@ -158,7 +158,7 @@ export const ADDITIONAL_INDEXING_IGNORE_FILETYPES = [ "*.jsonl", // "*.prompt", // can be incredibly confusing for the LLM to have another set of instructions injected into the prompt // Application specific - ".continue/", + ".shadow-code/", ]; export const ADDITIONAL_INDEXING_IGNORE_DIRS = [ diff --git a/core/indexing/ignore.vitest.ts b/core/indexing/ignore.vitest.ts index 565db5efd15..d2d1405078a 100644 --- a/core/indexing/ignore.vitest.ts +++ b/core/indexing/ignore.vitest.ts @@ -105,7 +105,7 @@ describe("isSecurityConcern", () => { }); it("should detect continue directory as security concern", () => { - expect(isSecurityConcern(".continue/config.json")).toBe(true); + expect(isSecurityConcern(".shadow-code/config.json")).toBe(true); }); it("should detect temporary secret directories as security concerns", () => { diff --git a/core/indexing/shouldIgnore.test.ts b/core/indexing/shouldIgnore.test.ts index c1935e7d99c..e818a5501d1 100644 --- a/core/indexing/shouldIgnore.test.ts +++ b/core/indexing/shouldIgnore.test.ts @@ -30,10 +30,10 @@ describe("shouldIgnore", () => { ]); expect(result).toBe(true); }); - test("should return true if a folder is ignored by .continueignore", async () => { + test("should return true if a folder is ignored by .shadow-codeignore", async () => { addToTestDir([ ["ignored-folder/file.txt", "content"], - [".continueignore", "ignored-folder/"], + [".shadow-codeignore", "ignored-folder/"], ]); const result = await shouldIgnore( TEST_DIR + "/ignored-folder/file.txt", @@ -60,11 +60,11 @@ describe("shouldIgnore", () => { expect(result).toBe(true); }); - test("should return true if a .continueignore override ignores file", async () => { + test("should return true if a .shadow-codeignore override ignores file", async () => { addToTestDir([ ["override-file.txt", "content"], [".gitignore", "override-file.txt"], - [".continueignore", "!override-file.txt"], + [".shadow-codeignore", "!override-file.txt"], ]); const result = await shouldIgnore( TEST_DIR + "/override-file.txt", @@ -78,7 +78,7 @@ describe("shouldIgnore", () => { addToTestDir([ ["level1/level2/level3/ignored-file.txt", "content"], ["level1/.gitignore", "level2/"], - ["level1/level2/.continueignore", "level3/"], + ["level1/level2/.shadow-codeignore", "level3/"], ]); const result = await shouldIgnore( TEST_DIR + "/level1/level2/level3/ignored-file.txt", diff --git a/core/indexing/walkDir.test.ts b/core/indexing/walkDir.test.ts index 50acba4edd1..903de3d8453 100644 --- a/core/indexing/walkDir.test.ts +++ b/core/indexing/walkDir.test.ts @@ -278,7 +278,7 @@ describe("walkDir functions", () => { it("should handle both gitignore and continueignore", async () => { addToTestDir([ [".gitignore", "*.py"], - [".continueignore", "*.ts"], + [".shadow-codeignore", "*.ts"], ["a.txt", "content"], ["b.py", "content"], ["c.ts", "content"], diff --git a/core/indexing/walkDir.ts b/core/indexing/walkDir.ts index 94c147942b4..6f130e7ecd6 100644 --- a/core/indexing/walkDir.ts +++ b/core/indexing/walkDir.ts @@ -307,10 +307,10 @@ export async function getIgnoreContext( .map(([name, _]) => name); // Find ignore files and get ignore arrays from their contexts - // These are done separately so that .continueignore can override .gitignore + // These are done separately so that .shadow-codeignore can override .gitignore const gitIgnoreFile = dirFiles.find((name) => name === ".gitignore"); const continueIgnoreFile = dirFiles.find( - (name) => name === ".continueignore", + (name) => name === ".shadow-codeignore", ); const getGitIgnorePatterns = async () => { @@ -322,7 +322,7 @@ export async function getIgnoreContext( }; const getContinueIgnorePatterns = async () => { if (continueIgnoreFile) { - const contents = await ide.readFile(`${currentDir}/.continueignore`); + const contents = await ide.readFile(`${currentDir}/.shadow-codeignore`); return gitIgArrayFromFile(contents); } return []; @@ -340,8 +340,8 @@ export async function getIgnoreContext( // Note precedence here! const ignoreContext = ignore() .add(ignoreArrays[0]) // gitignore - .add(defaultAndGlobalIgnores) // default file/folder ignores followed by global .continueignore - this is combined for speed - .add(ignoreArrays[1]); // local .continueignore + .add(defaultAndGlobalIgnores) // default file/folder ignores followed by global .shadow-codeignore - this is combined for speed + .add(ignoreArrays[1]); // local .shadow-codeignore return ignoreContext; } diff --git a/core/llm/claudeCodeToolSupport.vitest.ts b/core/llm/claudeCodeToolSupport.vitest.ts new file mode 100644 index 00000000000..1231ef91981 --- /dev/null +++ b/core/llm/claudeCodeToolSupport.vitest.ts @@ -0,0 +1,33 @@ +import { expect, test } from "vitest"; + +import { ModelDescription } from "../index.js"; +import { modelSupportsNativeTools } from "./toolSupport.js"; + +function model(overrides: Partial = {}): ModelDescription { + return { + title: "Claude Code", + provider: "claudecode", + model: "claude-opus-4-5", + ...overrides, + } as ModelDescription; +} + +// Regression: with claudecode absent from PROVIDER_TOOL_SUPPORT this returned +// false, so streamNormalInput never set completionOptions.tools. ClaudeCodeCli +// passes exactly that list to registerToolsForSession, so the spawned `claude` +// process saw only the force-included shadow_* tools - every other tool was +// described in the system prompt but rejected as "No such tool available". +test("modelSupportsNativeTools returns true for the claudecode provider", () => { + expect(modelSupportsNativeTools(model())).toBe(true); +}); + +test("modelSupportsNativeTools respects an explicit capabilities override for claudecode", () => { + expect( + modelSupportsNativeTools(model({ capabilities: { tools: false } })), + ).toBe(false); +}); + +test("modelSupportsNativeTools returns true for claudecode regardless of model name", () => { + expect(modelSupportsNativeTools(model({ model: "" }))).toBe(true); + expect(modelSupportsNativeTools(model({ model: "sonnet" }))).toBe(true); +}); diff --git a/core/llm/llms/ClaudeCodeCli.ts b/core/llm/llms/ClaudeCodeCli.ts index 345c60ec818..151857109db 100644 --- a/core/llm/llms/ClaudeCodeCli.ts +++ b/core/llm/llms/ClaudeCodeCli.ts @@ -43,6 +43,37 @@ interface StreamJsonEvent { }; } +/** + * Splits an NDJSON stream into non-empty trimmed lines, including any trailing + * line the process didn't terminate with a newline - that last line is + * typically the `result` event carrying the turn's usage numbers. + */ +async function* readLines( + stream: NodeJS.ReadableStream, +): AsyncGenerator { + let buffer = ""; + for await (const chunk of stream) { + buffer += chunk.toString(); + let newlineIndex: number; + while ((newlineIndex = buffer.indexOf("\n")) !== -1) { + const line = buffer.slice(0, newlineIndex).trim(); + buffer = buffer.slice(newlineIndex + 1); + if (line) { + yield line; + } + } + } + const trailing = buffer.trim(); + if (trailing) { + yield trailing; + } +} + +/** Agent run ids are opaque; keep them safe to embed in a temp file name. */ +function sanitizeForFilename(value: string): string { + return value.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 64); +} + function toolResultText( content: Array<{ type: "text"; text: string }> | string | undefined, ): string { @@ -63,12 +94,15 @@ class ClaudeCodeCli extends BaseLLM { // provider is a stateless completion API from Continue's perspective), so // this is the correct id to key on regardless - it's what the shadow_* // tools below already search against for any provider. - private async writeMcpConfig(sessionId: string): Promise { + private async writeMcpConfig(runKey: string): Promise { const mcpServer = getShadowCodeToolsMcpServer(); - const url = await mcpServer.ensureStarted(); + await mcpServer.ensureStarted(); + // Keyed on the agent run, not the chat session: a parent turn and the + // subagents it spawns share a chat session, and would otherwise overwrite + // and unlink each other's config file. const configPath = path.join( os.tmpdir(), - `shadow-code-mcp-${sessionId}.json`, + `shadow-code-mcp-${sanitizeForFilename(runKey)}.json`, ); await fs.writeFile( configPath, @@ -76,7 +110,7 @@ class ClaudeCodeCli extends BaseLLM { mcpServers: { [MCP_SERVER_NAME]: { type: "http", - url: `${url}?continueSessionId=${encodeURIComponent(sessionId)}`, + url: mcpServer.getUrlForSession(runKey), }, }, }), @@ -112,7 +146,18 @@ class ClaudeCodeCli extends BaseLLM { // the normal case, and shadow_get_chat_history would silently query an // empty session if we used the wrong one. const sessionId = options.shadowSessionId ?? deriveSessionId(messages); - const mcpConfigPath = await this.writeMcpConfig(sessionId); + // One key per agent run. Subagents share `sessionId` with the parent turn + // but each needs its own MCP transport, tool registration and config file. + const runKey = options.agentRunId ?? sessionId; + + // addEventListener on an already-aborted signal never fires, and there are + // awaits below before the listener is attached - without this the child + // would be spawned and then never killed. + if (signal.aborted) { + return; + } + + const mcpConfigPath = await this.writeMcpConfig(runKey); // Expose exactly the tools active for this turn (options.tools already // includes any client tool overrides plus, under Ultra Token Saving @@ -121,7 +166,12 @@ class ClaudeCodeCli extends BaseLLM { // full config. Registered/unregistered per call since this MCP server // is a shared, long-lived singleton serving every Continue session. const mcpServer = getShadowCodeToolsMcpServer(); - mcpServer.registerToolsForSession(sessionId, options.tools ?? []); + mcpServer.registerToolsForSession( + runKey, + options.tools ?? [], + sessionId, + options.agentLabel, + ); const args = [ "-p", @@ -139,7 +189,7 @@ class ClaudeCodeCli extends BaseLLM { "", // Our own MCP server already gates every call through Continue's // approval/policy flow (see ShadowCodeToolsMcpServer + the - // claudeCodeCli/authorizeToolCall round-trip to the GUI); Claude + // agent/authorizeToolCall round-trip to the GUI); Claude // Code's own permission layer has no terminal to prompt against in // -p mode and would otherwise just hang or auto-deny. "--permission-mode", @@ -164,13 +214,23 @@ class ClaudeCodeCli extends BaseLLM { const onAbort = () => child.kill(); signal.addEventListener("abort", onAbort); + // Without a listener, a failed spawn (e.g. `claude` not on PATH) emits an + // unhandled 'error' event, which takes down the whole Core process. + let spawnError: Error | undefined; + child.on("error", (e) => { + spawnError = e; + }); + child.stdin.on("error", () => { + // EPIPE if the CLI exited before reading the prompt; the exit code and + // stderr below are the real diagnosis. + }); + child.stdin.write(renderChatMessage(lastUserMessage)); child.stdin.end(); let stderr = ""; child.stderr.on("data", (chunk) => (stderr += chunk.toString())); - let buffer = ""; let usage: Usage | undefined; // Every tool call Claude Code makes (shadow_* or otherwise) is already @@ -184,84 +244,84 @@ class ClaudeCodeCli extends BaseLLM { // double-execution (see the "claudecode" bypass in tokenOptimizedChat.ts // and the forced-ultra-mode routing in streamChat.ts). try { - for await (const chunk of child.stdout) { - buffer += chunk.toString(); - let newlineIndex: number; - while ((newlineIndex = buffer.indexOf("\n")) !== -1) { - const line = buffer.slice(0, newlineIndex).trim(); - buffer = buffer.slice(newlineIndex + 1); - if (!line) continue; + for await (const line of readLines(child.stdout)) { + let event: StreamJsonEvent; + try { + event = JSON.parse(line); + } catch { + continue; // Not every line is guaranteed to be JSON we care about + } - let event: StreamJsonEvent; - try { - event = JSON.parse(line); - } catch { - continue; // Not every line is guaranteed to be JSON we care about + if (event.type === "assistant" && event.message?.content) { + let text = ""; + const toolCalls: ToolCallDelta[] = []; + for (const block of event.message.content) { + if (block.type === "text") { + text += block.text; + } else if (block.type === "tool_use") { + toolCalls.push({ + id: block.id, + type: "function", + function: { + name: block.name.startsWith(MCP_TOOL_PREFIX) + ? block.name.slice(MCP_TOOL_PREFIX.length) + : block.name, + arguments: JSON.stringify(block.input ?? {}), + }, + }); + } + } + if (text || toolCalls.length > 0) { + yield { + role: "assistant", + content: text, + ...(toolCalls.length > 0 ? { toolCalls } : {}), + }; } + continue; + } - if (event.type === "assistant" && event.message?.content) { - let text = ""; - const toolCalls: ToolCallDelta[] = []; - for (const block of event.message.content) { - if (block.type === "text") { - text += block.text; - } else if (block.type === "tool_use") { - toolCalls.push({ - id: block.id, - type: "function", - function: { - name: block.name.startsWith(MCP_TOOL_PREFIX) - ? block.name.slice(MCP_TOOL_PREFIX.length) - : block.name, - arguments: JSON.stringify(block.input ?? {}), - }, - }); - } - } - if (text || toolCalls.length > 0) { + if (event.type === "user" && event.message?.content) { + for (const block of event.message.content) { + if (block.type === "tool_result") { yield { - role: "assistant", - content: text, - ...(toolCalls.length > 0 ? { toolCalls } : {}), + role: "tool", + toolCallId: block.tool_use_id, + content: toolResultText(block.content), }; } - continue; - } - - if (event.type === "user" && event.message?.content) { - for (const block of event.message.content) { - if (block.type === "tool_result") { - yield { - role: "tool", - toolCallId: block.tool_use_id, - content: toolResultText(block.content), - }; - } - } - continue; } + continue; + } - if (event.type === "result" && event.usage) { - usage = { - promptTokens: event.usage.input_tokens ?? 0, - completionTokens: event.usage.output_tokens ?? 0, - promptTokensDetails: { - cachedTokens: event.usage.cache_read_input_tokens, - cacheWriteTokens: event.usage.cache_creation_input_tokens, - }, - }; - } + if (event.type === "result" && event.usage) { + usage = { + promptTokens: event.usage.input_tokens ?? 0, + completionTokens: event.usage.output_tokens ?? 0, + promptTokensDetails: { + cachedTokens: event.usage.cache_read_input_tokens, + cacheWriteTokens: event.usage.cache_creation_input_tokens, + }, + }; } } } finally { signal.removeEventListener("abort", onAbort); - mcpServer.unregisterSession(sessionId); + // If the consumer abandoned this generator without aborting (a throw + // further down the pipeline, say), nothing else will ever kill the child. + if (child.exitCode === null && child.signalCode === null) { + child.kill(); + } + mcpServer.unregisterSession(runKey); await fs.unlink(mcpConfigPath).catch(() => {}); } const exitCode: number = await new Promise((resolve) => child.once("close", resolve), ); + if (spawnError) { + throw new Error(`Failed to run the claude CLI: ${spawnError.message}`); + } if (exitCode !== 0 && !signal.aborted) { throw new Error( `claude CLI exited with code ${exitCode}${stderr ? `: ${stderr}` : ""}`, diff --git a/core/llm/llms/ClawRouter.ts b/core/llm/llms/ClawRouter.ts index 2cdb9a8cecd..35438d0a985 100644 --- a/core/llm/llms/ClawRouter.ts +++ b/core/llm/llms/ClawRouter.ts @@ -4,7 +4,7 @@ import { osModelsEditPrompt } from "../templates/edit.js"; import OpenAI from "./OpenAI.js"; // Get Continue version from package.json at build time -const CONTINUE_VERSION = process.env.npm_package_version || "unknown"; +const SHADOW_CODE_VERSION = process.env.npm_package_version || "unknown"; /** * ClawRouter LLM Provider @@ -44,7 +44,7 @@ class ClawRouter extends OpenAI { protected _getHeaders() { return { ...super._getHeaders(), - "User-Agent": `Continue/${CONTINUE_VERSION}`, + "User-Agent": `ShadowCode/${SHADOW_CODE_VERSION}`, "X-Continue-Provider": "clawrouter", }; } diff --git a/core/llm/llms/ClawRouter.vitest.ts b/core/llm/llms/ClawRouter.vitest.ts index fa3af73f14f..447b3de8042 100644 --- a/core/llm/llms/ClawRouter.vitest.ts +++ b/core/llm/llms/ClawRouter.vitest.ts @@ -30,7 +30,7 @@ describe("ClawRouter", () => { const headers = clawRouter["_getHeaders"](); - expect(headers["User-Agent"]).toMatch(/^Continue\//); + expect(headers["User-Agent"]).toMatch(/^ShadowCode\//); expect(headers["X-Continue-Provider"]).toBe("clawrouter"); }); diff --git a/core/llm/openaiTypeConverters.ts b/core/llm/openaiTypeConverters.ts index fb4673e11be..f661a98de6d 100644 --- a/core/llm/openaiTypeConverters.ts +++ b/core/llm/openaiTypeConverters.ts @@ -367,6 +367,9 @@ export function fromChatCompletionChunk( .filter((tool_call) => !tool_call.type || tool_call.type === "function") .map((tool_call) => ({ id: tool_call.id, + // Required to keep parallel tool calls apart: after the opening delta + // OpenAI identifies each call by index only, with no id. + index: tool_call.index, type: "function" as const, function: { name: (tool_call as any).function?.name, diff --git a/core/llm/rules/alwaysApply.vitest.ts b/core/llm/rules/alwaysApply.vitest.ts index f586ff87d67..aec29172079 100644 --- a/core/llm/rules/alwaysApply.vitest.ts +++ b/core/llm/rules/alwaysApply.vitest.ts @@ -44,7 +44,7 @@ describe("alwaysApply Behavior", () => { alwaysApply: true, globs: "**/*.tsx", // Should be ignored since alwaysApply is true source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }; // Test with no file context @@ -83,7 +83,7 @@ describe("alwaysApply Behavior", () => { alwaysApply: false, globs: "**/*.tsx", source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }; // Rule with alwaysApply: false and no globs @@ -93,7 +93,7 @@ describe("alwaysApply Behavior", () => { alwaysApply: false, // No globs source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }; // Test with no file context @@ -131,7 +131,7 @@ describe("alwaysApply Behavior", () => { name: "Default No Globs Rule", rule: "Default rule with no globs", source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", // No alwaysApply, no globs }; @@ -141,7 +141,7 @@ describe("alwaysApply Behavior", () => { rule: "Default rule with globs", globs: "**/*.tsx", source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", // No alwaysApply, with globs }; diff --git a/core/llm/rules/alwaysApplyRules.vitest.ts b/core/llm/rules/alwaysApplyRules.vitest.ts index b6073bf7df9..464f91cdd04 100644 --- a/core/llm/rules/alwaysApplyRules.vitest.ts +++ b/core/llm/rules/alwaysApplyRules.vitest.ts @@ -15,7 +15,7 @@ describe("Rule application with alwaysApply", () => { rule: "This rule should always be applied", alwaysApply: true, source: "rules-block", - sourceFile: ".continue/always-apply.md", + sourceFile: ".shadow-code/always-apply.md", }; // Create a colocated rule in a nested directory diff --git a/core/llm/rules/getSystemMessageWithRules.ts b/core/llm/rules/getSystemMessageWithRules.ts index cce647c148d..0dcd42f8309 100644 --- a/core/llm/rules/getSystemMessageWithRules.ts +++ b/core/llm/rules/getSystemMessageWithRules.ts @@ -130,10 +130,10 @@ const isFileInDirectory = ( }; /** - * Checks if a rule is a root-level rule (.continue directory or no file path) + * Checks if a rule is a root-level rule (.shadow-code directory or no file path) */ const isRootLevelRule = (rule: RuleWithSource): boolean => { - return !rule.sourceFile || rule.sourceFile.includes(".continue/"); // sourceFile path is absolute - hence we need to check for it in between + return !rule.sourceFile || rule.sourceFile.includes(".shadow-code/"); // sourceFile path is absolute - hence we need to check for it in between }; /** @@ -227,7 +227,7 @@ export const shouldApplyRule = ( return false; } - // Check if this is a root-level rule (in .continue directory or no file path) + // Check if this is a root-level rule (in .shadow-code directory or no file path) const isRootRule = isRootLevelRule(rule); // For non-root rules, we need to check if any files are in the rule's directory diff --git a/core/llm/rules/getSystemMessageWithRules.vitest.ts b/core/llm/rules/getSystemMessageWithRules.vitest.ts index 104ceecf8ca..34f30076362 100644 --- a/core/llm/rules/getSystemMessageWithRules.vitest.ts +++ b/core/llm/rules/getSystemMessageWithRules.vitest.ts @@ -69,7 +69,7 @@ describe("Rule colocation glob matching", () => { name: "Root Rule", rule: "Follow project standards", source: "rules-block", - sourceFile: "/path/to/repo/.continue/rules.md", + sourceFile: "/path/to/repo/.shadow-code/rules.md", // No restriction, should apply to all files }; @@ -212,7 +212,7 @@ describe("Rule policies", () => { name: "Root Rule", rule: "Follow project standards", source: "rules-block", - sourceFile: "/my/project/.continue/rules.md", + sourceFile: "/my/project/.shadow-code/rules.md", }; // Off policy should override even global rules diff --git a/core/llm/rules/implicitGlobalRules.vitest.ts b/core/llm/rules/implicitGlobalRules.vitest.ts index baa13745f2e..fb84b27d189 100644 --- a/core/llm/rules/implicitGlobalRules.vitest.ts +++ b/core/llm/rules/implicitGlobalRules.vitest.ts @@ -8,7 +8,7 @@ describe("Implicit global rules application", () => { name: "Implicit Global Rule", rule: "This rule should be applied to all messages (implicit global)", source: "rules-block", - sourceFile: ".continue/global-rule.md", + sourceFile: ".shadow-code/global-rule.md", // No alwaysApply specified // No globs specified }; @@ -19,7 +19,7 @@ describe("Implicit global rules application", () => { rule: "This rule should always be applied (explicit global)", alwaysApply: true, source: "rules-block", - sourceFile: ".continue/explicit-global.md", + sourceFile: ".shadow-code/explicit-global.md", }; // Create a colocated rule in a nested directory @@ -63,7 +63,7 @@ describe("Implicit global rules application", () => { name: "Root No Globs Rule", rule: "This is a root-level rule with no globs", source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", // No alwaysApply, no globs }; @@ -119,7 +119,7 @@ describe("Implicit global rules application", () => { name: "Assistant Guidelines", rule: "SOLID Design Principles - Coding Assistant Guidelines", source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", // No alwaysApply, no globs - should still apply }; diff --git a/core/llm/rules/nestedDirectoryRules.vitest.ts b/core/llm/rules/nestedDirectoryRules.vitest.ts index 97f8f82386e..a97b76619c7 100644 --- a/core/llm/rules/nestedDirectoryRules.vitest.ts +++ b/core/llm/rules/nestedDirectoryRules.vitest.ts @@ -22,7 +22,7 @@ describe("Nested directory rules application", () => { name: "Global Rule", rule: "SOLID Design Principles - Coding Agent Guidelines", source: "rules-block", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }; it("should apply nested directory rules to files in that directory", () => { diff --git a/core/llm/rules/ruleColocation.vitest.ts b/core/llm/rules/ruleColocation.vitest.ts index 644bfb95728..29a16f1a6bf 100644 --- a/core/llm/rules/ruleColocation.vitest.ts +++ b/core/llm/rules/ruleColocation.vitest.ts @@ -38,7 +38,7 @@ describe("Rule colocation - glob pattern matching", () => { alwaysApply: true, globs: "src/specific/**/*.ts", // Should be ignored since alwaysApply is true source: "colocated-markdown", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }, // Rule with explicit alwaysApply: false @@ -48,7 +48,7 @@ describe("Rule colocation - glob pattern matching", () => { alwaysApply: false, // No globs, so should never apply source: "colocated-markdown", - sourceFile: ".continue/rules.md", + sourceFile: ".shadow-code/rules.md", }, // Rule with explicit alwaysApply: false but with globs diff --git a/core/llm/rules/rules-utils.ts b/core/llm/rules/rules-utils.ts index 279cf55ebde..cd68e7ac773 100644 --- a/core/llm/rules/rules-utils.ts +++ b/core/llm/rules/rules-utils.ts @@ -10,7 +10,7 @@ export function getRuleDisplayName(rule: RuleMetadata): string { export function getRuleSourceDisplayName(rule: RuleMetadata): string { switch (rule.source) { - case ".continuerules": + case ".shadow-coderules": return "Project rules"; case "default-chat": return "Default chat system message"; diff --git a/core/llm/tokenOptimizedChat.ts b/core/llm/tokenOptimizedChat.ts index 76f62d2a045..c4d776e95c5 100644 --- a/core/llm/tokenOptimizedChat.ts +++ b/core/llm/tokenOptimizedChat.ts @@ -17,29 +17,41 @@ interface CompletedToolCall { } function extractCompletedToolCalls(chunks: ChatMessage[]): CompletedToolCall[] { - const callsById = new Map(); - const callOrder: string[] = []; - let currentId = ""; + // Keyed by delta.index when the provider sends one, falling back to a + // synthetic key derived from the id. Keying on "most recently seen id" alone + // merges parallel tool calls into one, because providers only repeat the id + // on the delta that opens each call - every subsequent argument fragment + // carries just an index. + const calls = new Map(); + const callOrder: (string | number)[] = []; + let currentKey: string | number | undefined; for (const chunk of chunks) { if (chunk.role !== "assistant" || !chunk.toolCalls?.length) continue; for (const delta of chunk.toolCalls as ToolCallDelta[]) { - if (delta.id) { - currentId = delta.id; - if (!callsById.has(currentId)) { - callsById.set(currentId, { id: currentId, name: "", args: "" }); - callOrder.push(currentId); - } + const key = + typeof delta.index === "number" + ? delta.index + : (delta.id ?? currentKey); + if (key === undefined) continue; + currentKey = key; + + let call = calls.get(key); + if (!call) { + call = { id: delta.id ?? "", name: "", args: "" }; + calls.set(key, call); + callOrder.push(key); } - if (!currentId) continue; - const call = callsById.get(currentId); - if (!call) continue; + // The id may arrive on a later delta than the one that opened the call. + if (delta.id && !call.id) call.id = delta.id; if (delta.function?.name) call.name += delta.function.name; if (delta.function?.arguments) call.args += delta.function.arguments; } } - return callOrder.map((id) => callsById.get(id)!).filter((c) => c && c.name); + return callOrder + .map((key) => calls.get(key)!) + .filter((c) => c && c.name && c.id); } function extractUsageFromChunks(chunks: ChatMessage[]): { @@ -208,9 +220,21 @@ export async function* tokenOptimizedStreamChat( shadowSessionId: sessionId, }; + // Everything after the last user message: the assistant tool-call / + // tool-result pairs of the turn currently in progress. Ultra mode drops + // *previous* turns, but dropping the *current* turn's in-flight state makes + // the loop non-convergent - the model would see the same user message with + // its tool results missing and simply re-emit the identical tool call. That + // is merely wasteful for a read_file; for spawn_subagents it re-runs the + // whole delegated task. + const lastUserIdx = messages.lastIndexOf(currentUserMsg); + const currentTurnTail = + lastUserIdx === -1 ? [] : messages.slice(lastUserIdx + 1); + let loopMessages: ChatMessage[] = [ ...(systemMsg ? [systemMsg] : []), currentUserMsg, + ...currentTurnTail, ]; let totalActualTokensIn = 0; @@ -222,130 +246,146 @@ export async function* tokenOptimizedStreamChat( completion: "", }; - // Internal agentic loop: execute shadow_* tools server-side, pass external tools to client - while (true) { - const chunks: ChatMessage[] = []; - const gen = model.streamChat(loopMessages, signal, augmentedOptions, { - precompiled: true, - }); + try { + // Internal agentic loop: execute shadow_* tools server-side, pass external tools to client + while (true) { + // The generator is resumed via gen.return() on abort, which skips straight + // to the finally below; this covers abort landing between iterations. + if (signal.aborted) { + break; + } - let next = await gen.next(); - while (!next.done) { - chunks.push(next.value); - next = await gen.next(); - } - if ( - next.value && - typeof next.value === "object" && - "prompt" in next.value - ) { - finalPromptLog = next.value as PromptLog; - } + const chunks: ChatMessage[] = []; + const gen = model.streamChat(loopMessages, signal, augmentedOptions, { + precompiled: true, + }); - const { promptTokens, completionTokens } = extractUsageFromChunks(chunks); - totalActualTokensIn += promptTokens; - totalActualTokensOut += completionTokens; - - // Claude Code CLI (core/llm/llms/ClaudeCodeCli.ts) resolves its entire - // tool-call loop internally, inside the single `claude -p` subprocess, - // via shadow-code-tools MCP - including shadow_* history lookups. Any - // toolCalls it yields here (paired with their results, for UI parity - // with the normal provider path) are already-resolved history, not - // pending work. Running the interception logic below would either - // re-execute already-resolved shadow_* calls a second time, or spawn a - // needless second `claude -p` process for calls that already have a - // final answer - so always treat a single iteration as done. - if (model.providerName === "claudecode") { - for (const chunk of chunks) { - yield chunk; + let next = await gen.next(); + while (!next.done) { + chunks.push(next.value); + if (signal.aborted) { + await gen.return(undefined as any); + break; + } + next = await gen.next(); + } + if ( + next.value && + typeof next.value === "object" && + "prompt" in next.value + ) { + finalPromptLog = next.value as PromptLog; + } + + const { promptTokens, completionTokens } = extractUsageFromChunks(chunks); + totalActualTokensIn += promptTokens; + totalActualTokensOut += completionTokens; + + // Claude Code CLI (core/llm/llms/ClaudeCodeCli.ts) resolves its entire + // tool-call loop internally, inside the single `claude -p` subprocess, + // via shadow-code-tools MCP - including shadow_* history lookups. Any + // toolCalls it yields here (paired with their results, for UI parity + // with the normal provider path) are already-resolved history, not + // pending work. Running the interception logic below would either + // re-execute already-resolved shadow_* calls a second time, or spawn a + // needless second `claude -p` process for calls that already have a + // final answer - so always treat a single iteration as done. + if (model.providerName === "claudecode") { + for (const chunk of chunks) { + yield chunk; + } + finalPromptLog = { + ...finalPromptLog, + completion: buildTextContent(chunks), + }; + break; } - finalPromptLog = { - ...finalPromptLog, - completion: buildTextContent(chunks), - }; - break; - } - const toolCalls = extractCompletedToolCalls(chunks); + const toolCalls = extractCompletedToolCalls(chunks); - if (toolCalls.length === 0) { - // Pure text response — stream all chunks to the caller - for (const chunk of chunks) { - yield chunk; + if (toolCalls.length === 0) { + // Pure text response — stream all chunks to the caller + for (const chunk of chunks) { + yield chunk; + } + finalPromptLog = { + ...finalPromptLog, + completion: buildTextContent(chunks), + }; + break; } - finalPromptLog = { - ...finalPromptLog, - completion: buildTextContent(chunks), - }; - break; - } - const shadowCalls = toolCalls.filter((tc) => - SHADOW_TOOL_NAMES.has(tc.name), - ); - const externalCalls = toolCalls.filter( - (tc) => !SHADOW_TOOL_NAMES.has(tc.name), - ); - - if (externalCalls.length > 0) { - // External/MCP tool calls — pass all chunks through to the client unchanged - for (const chunk of chunks) { - yield chunk; + const shadowCalls = toolCalls.filter((tc) => + SHADOW_TOOL_NAMES.has(tc.name), + ); + const externalCalls = toolCalls.filter( + (tc) => !SHADOW_TOOL_NAMES.has(tc.name), + ); + + if (externalCalls.length > 0) { + // External/MCP tool calls — pass all chunks through to the client unchanged + for (const chunk of chunks) { + yield chunk; + } + break; } - break; - } - // All tool calls are shadow tools — execute server-side (never round-tripped - // to the client, so the reduced-history guarantee holds), but still yield - // the resolved call/result so the client renders the same tool-call card - // it would for any other tool. Since this is followed by more streamed - // chunks from the next loop iteration (ending in plain text), the client's - // "pending tool call" detection naturally treats it as already resolved - // history rather than something it needs to execute itself. - const assistantToolCallMsg: AssistantChatMessage = { - role: "assistant", - content: "", - toolCalls: shadowCalls.map((tc) => ({ - id: tc.id, - type: "function" as const, - function: { name: tc.name, arguments: tc.args }, - })), - }; - loopMessages = [...loopMessages, assistantToolCallMsg]; - yield assistantToolCallMsg; - - const turnIndex = await ShadowChatDb.getCurrentTurnIndex(sessionId); - for (const call of shadowCalls) { - const result = await executeShadowTool(call, sessionId, historyLimit); - const toolResultMsg: ChatMessage = { - role: "tool", - content: result, - toolCallId: call.id, + // All tool calls are shadow tools — execute server-side (never round-tripped + // to the client, so the reduced-history guarantee holds), but still yield + // the resolved call/result so the client renders the same tool-call card + // it would for any other tool. Since this is followed by more streamed + // chunks from the next loop iteration (ending in plain text), the client's + // "pending tool call" detection naturally treats it as already resolved + // history rather than something it needs to execute itself. + const assistantToolCallMsg: AssistantChatMessage = { + role: "assistant", + content: "", + toolCalls: shadowCalls.map((tc) => ({ + id: tc.id, + type: "function" as const, + function: { name: tc.name, arguments: tc.args }, + })), }; - loopMessages = [...loopMessages, toolResultMsg]; - yield toolResultMsg; - - await ShadowChatDb.saveToolCall( - sessionId, - call.name, - call.id, - call.args, - result, - turnIndex, - ); + loopMessages = [...loopMessages, assistantToolCallMsg]; + yield assistantToolCallMsg; + + const turnIndex = await ShadowChatDb.getCurrentTurnIndex(sessionId); + for (const call of shadowCalls) { + const result = await executeShadowTool(call, sessionId, historyLimit); + const toolResultMsg: ChatMessage = { + role: "tool", + content: result, + toolCallId: call.id, + }; + loopMessages = [...loopMessages, toolResultMsg]; + yield toolResultMsg; + + await ShadowChatDb.saveToolCall( + sessionId, + call.name, + call.id, + call.args, + result, + turnIndex, + ); + } + // Loop: the LLM will now see the tool results and produce its final answer } - // Loop: the LLM will now see the tool results and produce its final answer + } finally { + // In a finally so an aborted or failed turn still lands in the token + // accounting. gen.return() on abort resumes this generator at its current + // yield, which runs this block; previously the turn was silently lost. + await ShadowChatDb.saveTurn( + sessionId, + userMessageText, + finalPromptLog.completion, + totalActualTokensIn, + totalActualTokensOut, + estimatedBaselineTokens, + ).catch((e) => { + console.error("Failed to record shadow chat turn", e); + }); } - // Log token savings for this turn - await ShadowChatDb.saveTurn( - sessionId, - userMessageText, - finalPromptLog.completion, - totalActualTokensIn, - totalActualTokensOut, - estimatedBaselineTokens, - ); - return finalPromptLog; } diff --git a/core/llm/tokenOptimizedChat.vitest.ts b/core/llm/tokenOptimizedChat.vitest.ts new file mode 100644 index 00000000000..57c5e153c93 --- /dev/null +++ b/core/llm/tokenOptimizedChat.vitest.ts @@ -0,0 +1,223 @@ +import { expect, test, vi } from "vitest"; + +import { ChatMessage, ILLM } from "../index.js"; +import { ShadowChatDb } from "../data/shadowChatDb.js"; +import { tokenOptimizedStreamChat } from "./tokenOptimizedChat.js"; + +// The DB is incidental to what these tests cover (message construction), and +// hitting real SQLite would make them order-dependent. +vi.mock("../data/shadowChatDb.js", () => ({ + ShadowChatDb: { + saveMessages: vi.fn(async () => {}), + saveToolCall: vi.fn(async () => {}), + saveTurn: vi.fn(async () => {}), + getCurrentTurnIndex: vi.fn(async () => 0), + }, +})); + +/** Captures the messages handed to the model on each turn. */ +function recordingLlm(turns: ChatMessage[][]) { + const sentMessages: ChatMessage[][] = []; + let turnIndex = 0; + + const llm = { + providerName: "anthropic", + model: "fake", + title: "Fake", + streamChat: async function* (messages: ChatMessage[]) { + sentMessages.push(messages); + const chunks = turns[Math.min(turnIndex, turns.length - 1)]; + turnIndex += 1; + for (const chunk of chunks) { + yield chunk; + } + }, + } as unknown as ILLM; + + return { llm, sentMessages }; +} + +async function drain(gen: AsyncGenerator) { + const out: ChatMessage[] = []; + let next = await gen.next(); + while (!next.done) { + out.push(next.value); + next = await gen.next(); + } + return out; +} + +test("tokenOptimizedStreamChat drops earlier turns but keeps the system and current user message", async () => { + const { llm, sentMessages } = recordingLlm([ + [{ role: "assistant", content: "done" }], + ]); + + const messages: ChatMessage[] = [ + { role: "system", content: "SYSTEM" }, + { role: "user", content: "old question" }, + { role: "assistant", content: "old answer" }, + { role: "user", content: "new question" }, + ]; + + await drain( + tokenOptimizedStreamChat( + llm, + messages, + new AbortController().signal, + {}, + "session-1", + 20, + ), + ); + + expect(sentMessages[0].map((m) => m.content)).toEqual([ + "SYSTEM", + "new question", + ]); +}); + +test("tokenOptimizedStreamChat carries the current turn's tool results into the next request", async () => { + // The regression: without the current-turn tail, the model sees only + // [system, user] again - its tool result is gone - so it re-emits the same + // tool call forever. For spawn_subagents that re-runs the whole task. + const { llm, sentMessages } = recordingLlm([ + [{ role: "assistant", content: "done" }], + ]); + + const messages: ChatMessage[] = [ + { role: "system", content: "SYSTEM" }, + { role: "user", content: "spawn some subagents" }, + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "call-1", + type: "function", + function: { name: "spawn_subagents", arguments: "{}" }, + }, + ], + }, + { role: "tool", content: "subagent report", toolCallId: "call-1" }, + ]; + + await drain( + tokenOptimizedStreamChat( + llm, + messages, + new AbortController().signal, + {}, + "session-2", + 20, + ), + ); + + const sent = sentMessages[0]; + expect(sent.map((m) => m.role)).toEqual([ + "system", + "user", + "assistant", + "tool", + ]); + expect(sent.at(-1)).toMatchObject({ + role: "tool", + toolCallId: "call-1", + content: "subagent report", + }); +}); + +test("tokenOptimizedStreamChat keeps parallel tool calls separate when index is present", async () => { + // Two calls opened with ids, then argument fragments carrying only index - + // the OpenAI streaming shape. Keying on "most recently seen id" merged both + // fragment streams into call-2. + const { llm } = recordingLlm([ + [ + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "call-1", + index: 0, + type: "function", + function: { name: "read_file", arguments: '{"a' }, + }, + ], + }, + { + role: "assistant", + content: "", + toolCalls: [ + { + id: "call-2", + index: 1, + type: "function", + function: { name: "grep_search", arguments: '{"b' }, + }, + ], + }, + { + role: "assistant", + content: "", + toolCalls: [ + { index: 0, type: "function", function: { arguments: '":1}' } }, + ], + }, + { + role: "assistant", + content: "", + toolCalls: [ + { index: 1, type: "function", function: { arguments: '":2}' } }, + ], + }, + ], + ]); + + const yielded = await drain( + tokenOptimizedStreamChat( + llm, + [ + { role: "system", content: "SYSTEM" }, + { role: "user", content: "go" }, + ], + new AbortController().signal, + {}, + "session-3", + 20, + ), + ); + + // Both are non-shadow tools, so ultra mode passes the chunks through for the + // client to execute; the point here is that extraction didn't merge them. + const allToolCalls = yielded.flatMap((m: any) => m.toolCalls ?? []); + const ids = new Set(allToolCalls.map((tc: any) => tc.id).filter(Boolean)); + expect(ids).toEqual(new Set(["call-1", "call-2"])); +}); + +test("tokenOptimizedStreamChat records the turn even when the model throws", async () => { + const llm = { + providerName: "anthropic", + model: "fake", + streamChat: async function* () { + throw new Error("boom"); + }, + } as unknown as ILLM; + + await expect( + drain( + tokenOptimizedStreamChat( + llm, + [ + { role: "system", content: "SYSTEM" }, + { role: "user", content: "go" }, + ], + new AbortController().signal, + {}, + "session-4", + 20, + ), + ), + ).rejects.toThrow("boom"); + + expect(ShadowChatDb.saveTurn).toHaveBeenCalled(); +}); diff --git a/core/llm/toolSupport.ts b/core/llm/toolSupport.ts index 3f65473233c..65e8e56398b 100644 --- a/core/llm/toolSupport.ts +++ b/core/llm/toolSupport.ts @@ -11,6 +11,17 @@ export const PROVIDER_TOOL_SUPPORT: Record boolean> = } return false; }, + // Claude Code CLI (core/llm/llms/ClaudeCodeCli.ts). Always true: the + // provider is Claude, and the whole integration is built on native tool + // calls surfaced through the shadow-code-tools MCP server. + // + // This must be registered, not left to the default. Without it + // modelSupportsNativeTools() returns false, so streamNormalInput never + // populates completionOptions.tools - and since that is exactly what + // ClaudeCodeCli hands to registerToolsForSession, the CLI would be exposed + // only the shadow_* tools that tokenOptimizedChat force-includes. Every + // other tool would be described in the system prompt but be uncallable. + claudecode: () => true, azure: (model) => { const lower = model.toLowerCase(); if (lower.match(/^gpt-[4-9]/) || lower.match(/^o[1-9]/)) return true; diff --git a/core/mcp/shadowCodeToolsServer.ts b/core/mcp/shadowCodeToolsServer.ts index 9f920a889ea..39490ff383f 100644 --- a/core/mcp/shadowCodeToolsServer.ts +++ b/core/mcp/shadowCodeToolsServer.ts @@ -8,7 +8,12 @@ import { ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js"; -import { ContinueConfig, Tool, ToolCall } from "../index.js"; +import { + ContinueConfig, + Tool, + ToolApprovalRequest, + ToolCall, +} from "../index.js"; /** * Everything the MCP server needs from the running Core instance. Kept as a @@ -31,15 +36,9 @@ export interface ShadowCodeToolsRuntime { * requires it, to show an approval prompt and wait for the user's decision. * Resolves immediately for auto-approved/auto-disabled tools. */ - requestApproval: (params: { - approvalId: string; - toolCallId: string; - sessionId: string | undefined; - toolName: string; - args: Record; - displayTitle?: string; - wouldLikeTo?: string; - }) => Promise<{ approved: boolean }>; + requestApproval: ( + params: ToolApprovalRequest, + ) => Promise<{ approved: boolean }>; } function toolToMcpSchema(tool: Tool) { @@ -63,38 +62,90 @@ function toolToMcpSchema(tool: Tool) { * config/messenger state `handleToolCall` uses - no extra process, no new * cross-process IPC. */ +/** Used when a request arrives without a ?continueSessionId= query param. */ +const DEFAULT_SESSION_KEY = "__default__"; + +interface RegisteredAgentRun { + tools: Tool[]; + /** + * The ShadowChatDb / Continue conversation id to attribute tool calls to. + * Distinct from the map key: several subagents share one chat session but + * each gets its own key so their tool registrations don't overwrite each + * other's. + */ + chatSessionId: string | undefined; + /** Shown on the approval banner, e.g. a subagent's task description. */ + agentLabel?: string; +} + +interface McpSession { + server: Server; + transport: StreamableHTTPServerTransport; +} + export class ShadowCodeToolsMcpServer { private httpServer: http.Server | undefined; - private transport: StreamableHTTPServerTransport | undefined; private url: string | undefined; private startPromise: Promise | undefined; - // The set of tools actually active for a given Continue session/turn - // (e.g. augmentedOptions.tools from tokenOptimizedStreamChat, which - // includes any tool overrides plus the force-included shadow_* tools) - - // registered by ClaudeCodeCli right before spawning `claude` for that - // session. Falls back to the full config.tools list if nothing was - // registered (e.g. a stale/unknown session id). - private toolsBySession = new Map(); + // One MCP Server + transport per agent run, NOT one per process. The + // transport is stateful (see start()), and a stateful transport binds to + // exactly one MCP client: the SDK rejects a second `initialize` with + // "Server already initialized" and close() never resets that flag. Sharing + // one transport therefore caps the whole process at a single `claude` + // subprocess, which makes parallel subagents impossible. + private sessions = new Map>(); + + // The set of tools actually active for a given agent run (e.g. + // augmentedOptions.tools from tokenOptimizedStreamChat, which includes any + // tool overrides plus the force-included shadow_* tools) - registered by + // ClaudeCodeCli right before spawning `claude`. Falls back to the full + // config.tools list if nothing was registered (e.g. a stale/unknown key). + private runsByKey = new Map(); constructor(private readonly runtime: ShadowCodeToolsRuntime) {} - registerToolsForSession(sessionId: string, tools: Tool[]) { - this.toolsBySession.set(sessionId, tools); + /** + * @param key Identifies one agent run - CompletionOptions.agentRunId, or the + * chat session id for the main turn. NOT the MCP transport's own session id. + */ + registerToolsForSession( + key: string, + tools: Tool[], + chatSessionId?: string, + agentLabel?: string, + ) { + this.runsByKey.set(key, { tools, chatSessionId, agentLabel }); } - unregisterSession(sessionId: string) { - this.toolsBySession.delete(sessionId); + unregisterSession(key: string) { + this.runsByKey.delete(key); + + const session = this.sessions.get(key); + this.sessions.delete(key); + // Tear down this run's transport/server so the port doesn't accumulate + // dead MCP sessions for the life of the Core process. + void session + ?.then(async ({ server, transport }) => { + await transport.close(); + await server.close(); + }) + .catch(() => {}); } - private async resolveTools(sessionId: string | undefined): Promise { - if (sessionId && this.toolsBySession.has(sessionId)) { - return this.toolsBySession.get(sessionId)!; + private async resolveTools(key: string): Promise { + const run = this.runsByKey.get(key); + if (run) { + return run.tools; } const config = await this.runtime.loadConfig(); return config?.tools ?? []; } + private resolveChatSessionId(key: string): string | undefined { + return this.runsByKey.get(key)?.chatSessionId; + } + /** Idempotent: safe to call on every provider invocation. */ async ensureStarted(): Promise { if (this.url) { @@ -106,29 +157,24 @@ export class ShadowCodeToolsMcpServer { return this.startPromise; } - private buildMcpServer(): Server { + // Each server instance serves exactly one agent run, so the key is captured + // here rather than read out of every request's _meta. + private buildMcpServer(key: string): Server { const server = new Server( { name: "shadow-code", version: "1.0.0" }, { capabilities: { tools: {} } }, ); - server.setRequestHandler(ListToolsRequestSchema, async (request) => { - const sessionId = (request.params?._meta as any)?.continueSessionId as - | string - | undefined; - const tools = await this.resolveTools(sessionId); + server.setRequestHandler(ListToolsRequestSchema, async () => { + const tools = await this.resolveTools(key); return { tools: tools.map(toolToMcpSchema) }; }); server.setRequestHandler(CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; - // The URL carries the originating Continue session id (see getUrlForSession). - const sessionId = (request.params._meta as any)?.continueSessionId as - | string - | undefined; - - const tools = await this.resolveTools(sessionId); + const sessionId = this.resolveChatSessionId(key); + const tools = await this.resolveTools(key); const tool = tools.find((t) => t.function.name === name); if (!tool) { @@ -149,6 +195,7 @@ export class ShadowCodeToolsMcpServer { args: args ?? {}, displayTitle: tool.displayTitle, wouldLikeTo: tool.wouldLikeTo, + agentLabel: this.runsByKey.get(key)?.agentLabel, }); if (!approved) { @@ -192,39 +239,97 @@ export class ShadowCodeToolsMcpServer { return server; } - private async start(): Promise { - const mcpServer = this.buildMcpServer(); - // NOTE: stateless mode (sessionIdGenerator: undefined) is broken on - // Windows in the installed SDK version - the transport silently 500s on - // every request after the first on the same server instance (confirmed - // via direct repro, unrelated to Claude Code CLI). Stateful mode doesn't - // hit this; the `mcp-session-id` it negotiates is purely a transport- - // level connection id and is unrelated to `continueSessionId` below - // (which identifies the Continue conversation, carried via query param - // and threaded into every request's `_meta` regardless of transport - // session). - this.transport = new StreamableHTTPServerTransport({ - sessionIdGenerator: () => randomUUID(), + /** + * One MCP Server + transport per agent run, created on that run's first + * request. Stored as the in-flight promise so two concurrent requests for the + * same key can't race into building two. + */ + private getOrCreateSession(key: string): Promise { + const existing = this.sessions.get(key); + if (existing) { + return existing; + } + + const created = (async (): Promise => { + const server = this.buildMcpServer(key); + // NOTE: stateless mode (sessionIdGenerator: undefined) is broken on + // Windows in the installed SDK version - the transport silently 500s on + // every request after the first on the same server instance (confirmed + // via direct repro, unrelated to Claude Code CLI). Stateful mode doesn't + // hit this; the `mcp-session-id` it negotiates is purely a transport- + // level connection id, unrelated to the `continueSessionId` query param + // that selects which run (and therefore which transport) a request + // belongs to. + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + }); + await server.connect(transport); + return { server, transport }; + })(); + + this.sessions.set(key, created); + // Don't cache a failed construction. + created.catch(() => { + if (this.sessions.get(key) === created) { + this.sessions.delete(key); + } }); - await mcpServer.connect(this.transport); + return created; + } + private async start(): Promise { this.httpServer = http.createServer((req, res) => { + // The query param selects which agent run - and therefore which MCP + // transport - this request belongs to. + const key = + new URL(req.url ?? "/", "http://127.0.0.1").searchParams.get( + "continueSessionId", + ) ?? DEFAULT_SESSION_KEY; + let body = ""; req.on("data", (chunk) => (body += chunk)); + req.on("error", () => { + // Client hung up mid-request; nothing useful left to do. + }); req.on("end", () => { - const parsed = body ? JSON.parse(body) : undefined; - const sessionId = new URL( - req.url ?? "/", - "http://127.0.0.1", - ).searchParams.get("continueSessionId"); - if (parsed && sessionId) { - parsed.params = parsed.params ?? {}; - parsed.params._meta = { - ...(parsed.params._meta ?? {}), - continueSessionId: sessionId, - }; - } - void this.transport!.handleRequest(req, res, parsed); + void (async () => { + try { + // The body must be parsed here and handed to the transport: its + // Node->Web request adapter cannot read the stream itself in this + // runtime (it throws "res.body.getReader is not a function"). + let parsed: unknown; + if (body) { + try { + parsed = JSON.parse(body); + } catch { + res.writeHead(400, { "Content-Type": "application/json" }); + res.end( + JSON.stringify({ + jsonrpc: "2.0", + error: { code: -32700, message: "Parse error" }, + id: null, + }), + ); + return; + } + } + + const { transport } = await this.getOrCreateSession(key); + await transport.handleRequest(req, res, parsed); + } catch (e) { + console.error("shadow-code-tools MCP request failed", e); + if (!res.headersSent) { + res.writeHead(500, { "Content-Type": "application/json" }); + } + res.end( + JSON.stringify({ + jsonrpc: "2.0", + error: { code: -32603, message: "Internal server error" }, + id: null, + }), + ); + } + })(); }); }); @@ -244,16 +349,32 @@ export class ShadowCodeToolsMcpServer { return this.url; } - /** URL to put in the generated --mcp-config for a given Continue session. */ - getUrlForSession(sessionId: string): string { + /** + * URL to put in the generated --mcp-config for a given agent run. The query + * param is what routes the run to its own MCP transport. + */ + getUrlForSession(key: string): string { if (!this.url) { throw new Error("ShadowCodeToolsMcpServer not started yet"); } - return `${this.url}?continueSessionId=${encodeURIComponent(sessionId)}`; + return `${this.url}?continueSessionId=${encodeURIComponent(key)}`; } async stop(): Promise { - await this.transport?.close(); + const sessions = [...this.sessions.values()]; + this.sessions.clear(); + this.runsByKey.clear(); + await Promise.all( + sessions.map(async (pending) => { + try { + const { server, transport } = await pending; + await transport.close(); + await server.close(); + } catch { + // Already torn down - nothing to do. + } + }), + ); await new Promise((resolve) => this.httpServer?.close(() => resolve()), ); diff --git a/core/mcp/verifyConcurrentSessions.mjs b/core/mcp/verifyConcurrentSessions.mjs new file mode 100644 index 00000000000..1e176a6bcfc --- /dev/null +++ b/core/mcp/verifyConcurrentSessions.mjs @@ -0,0 +1,124 @@ +/** + * Verifies that shadow-code's MCP server can serve several agent runs at once - + * the thing parallel subagents on the `claudecode` provider depend on. + * + * Run from the `core/` directory: + * + * node mcp/verifyConcurrentSessions.mjs + * + * Expected output: both runs return 200 with DIFFERENT mcp-session-id values. + * + * Why this is a script and not a Vitest: the assertion has to go through a real + * HTTP round-trip into @modelcontextprotocol/sdk's transport, and under Vitest + * that transport's response conversion throws + * "res.body.getReader is not a function" inside @hono/node-server - an artifact + * of Vitest's globals, unrelated to the code under test. + * + * Background: StreamableHTTPServerTransport in stateful mode binds to exactly + * one MCP client. A second `initialize` on the same transport is rejected with + * HTTP 400 "Invalid Request: Server already initialized", and close() never + * resets that flag. shadowCodeToolsServer.ts therefore keeps one transport per + * agent run, keyed by the ?continueSessionId= query param. Swap getOrCreate() + * below for a single shared transport and run-b turns into a 400 - that was the + * behavior before the per-run change. + */ +import { randomUUID } from "node:crypto"; +import * as http from "node:http"; + +import { Server } from "@modelcontextprotocol/sdk/server/index.js"; +import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js"; + +const sessions = new Map(); + +function getOrCreate(key) { + if (sessions.has(key)) { + return sessions.get(key); + } + const created = (async () => { + const server = new Server( + { name: "shadow-code", version: "1.0.0" }, + { capabilities: { tools: {} } }, + ); + const transport = new StreamableHTTPServerTransport({ + sessionIdGenerator: () => randomUUID(), + }); + await server.connect(transport); + return { server, transport }; + })(); + sessions.set(key, created); + return created; +} + +const httpServer = http.createServer((req, res) => { + const key = + new URL(req.url ?? "/", "http://127.0.0.1").searchParams.get( + "continueSessionId", + ) ?? "__default__"; + + let body = ""; + req.on("data", (chunk) => (body += chunk)); + req.on("end", () => { + void (async () => { + try { + const { transport } = await getOrCreate(key); + await transport.handleRequest( + req, + res, + body ? JSON.parse(body) : undefined, + ); + } catch (e) { + console.error("request failed", e); + if (!res.headersSent) res.writeHead(500); + res.end("{}"); + } + })(); + }); +}); + +const port = await new Promise((resolve) => + httpServer.listen(0, "127.0.0.1", () => resolve(httpServer.address().port)), +); +const base = `http://127.0.0.1:${port}/mcp`; + +const initialize = (key) => + fetch(`${base}?continueSessionId=${key}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json, text/event-stream", + }, + body: JSON.stringify({ + jsonrpc: "2.0", + id: 1, + method: "initialize", + params: { + protocolVersion: "2025-03-26", + capabilities: {}, + clientInfo: { name: "verify", version: "1.0.0" }, + }, + }), + }); + +const [a, b] = await Promise.all([initialize("run-a"), initialize("run-b")]); +const sessionA = a.headers.get("mcp-session-id"); +const sessionB = b.headers.get("mcp-session-id"); + +console.log(`run-a: ${a.status} mcp-session-id=${sessionA}`); +console.log(`run-b: ${b.status} mcp-session-id=${sessionB}`); + +const ok = a.status === 200 && b.status === 200 && sessionA !== sessionB; +console.log(ok ? "PASS: concurrent sessions supported" : "FAIL"); + +// Close the transports before the server, otherwise their still-open SSE +// handles trip a libuv assertion on Windows during teardown. +for (const pending of sessions.values()) { + const { server, transport } = await pending; + await transport.close(); + await server.close(); +} +console.log("transports closed cleanly"); +// No process.exit(): let Node drain its handles on its own. Forcing an exit +// while the SDK's SSE handles are still closing trips a libuv assertion on +// Windows, which is a property of the abrupt exit, not of close(). +httpServer.close(); +process.exitCode = ok ? 0 : 1; diff --git a/core/package-lock.json b/core/package-lock.json index a74e4ae7c59..e8a21f6c7f7 100644 --- a/core/package-lock.json +++ b/core/package-lock.json @@ -71,7 +71,7 @@ "tree-sitter-wasms": "^0.1.11", "untildify": "^6.0.0", "uuid": "^9.0.1", - "vectordb": "0.4.20", + "vectordb": "^0.4.20", "web-tree-sitter": "^0.21.0", "win-ca": "^3.5.1", "wink-nlp-utils": "^2.1.0", diff --git a/core/package.json b/core/package.json index 3e883ea24f1..c1924c549fe 100644 --- a/core/package.json +++ b/core/package.json @@ -116,7 +116,7 @@ "tree-sitter-wasms": "^0.1.11", "untildify": "^6.0.0", "uuid": "^9.0.1", - "vectordb": "0.4.20", + "vectordb": "^0.4.20", "web-tree-sitter": "^0.21.0", "win-ca": "^3.5.1", "wink-nlp-utils": "^2.1.0", diff --git a/core/promptFiles/createNewPromptFile.ts b/core/promptFiles/createNewPromptFile.ts index d7796763110..1f71455b6ca 100644 --- a/core/promptFiles/createNewPromptFile.ts +++ b/core/promptFiles/createNewPromptFile.ts @@ -48,7 +48,7 @@ export async function createNewPromptFileV2( const baseDirUri = joinPathsToUri( workspaceDirs[0], - promptPath ?? ".continue/prompts", + promptPath ?? ".shadow-code/prompts", ); // Find the first available filename diff --git a/core/promptFiles/getPromptFiles.ts b/core/promptFiles/getPromptFiles.ts index da6628d2f10..ead32b70d4b 100644 --- a/core/promptFiles/getPromptFiles.ts +++ b/core/promptFiles/getPromptFiles.ts @@ -62,7 +62,7 @@ export async function getAllPromptFiles( await Promise.all(fullDirs.map((dir) => getPromptFilesFromDir(ide, dir))) ).flat(); - // Also read from ~/.continue/prompts and ~/.continue/rules + // Also read from ~/.shadow-code/prompts and ~/.shadow-code/rules promptFiles.push(...readAllGlobalPromptFiles()); const promptFilesFromRulesDirectory = readAllGlobalPromptFiles( diff --git a/core/promptFiles/index.ts b/core/promptFiles/index.ts index 58be45fe835..f663eb09a38 100644 --- a/core/promptFiles/index.ts +++ b/core/promptFiles/index.ts @@ -1,10 +1,10 @@ import { ContextProviderName } from ".."; export const DEFAULT_PROMPTS_FOLDER_V1 = ".prompts"; -export const DEFAULT_PROMPTS_FOLDER_V2 = ".continue/prompts"; -export const DEFAULT_RULES_FOLDER = ".continue/rules"; +export const DEFAULT_PROMPTS_FOLDER_V2 = ".shadow-code/prompts"; +export const DEFAULT_RULES_FOLDER = ".shadow-code/rules"; -// Subdirectory names (without .continue/ prefix) +// Subdirectory names (without .shadow-code/ prefix) export const RULES_DIR_NAME = "rules"; export const PROMPTS_DIR_NAME = "prompts"; diff --git a/core/promptFiles/initPrompt.ts b/core/promptFiles/initPrompt.ts index f436d6e4344..ff575549e59 100644 --- a/core/promptFiles/initPrompt.ts +++ b/core/promptFiles/initPrompt.ts @@ -26,7 +26,7 @@ Analyze the project structure and key files to understand: - Build/deployment system ## Step 3: Generate ${initFilename} -Create a comprehensive ${initFilename} file in the .continue/rules/ directory with the following sections: +Create a comprehensive ${initFilename} file in the .shadow-code/rules/ directory with the following sections: 1. **Project Overview** - Brief description of the project's purpose @@ -70,7 +70,7 @@ Create a comprehensive ${initFilename} file in the .continue/rules/ directory wi Make sure your analysis is thorough but concise. Focus on information that would be most helpful to developers working on the project. If certain information isn't available from the codebase, make reasonable assumptions but mark these sections as needing verification. ## Step 4: Finalize -After creating the .continue/rules/${initFilename} file, provide a summary of what you've created and remind the user to: +After creating the .shadow-code/rules/${initFilename} file, provide a summary of what you've created and remind the user to: 1. Review and edit the file as needed 2. Commit it to their repository to share with their team 3. Explain that Continue will automatically load this file into context when working with the project diff --git a/core/protocol/core.ts b/core/protocol/core.ts index 2d65fcb43e4..655b874281c 100644 --- a/core/protocol/core.ts +++ b/core/protocol/core.ts @@ -310,6 +310,10 @@ export type ToCoreFromIdeOrWebviewProtocol = { mcpUiState?: McpUiState; }, ]; + // `tools/call` is a plain request, so the GUI's `abort` message (which is + // keyed on a streaming request's messageId) can't reach a running tool. This + // is how Stop terminates one - see Core.toolCallAbortControllers. + "tools/cancel": [{ toolCallId: string }, void]; "tools/evaluatePolicy": [ { toolName: string; diff --git a/core/protocol/passThrough.ts b/core/protocol/passThrough.ts index ac98b9b0318..b3065ca717b 100644 --- a/core/protocol/passThrough.ts +++ b/core/protocol/passThrough.ts @@ -87,6 +87,7 @@ export const WEBVIEW_TO_CORE_PASS_THROUGH: (keyof ToCoreFromWebviewProtocol)[] = "process/isBackgrounded", "process/killTerminalProcess", "models/fetch", + "tools/cancel", ]; // Message types to pass through from core to webview @@ -106,4 +107,5 @@ export const CORE_TO_WEBVIEW_PASS_THROUGH: (keyof ToWebviewFromCoreProtocol)[] = "sessionUpdate", "didCloseFiles", "toolCallPartialOutput", + "agent/authorizeToolCall", ]; diff --git a/core/protocol/webview.ts b/core/protocol/webview.ts index 05fe525b070..36f72e0beb6 100644 --- a/core/protocol/webview.ts +++ b/core/protocol/webview.ts @@ -42,10 +42,15 @@ export type ToWebviewFromIdeOrCoreProtocol = { sessionUpdate: [{ sessionInfo: any | undefined }, void]; toolCallPartialOutput: [{ toolCallId: string; contextItems: any[] }, void]; - // Sent by the in-process shadow-code-tools MCP server (core/mcp/shadowCodeToolsServer.ts) - // when a Claude Code CLI-driven tool call needs approval per the same policy rules - // that gate normal tool calls. Blocks until the user accepts/rejects in the GUI. - "claudeCodeCli/authorizeToolCall": [ + // Sent whenever a tool call originates somewhere other than the GUI's own + // streaming loop - the in-process shadow-code-tools MCP server driving the + // Claude Code CLI (core/mcp/shadowCodeToolsServer.ts), or the subagent runner + // (core/agent/subagentRunner.ts). Those callers reach Core's handleToolCall + // directly, which executes unconditionally, so this is their equivalent of + // the GUI's normal policy gate. Blocks until the user accepts/rejects when + // the resolved policy is allowedWithPermission; resolves immediately + // otherwise. + "agent/authorizeToolCall": [ { approvalId: string; toolCallId: string; @@ -54,6 +59,8 @@ export type ToWebviewFromIdeOrCoreProtocol = { args: Record; displayTitle?: string; wouldLikeTo?: string; + /** Which agent is asking, e.g. a subagent's task description. */ + agentLabel?: string; }, { approved: boolean }, ]; diff --git a/core/test/jest.global-setup.ts b/core/test/jest.global-setup.ts index c0a00ddc491..7a9064a10ef 100644 --- a/core/test/jest.global-setup.ts +++ b/core/test/jest.global-setup.ts @@ -1,12 +1,12 @@ import fs from "fs"; import path from "path"; -// Sets up the GLOBAL directory for testing - equivalent to ~/.continue -// IMPORTANT: the CONTINUE_GLOBAL_DIR environment variable is used in utils/paths for getting all local paths +// Sets up the GLOBAL directory for testing - equivalent to ~/.shadow-code +// IMPORTANT: the SHADOW_CODE_GLOBAL_DIR environment variable is used in utils/paths for getting all local paths export default async function () { - process.env.CONTINUE_GLOBAL_DIR = path.join(__dirname, ".continue-test"); - if (fs.existsSync(process.env.CONTINUE_GLOBAL_DIR)) { - fs.rmSync(process.env.CONTINUE_GLOBAL_DIR, { + process.env.SHADOW_CODE_GLOBAL_DIR = path.join(__dirname, ".continue-test"); + if (fs.existsSync(process.env.SHADOW_CODE_GLOBAL_DIR)) { + fs.rmSync(process.env.SHADOW_CODE_GLOBAL_DIR, { recursive: true, force: true, }); diff --git a/core/test/jest.setup-after-env.js b/core/test/jest.setup-after-env.js index 29b5157277c..3bdd54cf124 100644 --- a/core/test/jest.setup-after-env.js +++ b/core/test/jest.setup-after-env.js @@ -20,8 +20,8 @@ globalThis.TextDecoder = TextDecoder; // TODO - currently causing tests to fail because sqlite is still running for some reason // const clearTestDirectory = () => { -// if (fs.existsSync(process.env.CONTINUE_GLOBAL_DIR!)) { -// fs.rmSync(process.env.CONTINUE_GLOBAL_DIR!, { recursive: true }); +// if (fs.existsSync(process.env.SHADOW_CODE_GLOBAL_DIR!)) { +// fs.rmSync(process.env.SHADOW_CODE_GLOBAL_DIR!, { recursive: true }); // } // }; diff --git a/core/test/testEnv.test.ts b/core/test/testEnv.test.ts index 3ebddd64b1f..8a5d6596646 100644 --- a/core/test/testEnv.test.ts +++ b/core/test/testEnv.test.ts @@ -1,6 +1,6 @@ describe("Test environment", () => { - test("should have CONTINUE_GLOBAL_DIR env var set to .continue-test", () => { - expect(process.env.CONTINUE_GLOBAL_DIR).toBeDefined(); - expect(process.env.CONTINUE_GLOBAL_DIR)?.toMatch(/\.continue-test$/); + test("should have SHADOW_CODE_GLOBAL_DIR env var set to .continue-test", () => { + expect(process.env.SHADOW_CODE_GLOBAL_DIR).toBeDefined(); + expect(process.env.SHADOW_CODE_GLOBAL_DIR)?.toMatch(/\.continue-test$/); }); }); diff --git a/core/test/vitest.global-setup.ts b/core/test/vitest.global-setup.ts index c0a00ddc491..7a9064a10ef 100644 --- a/core/test/vitest.global-setup.ts +++ b/core/test/vitest.global-setup.ts @@ -1,12 +1,12 @@ import fs from "fs"; import path from "path"; -// Sets up the GLOBAL directory for testing - equivalent to ~/.continue -// IMPORTANT: the CONTINUE_GLOBAL_DIR environment variable is used in utils/paths for getting all local paths +// Sets up the GLOBAL directory for testing - equivalent to ~/.shadow-code +// IMPORTANT: the SHADOW_CODE_GLOBAL_DIR environment variable is used in utils/paths for getting all local paths export default async function () { - process.env.CONTINUE_GLOBAL_DIR = path.join(__dirname, ".continue-test"); - if (fs.existsSync(process.env.CONTINUE_GLOBAL_DIR)) { - fs.rmSync(process.env.CONTINUE_GLOBAL_DIR, { + process.env.SHADOW_CODE_GLOBAL_DIR = path.join(__dirname, ".continue-test"); + if (fs.existsSync(process.env.SHADOW_CODE_GLOBAL_DIR)) { + fs.rmSync(process.env.SHADOW_CODE_GLOBAL_DIR, { recursive: true, force: true, }); diff --git a/core/tools/builtIn.ts b/core/tools/builtIn.ts index 1af259b46cb..858e46e744e 100644 --- a/core/tools/builtIn.ts +++ b/core/tools/builtIn.ts @@ -17,6 +17,7 @@ export enum BuiltInToolNames { FetchUrlContent = "fetch_url_content", CodebaseTool = "codebase", ReadSkill = "read_skill", + SpawnSubagents = "spawn_subagents", // Shadow chat history tools — backed by the local per-session SQLite cache ShadowGetChatHistory = "shadow_get_chat_history", diff --git a/core/tools/callTool.ts b/core/tools/callTool.ts index 6a338c3d305..9fdb35698c0 100644 --- a/core/tools/callTool.ts +++ b/core/tools/callTool.ts @@ -21,6 +21,7 @@ import { readSkillImpl } from "./implementations/readSkill"; import { requestRuleImpl } from "./implementations/requestRule"; import { runTerminalCommandImpl } from "./implementations/runTerminalCommand"; import { searchWebImpl } from "./implementations/searchWeb"; +import { spawnSubagentsImpl } from "./implementations/spawnSubagents"; import { editExistingFileUnsupportedImpl, multiEditServerImpl, @@ -245,6 +246,8 @@ export async function callBuiltInTool( return await codebaseToolImpl(args, extras); case BuiltInToolNames.ReadSkill: return await readSkillImpl(args, extras); + case BuiltInToolNames.SpawnSubagents: + return await spawnSubagentsImpl(args, extras); case BuiltInToolNames.ViewRepoMap: return await viewRepoMapImpl(args, extras); case BuiltInToolNames.ViewSubdirectory: diff --git a/core/tools/constants.ts b/core/tools/constants.ts index 332a270efff..8c822fc2cf8 100644 --- a/core/tools/constants.ts +++ b/core/tools/constants.ts @@ -1,3 +1,13 @@ +/** + * Hard cap on how many model round-trips one subagent may take before it is + * forced to report back. Only applies to providers we drive ourselves; the + * Claude Code CLI runs its own internal loop that we don't step. + */ +export const SUBAGENT_MAX_ITERATIONS = 25; + +/** How many subagents from one spawn_subagents call may run at once. */ +export const SUBAGENT_MAX_CONCURRENCY = 5; + export const NO_TOOL_CALL_OUTPUT_MESSAGE = "No tool output"; export const CANCELLED_TOOL_CALL_MESSAGE = "The user cancelled this tool call."; export const ERRORED_TOOL_CALL_OUTPUT_MESSAGE = diff --git a/core/tools/definitions/createRuleBlock.ts b/core/tools/definitions/createRuleBlock.ts index f236bdb0f69..0ce0892c88c 100644 --- a/core/tools/definitions/createRuleBlock.ts +++ b/core/tools/definitions/createRuleBlock.ts @@ -62,7 +62,7 @@ export const createRuleBlock: Tool = { defaultToolPolicy: "disabled", systemMessageDescription: { prefix: `Sometimes the user will provide feedback or guidance on your output. If you were not aware of these "rules", consider using the ${BuiltInToolNames.CreateRuleBlock} tool to persist the rule for future interactions. -This tool cannot be used to edit existing rules, but you can search in the ".continue/rules" folder and use the edit tool to manage rules. +This tool cannot be used to edit existing rules, but you can search in the ".shadow-code/rules" folder and use the edit tool to manage rules. To create a rule, respond with a ${BuiltInToolNames.CreateRuleBlock} tool call and the following arguments: - name: ${NAME_ARG_DESC} - rule: ${RULE_ARG_DESC} diff --git a/core/tools/definitions/index.ts b/core/tools/definitions/index.ts index 800738ab9e6..6ffa93a9e89 100644 --- a/core/tools/definitions/index.ts +++ b/core/tools/definitions/index.ts @@ -15,6 +15,7 @@ export { readSkillTool } from "./readSkill"; export { requestRuleTool } from "./requestRule"; export { runTerminalCommandTool } from "./runTerminalCommand"; export { searchWebTool } from "./searchWeb"; +export { spawnSubagentsTool } from "./spawnSubagents"; export { shadowGetChatHistoryTool, shadowSearchMessagesTool, diff --git a/core/tools/definitions/spawnSubagents.ts b/core/tools/definitions/spawnSubagents.ts new file mode 100644 index 00000000000..5e5cc7b5285 --- /dev/null +++ b/core/tools/definitions/spawnSubagents.ts @@ -0,0 +1,81 @@ +import { Tool } from "../.."; +import { BUILT_IN_GROUP_NAME, BuiltInToolNames } from "../builtIn"; + +export const spawnSubagentsTool: Tool = { + type: "function", + displayTitle: "Spawn Subagents", + wouldLikeTo: "delegate work to subagents", + isCurrently: "running subagents", + hasAlready: "gathered results from subagents", + readonly: false, + group: BUILT_IN_GROUP_NAME, + function: { + name: BuiltInToolNames.SpawnSubagents, + description: `Delegate one or more self-contained tasks to subagents that run in parallel and report back. + +Each subagent works in its own context and returns a single written report. Only that report enters this conversation — none of the files it read, searches it ran, or dead ends it explored do. Use this whenever a task would otherwise flood the conversation with intermediate output. + +Good uses: +- Open-ended search across many files ("find every place X is implemented") +- Several independent questions that can be answered at the same time +- Any investigation where you only need the conclusion, not the raw material + +Do not use it for: +- A task needing one or two known file reads — just read them +- Work that must happen in a specific order, or where one task needs another's answer first +- Anything requiring back-and-forth: a subagent cannot ask you questions + +Write each prompt as if to someone who cannot see this conversation. State the goal, the relevant paths or names, and exactly what the report should contain. Launch independent tasks together in a single call rather than one at a time.`, + parameters: { + type: "object", + properties: { + tasks: { + type: "array", + description: + "The tasks to run in parallel. Each becomes one subagent.", + items: { + type: "object", + properties: { + description: { + type: "string", + description: + "A 3-5 word label for this task, shown to the user (e.g. 'Find auth entrypoints').", + }, + prompt: { + type: "string", + description: + "The subagent's complete instructions. It sees only this — no other part of the conversation. Say what to investigate and what its final report must contain.", + }, + allowed_tools: { + type: "array", + description: + "Optional. Tool names this subagent may use. Defaults to the read-only tools. Only widen this when the task genuinely requires it.", + items: { type: "string" }, + }, + model: { + type: "string", + description: + "Optional. Model to run this subagent on. Defaults to the current chat model.", + }, + }, + required: ["description", "prompt"], + }, + }, + }, + required: ["tasks"], + }, + }, + systemMessageDescription: { + prefix: `To delegate self-contained work to parallel subagents whose intermediate output never enters this conversation, use the ${BuiltInToolNames.SpawnSubagents} tool with a "tasks" array. Each task needs a short "description" and a fully self-contained "prompt". For example:`, + exampleArgs: [ + [ + "tasks", + '[{"description": "Find auth entrypoints", "prompt": "Search the repo for every HTTP route that performs authentication. Report each one as file:line with a one-line summary."}]', + ], + ], + }, + // Spawning is itself inert: it reads nothing and writes nothing. Every tool + // call a subagent makes is independently gated by the normal policy engine + // (see agent/authorizeToolCall), which is the real security boundary. + defaultToolPolicy: "allowedWithoutPermission", +}; diff --git a/core/tools/implementations/createRuleBlock.test.ts b/core/tools/implementations/createRuleBlock.test.ts index 4808037b48b..345fcd2129c 100644 --- a/core/tools/implementations/createRuleBlock.test.ts +++ b/core/tools/implementations/createRuleBlock.test.ts @@ -55,7 +55,7 @@ test("createRuleBlockImpl should create a filename based on sanitized rule name await createRuleBlockImpl(args, mockExtras as any); const fileUri = mockIde.writeFile.mock.calls[0][0]; - expect(fileUri).toBe("/.continue/rules/special-chracters-spaces.md"); + expect(fileUri).toBe("/.shadow-code/rules/special-chracters-spaces.md"); }); test("createRuleBlockImpl should create a rule with description pattern", async () => { diff --git a/core/tools/implementations/lsTool.vitest.ts b/core/tools/implementations/lsTool.vitest.ts index 1b2fef32abd..60decc707d2 100644 --- a/core/tools/implementations/lsTool.vitest.ts +++ b/core/tools/implementations/lsTool.vitest.ts @@ -37,7 +37,7 @@ test("resolveLsToolDirPath preserves forward slashes", () => { }); test("resolveLsToolDirPath preserves hidden directory paths", () => { - expect(resolveLsToolDirPath(".continue/rules")).toBe(".continue/rules"); + expect(resolveLsToolDirPath(".shadow-code/rules")).toBe(".shadow-code/rules"); }); test("resolveLsToolDirPath preserves hidden directory name only", () => { diff --git a/core/tools/implementations/spawnSubagents.ts b/core/tools/implementations/spawnSubagents.ts new file mode 100644 index 00000000000..b4bec94f04d --- /dev/null +++ b/core/tools/implementations/spawnSubagents.ts @@ -0,0 +1,134 @@ +import { ContextItem } from "../.."; +import { + runSubagent, + SubagentProgress, + SubagentTask, +} from "../../agent/subagentRunner"; +import { SUBAGENT_MAX_CONCURRENCY } from "../constants"; +import { ToolImpl } from "."; + +function parseTasks(args: any): SubagentTask[] { + const raw = args?.tasks; + if (!Array.isArray(raw) || raw.length === 0) { + throw new Error( + "`tasks` must be a non-empty array of { description, prompt } objects.", + ); + } + + return raw.map((task: any, i: number) => { + if (typeof task?.description !== "string" || !task.description.trim()) { + throw new Error(`tasks[${i}].description must be a non-empty string.`); + } + if (typeof task?.prompt !== "string" || !task.prompt.trim()) { + throw new Error(`tasks[${i}].prompt must be a non-empty string.`); + } + return { + description: task.description.trim(), + prompt: task.prompt, + allowed_tools: Array.isArray(task.allowed_tools) + ? task.allowed_tools.filter((t: unknown) => typeof t === "string") + : undefined, + model: typeof task.model === "string" ? task.model : undefined, + }; + }); +} + +function statusLabel(progress: SubagentProgress): string { + switch (progress.status) { + case "running": + return progress.iterations > 0 + ? `Working (step ${progress.iterations})` + : "Starting"; + case "done": + return "Done"; + case "errored": + return "Failed"; + case "canceled": + return "Canceled"; + } +} + +function toContextItem( + task: SubagentTask, + progress: SubagentProgress, +): ContextItem { + return { + name: task.description, + description: statusLabel(progress), + // The live transcript while running; the final report once finished, since + // that's what actually matters to the reader afterwards. + content: + progress.status === "done" && progress.report + ? progress.report + : progress.transcript || "_Starting…_", + status: progress.status, + }; +} + +/** + * Runs every task in one call concurrently. Fanning out from a single tool call + * (rather than relying on the model to emit N parallel tool calls) is + * deliberate: it doesn't depend on the model batching calls, on delta + * index handling, or on the GUI dispatching pending calls in parallel. + */ +export const spawnSubagentsImpl: ToolImpl = async (args, extras) => { + if ((extras.subagentDepth ?? 0) > 0) { + throw new Error( + "Subagents cannot spawn further subagents. Complete this task yourself with the tools you have.", + ); + } + + const tasks = parseTasks(args); + + const progressByIndex: SubagentProgress[] = tasks.map(() => ({ + status: "running", + iterations: 0, + transcript: "", + })); + + const publish = () => { + if (!extras.onPartialOutput || !extras.toolCallId) { + return; + } + extras.onPartialOutput({ + toolCallId: extras.toolCallId, + contextItems: tasks.map((task, i) => + toContextItem(task, progressByIndex[i]), + ), + }); + }; + + publish(); + + // Bounded concurrency: each subagent may be a whole `claude` subprocess, so + // an over-eager model asking for 20 at once shouldn't fork 20 processes. + let nextIndex = 0; + const workers = Array.from( + { length: Math.min(SUBAGENT_MAX_CONCURRENCY, tasks.length) }, + async () => { + while (nextIndex < tasks.length) { + const index = nextIndex++; + progressByIndex[index] = await runSubagent({ + task: tasks[index], + llm: extras.llm, + config: extras.config, + ide: extras.ide, + fetch: extras.fetch, + codeBaseIndexer: extras.codeBaseIndexer, + sessionId: extras.sessionId, + signal: extras.signal, + requestApproval: extras.requestApproval, + onProgress: (progress) => { + progressByIndex[index] = progress; + publish(); + }, + }); + publish(); + } + }, + ); + + await Promise.all(workers); + + return tasks.map((task, i) => toContextItem(task, progressByIndex[i])); +}; diff --git a/core/tools/index.ts b/core/tools/index.ts index 5812359fcf2..d7b04399d0f 100644 --- a/core/tools/index.ts +++ b/core/tools/index.ts @@ -13,6 +13,7 @@ export const getBaseToolDefinitions = () => [ toolDefinitions.lsTool, toolDefinitions.createRuleBlock, toolDefinitions.fetchUrlContentTool, + toolDefinitions.spawnSubagentsTool, toolDefinitions.shadowGetChatHistoryTool, toolDefinitions.shadowSearchMessagesTool, toolDefinitions.shadowSemanticSearchTool, diff --git a/core/util/constants.ts b/core/util/constants.ts index 9418c48ed42..b9977184b10 100644 --- a/core/util/constants.ts +++ b/core/util/constants.ts @@ -1,8 +1,6 @@ -export const EXTENSION_NAME = "continue"; +export const EXTENSION_NAME = "shadowCode"; export const NEW_SESSION_TITLE = "New Session"; -export const GITHUB_LINK = - "https://github.com/continuedev/continue/issues/new/choose"; -export const DISCUSSIONS_LINK = - "https://github.com/continuedev/continue/discussions"; +export const GITHUB_LINK = ""; +export const DISCUSSIONS_LINK = ""; diff --git a/core/util/grepSearch.ts b/core/util/grepSearch.ts index be5b7ee082f..bbfb0358c9e 100644 --- a/core/util/grepSearch.ts +++ b/core/util/grepSearch.ts @@ -1,7 +1,7 @@ /* Formats the output of a grep search to reduce unnecessary indentation, lines, etc Assumes a command with these params - ripgrep -i --ignore-file .continueignore --ignore-file .gitignore -C 2 --heading -m 100 -e . + ripgrep -i --ignore-file .shadow-codeignore --ignore-file .gitignore -C 2 --heading -m 100 -e . Also can truncate the output to a specified number of characters */ diff --git a/core/util/grepSearch.vitest.ts b/core/util/grepSearch.vitest.ts index df1a71efd82..72de1f84035 100644 --- a/core/util/grepSearch.vitest.ts +++ b/core/util/grepSearch.vitest.ts @@ -2,7 +2,7 @@ import { expect, test } from "vitest"; import { formatGrepSearchResults } from "./grepSearch"; // Sample grep output mimicking what would come from ripgrep with the params: -// ripgrep -i --ignore-file .continueignore --ignore-file .gitignore -C 2 --heading -m 100 -e . +// ripgrep -i --ignore-file .shadow-codeignore --ignore-file .gitignore -C 2 --heading -m 100 -e . const sampleGrepOutput = `./program.cs Console.WriteLine("Hello World!"); Calculator calc = new Calculator(); diff --git a/core/util/historyUtils.ts b/core/util/historyUtils.ts index fc0939c7bcf..e20fb2c268a 100644 --- a/core/util/historyUtils.ts +++ b/core/util/historyUtils.ts @@ -42,7 +42,7 @@ export function toMarkDown(history: ChatMessage[], time?: Date): string { if (!time) { time = new Date(); } - let content = `### [Continue](https://continue.dev) session transcript\n Exported: ${time.toLocaleString()}`; + let content = `### Shadow Code session transcript\n Exported: ${time.toLocaleString()}`; for (const msg of history) { let msgText = renderChatMessage(msg); diff --git a/core/util/paths.ts b/core/util/paths.ts index 323697f774c..66dd25d91df 100644 --- a/core/util/paths.ts +++ b/core/util/paths.ts @@ -24,15 +24,15 @@ export function setConfigFilePermissions(filePath: string): void { } } -const CONTINUE_GLOBAL_DIR = (() => { - const configPath = process.env.CONTINUE_GLOBAL_DIR; +const SHADOW_CODE_GLOBAL_DIR = (() => { + const configPath = process.env.SHADOW_CODE_GLOBAL_DIR; if (configPath) { // Convert relative path to absolute paths based on current working directory return path.isAbsolute(configPath) ? configPath : path.resolve(process.cwd(), configPath); } - return path.join(os.homedir(), ".continue"); + return path.join(os.homedir(), ".shadow-code"); })(); // export const DEFAULT_CONFIG_TS_CONTENTS = `import { Config } from "./types"\n\nexport function modifyConfig(config: Config): Config { @@ -58,7 +58,7 @@ export function getContinueUtilsPath(): string { export function getGlobalContinueIgnorePath(): string { const continueIgnorePath = path.join( getContinueGlobalPath(), - ".continueignore", + ".shadow-codeignore", ); if (!fs.existsSync(continueIgnorePath)) { fs.writeFileSync(continueIgnorePath, ""); @@ -67,8 +67,8 @@ export function getGlobalContinueIgnorePath(): string { } export function getContinueGlobalPath(): string { - // This is ~/.continue on mac/linux - const continuePath = CONTINUE_GLOBAL_DIR; + // This is ~/.shadow-code on mac/linux + const continuePath = SHADOW_CODE_GLOBAL_DIR; if (!fs.existsSync(continuePath)) { fs.mkdirSync(continuePath); } @@ -156,9 +156,9 @@ export function getConfigTsPath(): string { fs.writeFileSync( packageJsonPath, JSON.stringify({ - name: "continue-config", + name: "shadow-code-config", version: "1.0.0", - description: "My Continue Configuration", + description: "My Shadow Code Configuration", main: "config.js", }), ); @@ -209,7 +209,10 @@ export function getTsConfigPath(): string { export function getContinueRcPath(): string { // Disable indexing of the config folder to prevent infinite loops - const continuercPath = path.join(getContinueGlobalPath(), ".continuerc.json"); + const continuercPath = path.join( + getContinueGlobalPath(), + ".shadow-coderc.json", + ); if (!fs.existsSync(continuercPath)) { fs.writeFileSync( continuercPath, diff --git a/docs-site/app/layout.tsx b/docs-site/app/layout.tsx index ced9a7e3407..93b4c391ee7 100644 --- a/docs-site/app/layout.tsx +++ b/docs-site/app/layout.tsx @@ -6,9 +6,8 @@ import "./globals.css"; import "./docs.css"; export const metadata: Metadata = { - title: "Continue Docs", - description: - "Documentation for Continue — the open-source AI code assistant.", + title: "Shadow Code Docs", + description: "Documentation for Shadow Code.", }; export default function RootLayout({ diff --git a/docs-site/lib/basePath.ts b/docs-site/lib/basePath.ts index afea87186b7..5ae6ccc4e4f 100644 --- a/docs-site/lib/basePath.ts +++ b/docs-site/lib/basePath.ts @@ -1,7 +1,7 @@ /** * Base path for the deployed docs site. * - * On GitHub Pages the site lives under `/continue/`, so raw absolute asset + * On GitHub Pages the site lives under `/shadow-code/`, so raw absolute asset * references (search index, src, etc.) must be prefixed manually. Next * applies basePath automatically to , next/image and /_next assets, but * NOT to plain string paths, so use `withBasePath` for those. diff --git a/docs-site/lib/resolveHref.ts b/docs-site/lib/resolveHref.ts index 70b377b9fdd..3b1796b2ab4 100644 --- a/docs-site/lib/resolveHref.ts +++ b/docs-site/lib/resolveHref.ts @@ -1,7 +1,7 @@ /** * Resolve internal paths for the standalone docs app. * - * On the docs subdomain, /docs/X becomes /X (we're already on docs.continue.dev). + * On the docs subdomain, /docs/X becomes /X . * Cross-app links get absolute URLs. */ export function resolveHref(path: string): string { @@ -10,10 +10,7 @@ export function resolveHref(path: string): string { if (path === "/docs") return "/"; // Cross-app links → absolute URLs - if (path.startsWith("/blog")) - return `https://blog.continue.dev${path.slice(5) || ""}`; - if (path === "/login") return "https://continue.dev/login"; - if (path === "/") return "https://continue.dev"; + if (path.startsWith("/blog")) return path; // Everything else stays as-is return path; diff --git a/docs-site/package.json b/docs-site/package.json index 72d0fe2f72d..81d727ce7e4 100644 --- a/docs-site/package.json +++ b/docs-site/package.json @@ -1,5 +1,5 @@ { - "name": "continue-docs", + "name": "shadow-code-docs", "version": "0.1.0", "private": true, "scripts": { diff --git a/docs/autocomplete/how-to-use-it.mdx b/docs/autocomplete/how-to-use-it.mdx index 93dbe6b75a8..1f9f8f6b5df 100644 --- a/docs/autocomplete/how-to-use-it.mdx +++ b/docs/autocomplete/how-to-use-it.mdx @@ -2,16 +2,16 @@ title: "Autocomplete" sidebarTitle: "How To Use AI Autocomplete" icon: "circle-question" -description: "Learn how to use Continue's AI-powered code autocomplete feature with keyboard shortcuts for accepting, rejecting, or partially accepting inline suggestions as you type" +description: "Learn how to use Shadow Code's AI-powered code autocomplete feature with keyboard shortcuts for accepting, rejecting, or partially accepting inline suggestions as you type" --- -## How to Use AI Code Autocomplete in Continue +## How to Use AI Code Autocomplete in Shadow Code -Autocomplete provides inline code suggestions as you type. To enable it, simply click the "Continue" button in the status bar at the bottom right of your IDE or ensure the "Enable Tab Autocomplete" option is checked in your IDE settings. +Autocomplete provides inline code suggestions as you type. To enable it, simply click the "Shadow Code" button in the status bar at the bottom right of your IDE or ensure the "Enable Tab Autocomplete" option is checked in your IDE settings. ### Accepting a Full Suggestion diff --git a/docs/chat/how-to-use-it.mdx b/docs/chat/how-to-use-it.mdx index e5851717df3..8a023bac04d 100644 --- a/docs/chat/how-to-use-it.mdx +++ b/docs/chat/how-to-use-it.mdx @@ -2,14 +2,14 @@ title: "Chat" sidebarTitle: "How To Use Chat Mode" icon: "circle-question" -description: "Learn how to use Continue's Chat mode to solve coding problems without leaving your IDE, including code context sharing, applying generated solutions, and switching between models" +description: "Learn how to use Shadow Code's Chat mode to solve coding problems without leaving your IDE, including code context sharing, applying generated solutions, and switching between models" --- -## How to Use AI Chat in Continue for Coding Help +## How to Use AI Chat in Shadow Code for Coding Help Chat makes it easy to ask for help from an LLM without needing to leave the IDE. You send it a task, including any relevant information, and it replies with the text / code most likely to complete the task. If it does not give you what you want, then you can send follow up messages to clarify and adjust its approach until the task is completed. @@ -35,6 +35,6 @@ When the LLM replies with edits to a file, you can click the “Apply” button. Once you complete a task and want to start a new one, press `cmd/ctrl` + `L` (VS Code) or `cmd/ctrl` + `J` (JetBrains) to begin a new session, ensuring only relevant context for the next task is provided to the LLM. -## Change AI Models in Continue Chat for Different Coding Needs +## Change AI Models in Shadow Code Chat for Different Coding Needs If you have configured multiple models, you can switch between models using the dropdown or by pressing `cmd/ctrl` + `’` \ No newline at end of file diff --git a/docs/cli/configuration.mdx b/docs/cli/configuration.mdx index 14e170d6eed..80c131fb340 100644 --- a/docs/cli/configuration.mdx +++ b/docs/cli/configuration.mdx @@ -6,7 +6,7 @@ title: "Configuration" 1. **`--config` flag** — a file path passed at launch 2. **Saved config** — the last-used configuration, persisted across sessions -3. **Default** — `~/.continue/config.yaml` +3. **Default** — `~/.shadow-code/config.yaml` ## `--config` flag @@ -30,7 +30,7 @@ This shows your available local configs. The selection is saved for next time. ## `config.yaml` -`cn` looks for `~/.continue/config.yaml`. This file uses the same format as the IDE extensions — see [config.yaml reference](/customize/deep-dives/configuration) for the full schema. +`cn` looks for `~/.shadow-code/config.yaml`. This file uses the same format as the IDE extensions — see [config.yaml reference](/customize/deep-dives/configuration) for the full schema. ## CLI-specific flags diff --git a/docs/cli/quickstart.mdx b/docs/cli/quickstart.mdx index 6913da993ad..bfc593257a4 100644 --- a/docs/cli/quickstart.mdx +++ b/docs/cli/quickstart.mdx @@ -7,7 +7,7 @@ import CLIInstall from '/snippets/cli-install.mdx' -Continue CLI (`cn`) is a terminal-based coding agent. It can edit files, run commands, and work through multi-step tasks — the same agent that powers the Continue IDE extensions, running in your terminal. +Shadow Code CLI (`cn`) is a terminal-based coding agent. It can edit files, run commands, and work through multi-step tasks — the same agent that powers the Shadow Code IDE extensions, running in your terminal. @@ -26,7 +26,7 @@ cn --version ### Requirements - **Node.js 20+** — needed for the npm install path. The shell installer bundles its own runtime. -- A [Continue](https://continue.dev) account, or an Anthropic API key. +- A [Shadow Code]() account, or an Anthropic API key. ## First run @@ -35,17 +35,17 @@ cd your-project cn ``` -On first launch you'll be asked to log in with [Continue](https://continue.dev) or enter an Anthropic API key. After that, you're in a session and can start typing. +On first launch you'll be asked to log in with [Shadow Code]() or enter an Anthropic API key. After that, you're in a session and can start typing. ## Authentication -### Log in with Continue +### Log in with Shadow Code ```bash cn login ``` -This opens your browser to authenticate with [Continue](https://continue.dev). Once authenticated, `cn` can use your configured assistants, models, and MCP servers from the platform. +This opens your browser to authenticate with [Shadow Code](). Once authenticated, `cn` can use your configured assistants, models, and MCP servers from the platform. ### API key (headless / CI) @@ -58,7 +58,7 @@ cn -p "your prompt" ### Local API key -If you don't want to use Continue, you can use an Anthropic API key directly. On first launch, `cn` will prompt you to choose between logging in with Continue or entering an Anthropic API key. +If you don't want to use Shadow Code, you can use an Anthropic API key directly. On first launch, `cn` will prompt you to choose between logging in with Shadow Code or entering an Anthropic API key. ## Two modes diff --git a/docs/cli/tool-permissions.mdx b/docs/cli/tool-permissions.mdx index 321755c722c..1a5dada3b4d 100644 --- a/docs/cli/tool-permissions.mdx +++ b/docs/cli/tool-permissions.mdx @@ -49,10 +49,10 @@ cn --allow Bash --exclude "Bash(npm install*)" ## `permissions.yaml` -Persistent permissions are stored in `~/.continue/permissions.yaml`. This file is updated when you choose "Continue + don't ask again" in the TUI approval prompt. +Persistent permissions are stored in `~/.shadow-code/permissions.yaml`. This file is updated when you choose "Shadow Code + don't ask again" in the TUI approval prompt. ```yaml -# ~/.continue/permissions.yaml +# ~/.shadow-code/permissions.yaml allow: - Read(*) - Write(**/*.ts) diff --git a/docs/cli/tui-mode.mdx b/docs/cli/tui-mode.mdx index 673bdafad43..bff269edf62 100644 --- a/docs/cli/tui-mode.mdx +++ b/docs/cli/tui-mode.mdx @@ -2,7 +2,7 @@ title: "TUI Mode" --- -Run `cn` to start an interactive session. You get a prompt where you can type messages, reference files with `@`, and use slash commands. `cn` uses the same underlying agent as the Continue IDE extensions. +Run `cn` to start an interactive session. You get a prompt where you can type messages, reference files with `@`, and use slash commands. `cn` uses the same underlying agent as the Shadow Code IDE extensions. @@ -30,7 +30,7 @@ Type `/` to see available commands. Run `/help` for the full list. | `/clear` | Clear the chat history | | `/login` | Authenticate with your account | | `/logout` | Sign out of your current session | -| `/update` | Update the Continue CLI | +| `/update` | Update the Shadow Code CLI | | `/whoami` | Check who you're currently logged in as | | `/info` | Show session information, including token usage and cost | | `/model` | Switch between configured chat models | @@ -45,7 +45,7 @@ Type `/` to see available commands. Run `/help` for the full list. | `/exit` | Exit the chat | | `/jobs` | List background jobs | -When you're connected to a remote environment, Continue also adds remote-only slash commands such as `/diff` and `/apply`. +When you're connected to a remote environment, Shadow Code also adds remote-only slash commands such as `/diff` and `/apply`. ## Resume previous sessions @@ -76,8 +76,8 @@ This is useful when the agent needs decisions like scope, preferences, or enviro Tools that can modify your system (file writes, terminal commands) prompt for approval before executing. You get three options: -- **Continue** — approve this call -- **Continue + don't ask again** — approve and save a policy rule to `~/.continue/permissions.yaml` +- **Shadow Code** — approve this call +- **Shadow Code + don't ask again** — approve and save a policy rule to `~/.shadow-code/permissions.yaml` - **No** — reject the call and give the agent new instructions Read-only tools (`Read`, `List`, `Search`, `Fetch`, `Diff`, `AskQuestion`) run automatically. See [tool permissions](/cli/tool-permissions) for the full policy system. diff --git a/docs/customize/custom-providers.mdx b/docs/customize/custom-providers.mdx index 14e8b2c8be1..972581a7f6f 100644 --- a/docs/customize/custom-providers.mdx +++ b/docs/customize/custom-providers.mdx @@ -3,7 +3,7 @@ title: "Context Providers" description: "Context Providers allow you to type '@' and see a dropdown of content that can all be fed to the LLM as context. Every context provider is a plugin, which means if you want to reference some source of information that you don't see here, you can request (or build!) a new context provider." --- -As an example, say you are working on solving a new GitHub Issue. You type '@Issue' and select the one you are working on. Continue can now see the issue title and contents. You also know that the issue is related to the files 'readme.md' and 'helloNested.py', so you type '@readme' and '@hello' to find and select them. Now these 3 "Context Items" are displayed inline with the rest of your input. +As an example, say you are working on solving a new GitHub Issue. You type '@Issue' and select the one you are working on. Shadow Code can now see the issue title and contents. You also know that the issue is related to the files 'readme.md' and 'helloNested.py', so you type '@readme' and '@hello' to find and select them. Now these 3 "Context Items" are displayed inline with the rest of your input. ![Context Items](/images/customize/images/context-provider-example-0c96ff77286fa970b23dddfdc1fa986a.png) @@ -232,9 +232,9 @@ context: - provider: os ``` -### Using the Model Context Protocol (MCP) in Continue +### Using the Model Context Protocol (MCP) in Shadow Code -The [Model Context Protocol](https://modelcontextprotocol.io/introduction) is a standard proposed by Anthropic to unify prompts, context, and tool use. Continue supports any MCP server with the MCP context provider. Read their [quickstart](https://modelcontextprotocol.io/quickstart) to learn how to set up a local server and then set up your configuration like this: +The [Model Context Protocol](https://modelcontextprotocol.io/introduction) is a standard proposed by Anthropic to unify prompts, context, and tool use. Shadow Code supports any MCP server with the MCP context provider. Read their [quickstart](https://modelcontextprotocol.io/quickstart) to learn how to set up a local server and then set up your configuration like this: config.yaml @@ -250,6 +250,6 @@ mcpServers: You'll then be able to type "@" and see "MCP" in the context providers dropdown. -### How to Request a New Context Provider in Continue +### How to Request a New Context Provider in Shadow Code -Not seeing what you want? Create an issue [here](https://github.com/continuedev/continue/issues/new?assignees=TyDunn&labels=enhancement&projects=&template=feature-request-%F0%9F%92%AA.md&title=) to request a new Context Provider. +Not seeing what you want? Create an issue here to request a new Context Provider. diff --git a/docs/customize/deep-dives/autocomplete.mdx b/docs/customize/deep-dives/autocomplete.mdx index 87a4ede2450..60c7b8003fc 100644 --- a/docs/customize/deep-dives/autocomplete.mdx +++ b/docs/customize/deep-dives/autocomplete.mdx @@ -1,6 +1,6 @@ --- -title: "Continue Autocomplete Setup and Configuration Guide" -description: "Step-by-step guide to setting up and configuring autocomplete in Continue, including Codestral, Ollama, and IDE settings." +title: "Shadow Code Autocomplete Setup and Configuration Guide" +description: "Step-by-step guide to setting up and configuring autocomplete in Shadow Code, including Codestral, Ollama, and IDE settings." keywords: [autocomplete] sidebarTitle: Autocomplete --- @@ -11,7 +11,7 @@ import { ModelRecommendations } from "/snippets/ModelRecommendations.jsx"; -## How to Set Up Autocomplete in Continue with Codestral (Recommended) +## How to Set Up Autocomplete in Shadow Code with Codestral (Recommended) If you want to have the best autocomplete experience, we recommend using Codestral, which is available through the [Mistral API](https://console.mistral.ai/). To do this, obtain an API key and add it to your config: @@ -53,7 +53,7 @@ If you want to have the best autocomplete experience, we recommend using Codestr "https://api.mistral.ai/v1"` in your `tabAutocompleteModel`. -## How to Set Up Autocomplete in Continue with Ollama (Local Model) +## How to Set Up Autocomplete in Shadow Code with Ollama (Local Model) If you'd like to run your autocomplete model locally, we recommend using Ollama. To do this, first download the latest version of Ollama from [here](https://ollama.ai). Then, run the following command to download our recommended model: @@ -126,9 +126,9 @@ models: Then, in the continue panel, select this model as the default model for autocomplete. -## Autocomplete Configuration Options in Continue +## Autocomplete Configuration Options in Shadow Code -### Customize Autocomplete User Settings in the Continue Extension +### Customize Autocomplete User Settings in the Shadow Code Extension {/* - `Use autocomplete cache`: If on, caches completions */} The following settings can be configured for autocompletion in the IDE extension User Settings Page: @@ -163,23 +163,23 @@ models: The `config.json` configuration format offers configuration options through `tabAutocompleteOptions`. See the [JSON Reference](/reference/json-reference#tabautocompleteoptions) for more details. -## Autocomplete FAQs and Troubleshooting in Continue +## Autocomplete FAQs and Troubleshooting in Shadow Code ### I want better completions, should I use GPT-5? -Perhaps surprisingly, the answer is no. The models that we suggest for autocomplete are trained with a highly specific prompt format, which allows them to respond to requests for completing code (see examples of these prompts [here](https://github.com/continuedev/continue/blob/main/core/autocomplete/templating/AutocompleteTemplate.ts)). Some of the best commercial models like GPT-5 or Claude are not trained with this prompt format, which means that they won't generate useful completions. Luckily, a huge model is not required for great autocomplete. Most of the state-of-the-art autocomplete models are no more than 10b parameters, and increasing beyond this does not significantly improve performance. +Perhaps surprisingly, the answer is no. The models that we suggest for autocomplete are trained with a highly specific prompt format, which allows them to respond to requests for completing code (see examples of these prompts here). Some of the best commercial models like GPT-5 or Claude are not trained with this prompt format, which means that they won't generate useful completions. Luckily, a huge model is not required for great autocomplete. Most of the state-of-the-art autocomplete models are no more than 10b parameters, and increasing beyond this does not significantly improve performance. ### Autocomplete Not Working – How to Fix It Follow these steps to ensure that everything is set up correctly: -1. Make sure you have the "Enable Tab Autocomplete" setting checked (in VS Code, you can toggle by clicking the "Continue" button in the status bar, and in JetBrains by going to Settings -> Tools -> Continue). +1. Make sure you have the "Enable Tab Autocomplete" setting checked (in VS Code, you can toggle by clicking the "Shadow Code" button in the status bar, and in JetBrains by going to Settings -> Tools -> Shadow Code). 2. Make sure you have downloaded Ollama. 3. Run `ollama run qwen2.5-coder:1.5b` to verify that the model is downloaded. 4. Make sure that any other completion providers are disabled (e.g. Copilot), as they may interfere. -5. Check the output of the logs to find any potential errors: cmd/ctrl + shift + P -> "Toggle Developer Tools" -> "Console" tab in VS Code, ~/.continue/logs/core.log in JetBrains. +5. Check the output of the logs to find any potential errors: cmd/ctrl + shift + P -> "Toggle Developer Tools" -> "Console" tab in VS Code, ~/.shadow-code/logs/core.log in JetBrains. 6. Check VS Code settings to make sure that `"editor.inlineSuggest.enabled"` is set to `true` (use cmd/ctrl + , then search for this and check the box) -7. If you are still having issues, please file an issue on [GitHub](https://github.com/continuedev/continue/issues). +7. If you are still having issues, please file an issue on GitHub. ### Why Are My Completions Only Single-Line? @@ -206,20 +206,20 @@ This is a built-in feature of VS Code, but it's just a bit hidden. Follow these This will make multi-line completion (including continue and from VS Code built-in or other plugin snippets) still work, and you will see multi-line completion. However, Tab will only fill in one line at a time. Any unnecessary code can be canceled with Esc. If you need to apply all the code, just press Tab multiple times. -### How to Turn Off Autocomplete in Continue (VS Code and JetBrains) +### How to Turn Off Autocomplete in Shadow Code (VS Code and JetBrains) #### VS Code -Click the "Continue" button in the status panel at the bottom right of the screen. The checkmark will become a "cancel" symbol and you will no longer see completions. You can click again to turn it back on. +Click the "Shadow Code" button in the status panel at the bottom right of the screen. The checkmark will become a "cancel" symbol and you will no longer see completions. You can click again to turn it back on. -Alternatively, open VS Code settings, search for "Continue" and uncheck the box for "Enable Tab Autocomplete". +Alternatively, open VS Code settings, search for "Shadow Code" and uncheck the box for "Enable Tab Autocomplete". You can also use the default shortcut to disable autocomplete directly using a chord: press and hold ctrl/cmd + K (continue holding ctrl/cmd) and press ctrl/cmd + A. This will turn off autocomplete without navigating through settings. #### JetBrains -Open Settings -> Tools -> Continue and uncheck the box for "Enable Tab Autocomplete". +Open Settings -> Tools -> Shadow Code and uncheck the box for "Enable Tab Autocomplete". #### Feedback -If you're turning off autocomplete, we'd love to hear how we can improve! Please file an issue on [GitHub](https://github.com/continuedev/continue/issues). +If you're turning off autocomplete, we'd love to hear how we can improve! Please file an issue on GitHub. diff --git a/docs/customize/deep-dives/configuration.mdx b/docs/customize/deep-dives/configuration.mdx index 8bbde24e119..3ba8c1cbf83 100644 --- a/docs/customize/deep-dives/configuration.mdx +++ b/docs/customize/deep-dives/configuration.mdx @@ -1,11 +1,11 @@ --- -title: "How to Configure Continue" -description: Learn how to access and manage Continue configurations through local YAML files +title: "How to Configure Shadow Code" +description: Learn how to access and manage Shadow Code configurations through local YAML files keywords: [config, settings, customize] sidebarTitle: "Configuration" --- -You can easily access your configuration from the Continue Chat sidebar. Open the sidebar by pressing cmd/ctrl + L (VS Code) or cmd/ctrl + J (JetBrains) and click the Agent selector above the main chat input. Then, you can hover over an agent and click the `gear` icon. +You can easily access your configuration from the Shadow Code Chat sidebar. Open the sidebar by pressing cmd/ctrl + L (VS Code) or cmd/ctrl + J (JetBrains) and click the Agent selector above the main chat input. Then, you can hover over an agent and click the `gear` icon. ![configure](/images/configure-continue.png) @@ -13,14 +13,14 @@ You can easily access your configuration from the Continue Chat sidebar. Open th Local user-level configuration is stored and can be edited in your home directory in `config.yaml`: -- `~/.continue/config.yaml` (MacOS / Linux) +- `~/.shadow-code/config.yaml` (MacOS / Linux) - `%USERPROFILE%\.continue\config.yaml` (Windows) To open this `config.yaml`, you need to open the configs dropdown in the top-right portion of the chat input. On that dropdown beside the "Local Config" option, select the cog icon. It will open the local `config.yaml`. ![local-config-open-steps](/images/local-config-open-steps.png) -When editing this file, you can see the available options suggested as you type, or check the reference below. When you save a config file from the IDE, Continue will automatically refresh to take into account your changes. A config file is automatically created the first time you use Continue, and always automatically generated with default values if it doesn't exist. +When editing this file, you can see the available options suggested as you type, or check the reference below. When you save a config file from the IDE, Shadow Code will automatically refresh to take into account your changes. A config file is automatically created the first time you use Shadow Code, and always automatically generated with default values if it doesn't exist. See the full reference for `config.yaml` [here](/reference). @@ -31,18 +31,18 @@ See the full reference for `config.yaml` [here](/reference). - [`config.json`](/reference) - The original configuration format which is stored in a file at the same location as `config.yaml` -- `.continuerc.json` - Workspace-level configuration +- `.shadow-coderc.json` - Workspace-level configuration - `config.ts` - Advanced configuration (probably unnecessary) - a TypeScript file in your home directory that can be used to programmatically modify (_merged_) the `config.json` schema: - - `~/.continue/config.ts` (MacOS / Linux) + - `~/.shadow-code/config.ts` (MacOS / Linux) - `%USERPROFILE%\.continue\config.ts` (Windows) -### How to Use `.continuerc.json` for Workspace Configuration +### How to Use `.shadow-coderc.json` for Workspace Configuration -The format of `.continuerc.json` is the same as `config.json`, plus one _additional_ property `mergeBehavior`, which can be set to either "merge" or "overwrite". If set to "merge" (the default), `.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`. +The format of `.shadow-coderc.json` is the same as `config.json`, plus one _additional_ property `mergeBehavior`, which can be set to either "merge" or "overwrite". If set to "merge" (the default), `.shadow-coderc.json` will be applied on top of `config.json` (arrays and objects are merged). If set to "overwrite", then every top-level property of `.shadow-coderc.json` will overwrite that property from `config.json`. Example -```json title=".continuerc.json" +```json title=".shadow-coderc.json" { "tabAutocompleteOptions": { "disable": true @@ -53,7 +53,7 @@ Example ### How to Use `config.ts` for Advanced Configuration -`config.yaml` or `config.json` can handle the vast majority of necessary configuration, so we recommend using it whenever possible. However, if you need to programmatically extend Continue configuration, you can use a `config.ts` file, placed at `~/.continue/config.ts` (MacOS / Linux) or `%USERPROFILE%\.continue\config.ts` (Windows). +`config.yaml` or `config.json` can handle the vast majority of necessary configuration, so we recommend using it whenever possible. However, if you need to programmatically extend Shadow Code configuration, you can use a `config.ts` file, placed at `~/.shadow-code/config.ts` (MacOS / Linux) or `%USERPROFILE%\.continue\config.ts` (Windows). `config.ts` must export a `modifyConfig` function, like: diff --git a/docs/customize/deep-dives/custom-providers.mdx b/docs/customize/deep-dives/custom-providers.mdx index 4142ee28c4c..d9f9209404c 100644 --- a/docs/customize/deep-dives/custom-providers.mdx +++ b/docs/customize/deep-dives/custom-providers.mdx @@ -166,7 +166,7 @@ Response ### Model Context Protocol -The [Model Context Protocol](https://modelcontextprotocol.io/introduction) is a standard proposed by Anthropic to unify prompts, context, and tool use. Continue supports any MCP server with the MCP context provider. Read their [quickstart](https://modelcontextprotocol.io/quickstart) to learn how to set up a local server and then set up your configuration like this: +The [Model Context Protocol](https://modelcontextprotocol.io/introduction) is a standard proposed by Anthropic to unify prompts, context, and tool use. Shadow Code supports any MCP server with the MCP context provider. Read their [quickstart](https://modelcontextprotocol.io/quickstart) to learn how to set up a local server and then set up your configuration like this: ```yaml config.yaml mcpServers: diff --git a/docs/customize/deep-dives/development-data.mdx b/docs/customize/deep-dives/development-data.mdx index 35ccb06c23c..d9b43d50a0b 100644 --- a/docs/customize/deep-dives/development-data.mdx +++ b/docs/customize/deep-dives/development-data.mdx @@ -1,11 +1,11 @@ --- -title: "How to Collect and Manage Development Data in Continue" +title: "How to Collect and Manage Development Data in Shadow Code" description: Collecting data on how you build software keywords: [development data, dev data, LLM-aided development] sidebarTitle: "Development Data" --- -When you use Continue, you automatically collect data on how you build software. By default, this development data is saved to `.continue/dev_data` on your local machine. +When you use Shadow Code, you automatically collect data on how you build software. By default, this development data is saved to `.shadow-code/dev_data` on your local machine. You can read more about how development data is generated as a byproduct of LLM-aided development and why we believe that you should start collecting it now: [It’s time to collect data on how you build software](https://blog.continue.dev/its-time-to-collect-data-on-how-you-build-software) @@ -15,4 +15,4 @@ You can also configure custom destinations for your data, including remote HTTP Data destinations should be configured directly in the `data` section of your `config.yaml` file. See more details about adding `data` configuration in the [YAML specification](/reference#data). -When sending development data to your own HTTP endpoint, it will receive an event JSON blob at the given `schema` version. You can view event names, schema versions, and fields [here in the source code](https://github.com/continuedev/continue/tree/main/packages/config-yaml/src/schemas/data). +When sending development data to your own HTTP endpoint, it will receive an event JSON blob at the given `schema` version. You can view event names, schema versions, and fields here in the source code. diff --git a/docs/customize/deep-dives/mcp.mdx b/docs/customize/deep-dives/mcp.mdx index d90cf398fa3..26b14ccd352 100644 --- a/docs/customize/deep-dives/mcp.mdx +++ b/docs/customize/deep-dives/mcp.mdx @@ -1,5 +1,5 @@ --- -title: "How to Set Up Model Context Protocol (MCP) in Continue" +title: "How to Set Up Model Context Protocol (MCP) in Shadow Code" description: MCP use and customization keywords: [tool, use, function calling, claude, automatic] sidebarTitle: "Model Context Protocol (MCP)" @@ -14,7 +14,7 @@ the wider digital world. This standard, created by Anthropic to bring together prompts, context, and tool use, is key for building truly useful AI experiences that can be set up with custom tools. -## How MCP Works in Continue +## How MCP Works in Shadow Code Currently custom tools can be configured using the Model Context Protocol standard to unify prompts, context, and tool use. @@ -29,11 +29,11 @@ For ready-to-use configurations for popular MCP servers, see [Example MCP Server Below is a quick example of setting up a new MCP server for use in your config: -1. Create a folder called `.continue/mcpServers` at the top level of your workspace +1. Create a folder called `.shadow-code/mcpServers` at the top level of your workspace 2. Add a file called `playwright-mcp.yaml` to this folder 3. Write the following contents and save -```yaml title=".continue/mcpServers/playwright-mcp.yaml" +```yaml title=".shadow-code/mcpServers/playwright-mcp.yaml" name: Playwright mcpServer version: 0.0.1 schema: v1 @@ -54,18 +54,18 @@ The result will be a generated file called `hn.txt` in the current working direc ![playwright mcp](/images/mcp-playwright.png) -## How to Set Up Continue Documentation Search with MCP +## How to Set Up Shadow Code Documentation Search with MCP -You can set up an MCP server to search the Continue documentation directly from your config. This is particularly useful for getting help with Continue configuration and features. +You can set up an MCP server to search the Shadow Code documentation directly from your config. This is particularly useful for getting help with Shadow Code configuration and features. -For complete setup instructions, troubleshooting, and usage examples, see the [Continue MCP Reference](/reference/continue-mcp). +For complete setup instructions, troubleshooting, and usage examples, see the [Shadow Code MCP Reference](/reference/continue-mcp). ## Using JSON MCP Format from Claude, Cursor, Cline, etc -If you're coming from another tool that uses JSON MCP format configuration files (like Claude Desktop, Cursor, or Cline), you can copy those JSON config files directly into your `.continue/mcpServers/` directory (note the plural "Servers") and Continue will automatically pick them up. +If you're coming from another tool that uses JSON MCP format configuration files (like Claude Desktop, Cursor, or Cline), you can copy those JSON config files directly into your `.shadow-code/mcpServers/` directory (note the plural "Servers") and Shadow Code will automatically pick them up. -For example, place your JSON MCP config file at `.continue/mcpServers/mcp.json` in your workspace. +For example, place your JSON MCP config file at `.shadow-code/mcpServers/mcp.json` in your workspace. ## How to Configure MCP Servers @@ -87,7 +87,7 @@ mcpServers: ``` -When creating a standalone block file in `.continue/mcpServers/`, remember to include the required metadata fields (`name`, `version`, `schema`) as shown in the Quick Start example above. +When creating a standalone block file in `.shadow-code/mcpServers/`, remember to include the required metadata fields (`name`, `version`, `schema`) as shown in the Quick Start example above. ### How to Configure MCP Server Properties diff --git a/docs/customize/deep-dives/model-capabilities.mdx b/docs/customize/deep-dives/model-capabilities.mdx index fc429c8b613..3049b5912c0 100644 --- a/docs/customize/deep-dives/model-capabilities.mdx +++ b/docs/customize/deep-dives/model-capabilities.mdx @@ -1,15 +1,15 @@ --- -title: "How to Configure Model Capabilities in Continue" +title: "How to Configure Model Capabilities in Shadow Code" description: Understanding and configuring model capabilities for tools and image support keywords: [capabilities, tools, function calling, image input, config] sidebarTitle: "Model Capabilities" --- -Continue needs to know what features your models support to provide the best experience. This guide explains how model capabilities work and how to configure them. +Shadow Code needs to know what features your models support to provide the best experience. This guide explains how model capabilities work and how to configure them. ## What Are Model Capabilities? -Model capabilities tell Continue what features a model supports: +Model capabilities tell Shadow Code what features a model supports: - **`tool_use`** - Whether the model can use tools and functions - **`image_input`** - Whether the model can process images @@ -20,13 +20,13 @@ Without proper capability configuration, you may encounter issues like: - Tools not working at all - Image uploads being disabled -## How Continue Detects Model Capabilities +## How Shadow Code Detects Model Capabilities -Continue uses a two-tier system for determining model capabilities: +Shadow Code uses a two-tier system for determining model capabilities: ### How Automatic Detection Works (Default) -Continue automatically detects capabilities based on your provider and model name. For example: +Shadow Code automatically detects capabilities based on your provider and model name. For example: - **OpenAI**: GPT-4 and GPT-3.5 Turbo models support tools - **Anthropic**: Claude 3.5+ models support both tools and images @@ -37,15 +37,15 @@ This works well for popular models, but may not cover custom deployments or newe For implementation details, see: -- [toolSupport.ts](https://github.com/continuedev/continue/blob/main/core/llm/toolSupport.ts) - Tool capability detection logic +- toolSupport.ts - Tool capability detection logic - [@continuedev/llm-info](https://www.npmjs.com/package/@continuedev/llm-info) - Image support detection ### How to Configure Capabilities Manually -You can add capabilities to models that Continue doesn't automatically detect in your `config.yaml`. +You can add capabilities to models that Shadow Code doesn't automatically detect in your `config.yaml`. - You cannot override autodetection - you can only add capabilities. Continue + You cannot override autodetection - you can only add capabilities. Shadow Code will always use its built-in knowledge about your model in addition to any capabilities you specify. @@ -66,7 +66,7 @@ models: Add capabilities when: 1. **Using custom deployments** - Your API endpoint serves a model with different capabilities than the standard version -2. **Using newer models** - Continue doesn't yet recognize a newly released model +2. **Using newer models** - Shadow Code doesn't yet recognize a newly released model 3. **Experiencing issues** - Autodetection isn't working correctly for your setup 4. **Using proxy services** - Some proxy services modify model capabilities @@ -74,7 +74,7 @@ Add capabilities when: ### How to Add Basic Tool Support -Add tool support for a model that Continue doesn't recognize: +Add tool support for a model that Shadow Code doesn't recognize: ```yaml models: @@ -110,7 +110,7 @@ models: ``` - An empty capabilities array does not disable autodetection. Continue will + An empty capabilities array does not disable autodetection. Shadow Code will still detect and use the model's actual capabilities. To truly limit a model's capabilities, you would need to use a model that doesn't support those features. @@ -159,13 +159,13 @@ For troubleshooting capability-related issues like Agent mode being unavailable 1. **Start with autodetection** - Only override if you experience issues 2. **Test after changes** - Verify tools and images work as expected -3. **Keep Continue updated** - Newer versions improve autodetection +3. **Keep Shadow Code updated** - Newer versions improve autodetection -Remember: Setting capabilities only adds to autodetection. Continue will still use its built-in knowledge about your model in addition to your specified capabilities. +Remember: Setting capabilities only adds to autodetection. Shadow Code will still use its built-in knowledge about your model in addition to your specified capabilities. ## Model Capability Support -This matrix shows which models support tool use and image input capabilities. Continue auto-detects these capabilities, but you can override them if needed. +This matrix shows which models support tool use and image input capabilities. Shadow Code auto-detects these capabilities, but you can override them if needed. ### OpenAI diff --git a/docs/customize/deep-dives/prompts.mdx b/docs/customize/deep-dives/prompts.mdx index 8bb42c791c7..7fb38b32295 100644 --- a/docs/customize/deep-dives/prompts.mdx +++ b/docs/customize/deep-dives/prompts.mdx @@ -1,5 +1,5 @@ --- -title: "How to Create and Manage Prompts in Continue" +title: "How to Create and Manage Prompts in Shadow Code" description: "Prompts are used to kick off tasks for Agent mode, Plan mode, and Chat mode" keywords: [prompts, context, slash command] sidebarTitle: "Prompts" @@ -57,7 +57,7 @@ You're a Supabase Postgres expert in writing database functions. Generate **high ... ``` -You can read the rest of the `Create Supabase functions` prompt [here](http://continue.dev/supabase/create-functions) +You can read the rest of the `Create Supabase functions` prompt here If you are using a local `config.yaml`, you can add it to your config like this: @@ -74,7 +74,7 @@ To use this prompt, you can open Chat / Agent / Edit, type /, select ## Using a prompt with `cn (TUI mode)` -You can run this command to start [cn](../../guides/cli) with the [Create Supabase functions](http://continue.dev/supabase/create-functions) prompt. +You can run this command to start [cn](../../guides/cli) with the Create Supabase functions prompt. ``` cn --prompt supabase/create-functions "I need a function that checks for the health status" @@ -97,7 +97,7 @@ Here is a command that you could run whenever you have a new feature: cn -p --prompt supabase/create-functions "I need a function for the new feature on my current branch similar to my existing database functions" ``` -You can see the entire `Create Supabase functions` prompt [here](http://continue.dev/supabase/create-functions) +You can see the entire `Create Supabase functions` prompt here When you run this workflow, [cn](../../guides/cli) will checkout your current branch, explore the new and existing code, and then draft a function for you. diff --git a/docs/customize/deep-dives/rules.mdx b/docs/customize/deep-dives/rules.mdx index f5f52955dec..c2222988fbc 100644 --- a/docs/customize/deep-dives/rules.mdx +++ b/docs/customize/deep-dives/rules.mdx @@ -1,5 +1,5 @@ --- -title: "How to Create and Manage Rules in Continue" +title: "How to Create and Manage Rules in Shadow Code" description: "Rules are used to provide system message instructions to the model for Agent mode, Chat mode, and Edit mode requests" keywords: [rules, system, prompt, message] sidebarTitle: "Rules" @@ -12,7 +12,7 @@ Rules provide instructions to the model for [Agent mode](../../ide-extensions/ag [apply](../model-roles/apply). -## How Rules Work in Continue +## How Rules Work in Shadow Code You can view the current rules by clicking the pen icon above the main toolbar: @@ -24,11 +24,11 @@ To form the system message, rules are joined with new lines, in the order they a Below is a quick example of setting up a new rule file: -1. Create a folder called `.continue/rules` at the top level of your workspace +1. Create a folder called `.shadow-code/rules` at the top level of your workspace 2. Add a file called `pirates-rule.md` to this folder. 3. Write the following contents to `pirates-rule.md` and save. -```md title=".continue/rules/pirates-rule.md" +```md title=".shadow-code/rules/pirates-rule.md" --- name: Pirate rule --- @@ -51,7 +51,7 @@ Rules can be added locally using the "Add Rules" button. **Automatically create local rules**: When in Agent mode, you can prompt the agent to create a rule for you using the `create_rule_block` tool if enabled. -For example, you can say "Create a rule for this", and a rule will be created for you in `.continue/rules` based on your conversation. +For example, you can say "Create a rule for this", and a rule will be created for you in `.shadow-code/rules` based on your conversation. ### How to Configure Rule Properties and Syntax @@ -80,10 +80,10 @@ Rules can be simple text, written in YAML configuration files, or as Markdown (` name: Documentation Standards globs: docs/**/*.{md,mdx} alwaysApply: false -description: Standards for writing and maintaining Continue Docs +description: Standards for writing and maintaining Shadow Code Docs --- -# Continue Docs Standards +# Shadow Code Docs Standards - Follow Mintlify documentation standards - Include YAML frontmatter with title, description, and keywords @@ -125,13 +125,13 @@ rules: ### How to Set Up Project-Specific Rules -You can create project-specific rules by adding a `.continue/rules` folder to the root of your project and adding new rule files. +You can create project-specific rules by adding a `.shadow-code/rules` folder to the root of your project and adding new rule files. Rules files are loaded in lexicographical order, so you can prefix them with numbers to control the order in which they are applied. For example: `01-general.md`, `02-frontend.md`, `03-backend.md`. ### Example: How to Create TypeScript-Specific Rules -```md title=".continue/rules/typescript.md" +```md title=".shadow-code/rules/typescript.md" --- name: TypeScript Best Practices globs: ["**/*.ts", "**/*.tsx"] @@ -154,13 +154,13 @@ globs: ["**/*.ts", "**/*.tsx"] **Problem**: Your rules exist but don't show up in the rules toolbar. **Check These**: -1. **File location**: Ensure rules are in `.continue/rules/` (not `.continue/rule/`) +1. **File location**: Ensure rules are in `.shadow-code/rules/` (not `.shadow-code/rule/`) 2. **File format**: Rules should be `.md` files with proper YAML frontmatter ### How to Customize Chat System Message -Continue includes a simple default system message for [Agent mode](../../ide-extensions/agent/quick-start) and [Chat](../../ide-extensions/chat/quick-start) requests, to help the model provide reliable codeblock formats in its output. +Shadow Code includes a simple default system message for [Agent mode](../../ide-extensions/agent/quick-start) and [Chat](../../ide-extensions/chat/quick-start) requests, to help the model provide reliable codeblock formats in its output. -This can be viewed in the rules section of the toolbar (see above), or in the source code [here](https://github.com/continuedev/continue/blob/main/core/llm/constructMessages.ts#L4). +This can be viewed in the rules section of the toolbar (see above), or in the source code here. Advanced users can override this system message for a specific model if needed by using `chatOptions.baseSystemMessage`. See the [`config.yaml` reference](/reference#models). diff --git a/docs/customize/mcp-tools.mdx b/docs/customize/mcp-tools.mdx index 97b8a6ea122..41577ddd041 100644 --- a/docs/customize/mcp-tools.mdx +++ b/docs/customize/mcp-tools.mdx @@ -1,9 +1,9 @@ --- title: "MCP servers" -description: "Learn how to use Model Context Protocol (MCP) blocks in Continue to integrate external tools, connect databases, and extend your development environment." +description: "Learn how to use Model Context Protocol (MCP) blocks in Shadow Code to integrate external tools, connect databases, and extend your development environment." --- -Model Context Protocol (MCP) servers let Continue connect to external tools, systems, and databases by running MCP servers. +Model Context Protocol (MCP) servers let Shadow Code connect to external tools, systems, and databases by running MCP servers. These servers make it possible to: diff --git a/docs/customize/model-providers/more/SambaNova.mdx b/docs/customize/model-providers/more/SambaNova.mdx index 3cb4dd3e0a5..97b05a0cf3c 100644 --- a/docs/customize/model-providers/more/SambaNova.mdx +++ b/docs/customize/model-providers/more/SambaNova.mdx @@ -1,6 +1,6 @@ --- title: "SambaNova" -description: "Configure SambaCloud with Continue to access their high-performance platform for running large AI models, including Llama 4 Scout with world record open source model performance" +description: "Configure SambaCloud with Shadow Code to access their high-performance platform for running large AI models, including Llama 4 Scout with world record open source model performance" --- The SambaNova Cloud is a cloud platform for running large AI models with the world record open source models performance. You can follow the instructions in [this blog post](https://sambanova.ai/blog/accelerating-coding-with-sambanova-cloud?ref=blog.continue.dev) to configure your setup. @@ -35,4 +35,4 @@ The SambaNova Cloud is a cloud platform for running large AI models with the wor -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/SambaNova.ts) +View the source diff --git a/docs/customize/model-providers/more/asksage.mdx b/docs/customize/model-providers/more/asksage.mdx index 2abfccd259c..478a66fdfad 100644 --- a/docs/customize/model-providers/more/asksage.mdx +++ b/docs/customize/model-providers/more/asksage.mdx @@ -23,7 +23,7 @@ Ask Sage provides secure, government-compliant access to LLMs. This guide explai ## 2. Configuration -Add your Ask Sage model to your Continue configuration file, which is located at `~/.continue/config.yaml`. If it does not exist, create it. +Add your Ask Sage model to your Shadow Code configuration file, which is located at `~/.shadow-code/config.yaml`. If it does not exist, create it. @@ -107,7 +107,7 @@ Replace `/path/to/dod/certificates` with your actual CA bundle file path. ## 4. Final Steps - Save your configuration file. -- Restart Continue to apply changes. +- Restart Shadow Code to apply changes. Your Ask Sage model will now be available in Continue. @@ -125,15 +125,15 @@ Supported features: --- -## Setting up Continue CLI +## Setting up Shadow Code CLI -1. Install Continue CLI -Follow the steps from the [official Continue docs](https://docs.continue.dev/cli/quickstart). +1. Install Shadow Code CLI +Follow the steps from the official Shadow Code docs. 2. Setting up a custom config for Ask Sage Follow the steps in step 2 of the Overview above 👆. -3. How to use Continue CLI -Use [Continue's official guide](https://docs.continue.dev/guides/cli). +3. How to use Shadow Code CLI +Use Shadow Code's official guide. - Once the editor appears in the terminal, type "/config", and select the config created in step 2. --- diff --git a/docs/customize/model-providers/more/cerebras.mdx b/docs/customize/model-providers/more/cerebras.mdx index 32d5598cd38..7c23380bb1f 100644 --- a/docs/customize/model-providers/more/cerebras.mdx +++ b/docs/customize/model-providers/more/cerebras.mdx @@ -1,13 +1,13 @@ --- title: "Cerebras" -description: "Configure Cerebras Inference with Continue for fast model inference using specialized silicon, including setup instructions for Llama 3.1 70B model" +description: "Configure Cerebras Inference with Shadow Code for fast model inference using specialized silicon, including setup instructions for Llama 3.1 70B model" --- Cerebras Inference uses specialized silicon to provides fast inference. 1. Create an account in the portal [here](https://cloud.cerebras.ai/). 2. Create and copy the API key for use in Continue. -3. Update your Continue config file: +3. Update your Shadow Code config file: diff --git a/docs/customize/model-providers/more/clawrouter.mdx b/docs/customize/model-providers/more/clawrouter.mdx index 7c91cf0ae5e..feba7efdb7a 100644 --- a/docs/customize/model-providers/more/clawrouter.mdx +++ b/docs/customize/model-providers/more/clawrouter.mdx @@ -1,5 +1,5 @@ --- -title: "How to Configure ClawRouter with Continue" +title: "How to Configure ClawRouter with Shadow Code" sidebarTitle: "ClawRouter" --- @@ -129,7 +129,7 @@ ClawRouter supports function calling and tool use through its underlying model p ## Switching Between Routing Profiles -Add multiple ClawRouter profiles to your config and switch via Continue's model picker: +Add multiple ClawRouter profiles to your config and switch via Shadow Code's model picker: @@ -194,10 +194,10 @@ Add multiple ClawRouter profiles to your config and switch via Continue's model -Use the **model picker dropdown** in Continue's chat panel to switch between profiles. Each profile routes to different model tiers based on cost vs. quality trade-offs. +Use the **model picker dropdown** in Shadow Code's chat panel to switch between profiles. Each profile routes to different model tiers based on cost vs. quality trade-offs. - **Quick switch via CLI:** In the Continue chat, type `/model` followed by the profile name (e.g., `/model ClawRouter Eco`). + **Quick switch via CLI:** In the Shadow Code chat, type `/model` followed by the profile name (e.g., `/model ClawRouter Eco`). ## Custom API Base @@ -232,7 +232,7 @@ If you're running ClawRouter on a different port or host: ## Using Multiple Roles -You can configure ClawRouter for different Continue roles: +You can configure ClawRouter for different Shadow Code roles: @@ -366,7 +366,7 @@ ClawRouter handles common LLM errors automatically at the router level: ### Automatic Error Recovery -| Error | Continue's Default | ClawRouter's Handling | +| Error | Shadow Code's Default | ClawRouter's Handling | |-------|-------------------|----------------------| | **429 Rate Limit** | Retry same provider with backoff | Route to different provider entirely | | **402 Payment Required** | Fail immediately | x402 auto-payment from wallet | diff --git a/docs/customize/model-providers/more/cloudflare.mdx b/docs/customize/model-providers/more/cloudflare.mdx index 7887ada8e7c..d790918e683 100644 --- a/docs/customize/model-providers/more/cloudflare.mdx +++ b/docs/customize/model-providers/more/cloudflare.mdx @@ -1,6 +1,6 @@ --- title: "Cloudflare" -description: "Configure Cloudflare Workers AI with Continue to access various models for chat and autocomplete, including Llama 3 8B and DeepSeek Coder through Cloudflare's serverless AI platform" +description: "Configure Cloudflare Workers AI with Shadow Code to access various models for chat and autocomplete, including Llama 3 8B and DeepSeek Coder through Cloudflare's serverless AI platform" --- Cloudflare Workers AI can be used for both chat and tab autocompletion in Continue. Here is an example of Cloudflare Workers AI configuration: @@ -88,4 +88,4 @@ Visit the [Cloudflare dashboard](https://dash.cloudflare.com/) to [create an API Review [available models](https://developers.cloudflare.com/workers-ai/models/) on Workers AI -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Cloudflare.ts) +View the source diff --git a/docs/customize/model-providers/more/cohere.mdx b/docs/customize/model-providers/more/cohere.mdx index a46a0e25116..7c8f8c7819f 100644 --- a/docs/customize/model-providers/more/cohere.mdx +++ b/docs/customize/model-providers/more/cohere.mdx @@ -1,6 +1,6 @@ --- title: "Cohere" -description: "Configure Cohere's AI models with Continue, including setup for Command A for chat, embed-v4.0 for embeddings, and rerank-v3.5 for reranking capabilities" +description: "Configure Cohere's AI models with Shadow Code, including setup for Command A for chat, embed-v4.0 for embeddings, and rerank-v3.5 for reranking capabilities" --- Before using Cohere, visit the [Cohere dashboard](https://dashboard.cohere.com/api-keys) to create an API key. diff --git a/docs/customize/model-providers/more/deepinfra.mdx b/docs/customize/model-providers/more/deepinfra.mdx index 5d81c69ee36..e5bec04dd72 100644 --- a/docs/customize/model-providers/more/deepinfra.mdx +++ b/docs/customize/model-providers/more/deepinfra.mdx @@ -1,10 +1,10 @@ --- title: "DeepInfra" -description: "Configure DeepInfra with Continue to access low-cost inference for open-source models like Mixtral-8x7B-Instruct, including API setup instructions" +description: "Configure DeepInfra with Shadow Code to access low-cost inference for open-source models like Mixtral-8x7B-Instruct, including API setup instructions" --- - **Discover Deep Infra models [here](https://continue.dev/deepinfra)** + **Discover Deep Infra models here** @@ -44,5 +44,5 @@ description: "Configure DeepInfra with Continue to access low-cost inference for - **Check out a more advanced configuration [here](https://continue.dev/deepinfra/qwen-qwen2.5-coder-32b-instruct?view=config)** + **Check out a more advanced configuration here** \ No newline at end of file diff --git a/docs/customize/model-providers/more/flowise.mdx b/docs/customize/model-providers/more/flowise.mdx index ca05985709a..1913cd693f1 100644 --- a/docs/customize/model-providers/more/flowise.mdx +++ b/docs/customize/model-providers/more/flowise.mdx @@ -1,9 +1,9 @@ --- title: "Flowise" -description: "Configure Flowise with Continue to integrate with this low-code/no-code drag & drop tool for building and visualizing LLM applications" +description: "Configure Flowise with Shadow Code to integrate with this low-code/no-code drag & drop tool for building and visualizing LLM applications" --- -[Flowise](https://flowiseai.com/) is a low-code/no-code drag & drop tool with the aim to make it easy for people to visualize and build LLM apps. Continue can then be configured to use the `Flowise` LLM class, like the example here: +[Flowise](https://flowiseai.com/) is a low-code/no-code drag & drop tool with the aim to make it easy for people to visualize and build LLM apps. Shadow Code can then be configured to use the `Flowise` LLM class, like the example here: @@ -35,4 +35,4 @@ description: "Configure Flowise with Continue to integrate with this low-code/no -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Flowise.ts) +View the source diff --git a/docs/customize/model-providers/more/function-network.mdx b/docs/customize/model-providers/more/function-network.mdx index f35aca0aefc..6e2f092c529 100644 --- a/docs/customize/model-providers/more/function-network.mdx +++ b/docs/customize/model-providers/more/function-network.mdx @@ -1,6 +1,6 @@ --- title: "Function Network" -description: "Configure Function Network with Continue to access private and affordable AI models, including Llama 3.1, Qwen2.5-Coder, and various embedding models for a user-owned AI experience" +description: "Configure Function Network with Shadow Code to access private and affordable AI models, including Llama 3.1, Qwen2.5-Coder, and various embedding models for a user-owned AI experience" --- > Private, Affordable User-Owned AI diff --git a/docs/customize/model-providers/more/groq.mdx b/docs/customize/model-providers/more/groq.mdx index 0b2a4dd7d14..c5dd7f3dd27 100644 --- a/docs/customize/model-providers/more/groq.mdx +++ b/docs/customize/model-providers/more/groq.mdx @@ -1,5 +1,5 @@ --- -title: "How to Configure Groq with Continue" +title: "How to Configure Groq with Shadow Code" sidebarTitle: "Groq" --- diff --git a/docs/customize/model-providers/more/ipex_llm.mdx b/docs/customize/model-providers/more/ipex_llm.mdx index 39478c49d5e..1aff8d198be 100644 --- a/docs/customize/model-providers/more/ipex_llm.mdx +++ b/docs/customize/model-providers/more/ipex_llm.mdx @@ -1,6 +1,6 @@ --- title: "Intel Extension for PyTorch" -description: "Configure Intel Extension for PyTorch (IPEX-LLM) with Continue to run language models with very low latency on Intel CPUs and GPUs, leveraging accelerated Ollama backend" +description: "Configure Intel Extension for PyTorch (IPEX-LLM) with Shadow Code to run language models with very low latency on Intel CPUs and GPUs, leveraging accelerated Ollama backend" --- @@ -9,7 +9,7 @@ description: "Configure Intel Extension for PyTorch (IPEX-LLM) with Continue to discrete GPU such as Arc A-Series, Flex and Max) with very low latency. -IPEX-LLM supports accelerated Ollama backend to be hosted on Intel GPU. Refer to [this guide](https://ipex-llm.readthedocs.io/en/latest/doc/LLM/Quickstart/ollama_quickstart.html) from IPEX-LLM official documentation about how to install and run Ollama serve accelerated by IPEX-LLM on Intel GPU. You can then configure Continue to use the IPEX-LLM accelerated `"ollama"` provider as follows: +IPEX-LLM supports accelerated Ollama backend to be hosted on Intel GPU. Refer to [this guide](https://ipex-llm.readthedocs.io/en/latest/doc/LLM/Quickstart/ollama_quickstart.html) from IPEX-LLM official documentation about how to install and run Ollama serve accelerated by IPEX-LLM on Intel GPU. You can then configure Shadow Code to use the IPEX-LLM accelerated `"ollama"` provider as follows: @@ -39,7 +39,7 @@ IPEX-LLM supports accelerated Ollama backend to be hosted on Intel GPU. Refer to -If you would like to reach the Ollama service from another machine, make sure you set or export the environment variable `OLLAMA_HOST=0.0.0.0` before executing the command `ollama serve`. Then, in the Continue configuration, set `'apiBase'` to correspond with the IP address / port of the remote machine. That is, Continue can be configured to be: +If you would like to reach the Ollama service from another machine, make sure you set or export the environment variable `OLLAMA_HOST=0.0.0.0` before executing the command `ollama serve`. Then, in the Shadow Code configuration, set `'apiBase'` to correspond with the IP address / port of the remote machine. That is, Shadow Code can be configured to be: @@ -73,7 +73,7 @@ If you would like to reach the Ollama service from another machine, make sure yo If you would like to preload the model before your first conversation with - that model in Continue, you could refer to + that model in Shadow Code, you could refer to [here](https://ipex-llm.readthedocs.io/en/latest/doc/LLM/Quickstart/continue_quickstart.html#pull-and-prepare-the-model) for more information. diff --git a/docs/customize/model-providers/more/kindo.mdx b/docs/customize/model-providers/more/kindo.mdx index 42d5f68962e..d9b2e95ab22 100644 --- a/docs/customize/model-providers/more/kindo.mdx +++ b/docs/customize/model-providers/more/kindo.mdx @@ -1,6 +1,6 @@ --- title: "Kindo" -description: "Configure Kindo with Continue to centralize control over your organization's AI operations with support for commercial and open-source models while ensuring data protection and policy compliance" +description: "Configure Kindo with Shadow Code to centralize control over your organization's AI operations with support for commercial and open-source models while ensuring data protection and policy compliance" --- Kindo offers centralized control over your organization's AI operations, ensuring data protection and compliance with internal policies while supporting various commercial and open-source models. To get started, sign up [here](https://app.kindo.ai/), create an API key in [Settings > API > API Keys](https://app.kindo.ai/settings/api), and choose a model from the list of supported models in the "Available Models" tab or copy and paste the config in [Plugins > Your Configuration](https://app.kindo.ai/plugins). diff --git a/docs/customize/model-providers/more/lemonade.mdx b/docs/customize/model-providers/more/lemonade.mdx index 62b59cf0d8e..3cd511dc854 100644 --- a/docs/customize/model-providers/more/lemonade.mdx +++ b/docs/customize/model-providers/more/lemonade.mdx @@ -1,15 +1,15 @@ --- title: "Lemonade Server" -description: "Configure Lemonade Server with Continue for refreshingly fast local LLM inference on GPUs and NPUs" +description: "Configure Lemonade Server with Shadow Code for refreshingly fast local LLM inference on GPUs and NPUs" --- - Get started with [Lemonade Server](https://lemonade-server.ai/) - Refreshingly fast LLMs on GPUs and NPUs with seamless Continue integration + Get started with [Lemonade Server](https://lemonade-server.ai/) - Refreshingly fast LLMs on GPUs and NPUs with seamless Shadow Code integration ## Overview -Lemonade Server provides optimized local LLM inference with support for GPU and NPU hardware acceleration. It offers an OpenAI-compatible API that seamlessly integrates with Continue and other open-source platforms. +Lemonade Server provides optimized local LLM inference with support for GPU and NPU hardware acceleration. It offers an OpenAI-compatible API that seamlessly integrates with Shadow Code and other open-source platforms. ## Installation @@ -17,14 +17,14 @@ Download and install Lemonade Server from [lemonade-server.ai](https://lemonade- ## Configuration -Lemonade Server is available directly in the Continue UI as a provider. You can select it from the model provider dropdown without manual configuration. +Lemonade Server is available directly in the Shadow Code UI as a provider. You can select it from the model provider dropdown without manual configuration. -### Option 1: Using the Continue UI (Recommended) +### Option 1: Using the Shadow Code UI (Recommended) -1. Click on the model selector dropdown in Continue +1. Click on the model selector dropdown in Shadow Code 2. Select "Add Model" 3. Choose "Lemonade Server" from the provider list -4. Continue will automatically configure the connection +4. Shadow Code will automatically configure the connection ### Option 2: Manual Configuration @@ -64,7 +64,7 @@ If you need custom settings, you can manually configure Lemonade: 1. **Install Lemonade Server**: Download from [lemonade-server.ai](https://lemonade-server.ai/) 2. **Start the server**: Launch Lemonade Server (runs on `http://localhost:8000/api/v1/` by default) -3. **Add to Continue**: Select Lemonade Server from the model provider dropdown in Continue +3. **Add to Shadow Code**: Select Lemonade Server from the model provider dropdown in Shadow Code 4. **Load a model**: Choose your preferred model through the interface ## Hardware Support @@ -79,7 +79,7 @@ Lemonade Server automatically detects and optimizes for available hardware: - OpenAI-compatible API for seamless integration - Support for popular model formats - Automatic hardware detection and optimization -- Integration with Continue, Open WebUI, Gaia, and AnythingLLM +- Integration with Shadow Code, Open WebUI, Gaia, and AnythingLLM - Active open-source community -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Lemonade.ts) \ No newline at end of file +View the source \ No newline at end of file diff --git a/docs/customize/model-providers/more/llamafile.mdx b/docs/customize/model-providers/more/llamafile.mdx index 736cddc8e8a..b949b95fa1c 100644 --- a/docs/customize/model-providers/more/llamafile.mdx +++ b/docs/customize/model-providers/more/llamafile.mdx @@ -1,6 +1,6 @@ --- title: "Llamafile" -description: "Configure Llamafile with Continue to use self-contained binary files that can run open-source language models like Mistral without additional setup" +description: "Configure Llamafile with Shadow Code to use self-contained binary files that can run open-source language models like Mistral without additional setup" --- A [llamafile](https://github.com/Mozilla-Ocho/llamafile#readme) is a self-contained binary that can run an open-source LLM. You can configure this provider in your config.json as follows: @@ -33,4 +33,4 @@ A [llamafile](https://github.com/Mozilla-Ocho/llamafile#readme) is a self-contai -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Llamafile.ts) +View the source diff --git a/docs/customize/model-providers/more/mimo.mdx b/docs/customize/model-providers/more/mimo.mdx index 1db934da93c..313735a40a1 100644 --- a/docs/customize/model-providers/more/mimo.mdx +++ b/docs/customize/model-providers/more/mimo.mdx @@ -1,40 +1,40 @@ ---- -title: "How to Configure Xiaomi Mimo with Continue" -sidebarTitle: "Xiaomi Mimo" ---- - - - Get your API key from the [Xiaomi Mimo Platform](https://platform.xiaomimimo.com/) - - -## Configuration - - - - ```yaml title="config.yaml" - name: My Config - version: 0.0.1 - schema: v1 - - models: - - name: - provider: mimo - model: mimo-v2-flash - apiKey: - ``` - - - ```json title="config.json" - { - "models": [ - { - "title": "", - "provider": "mimo", - "model": "mimo-v2-flash", - "apiKey": "" - } - ] - } - ``` - - +--- +title: "How to Configure Xiaomi Mimo with Shadow Code" +sidebarTitle: "Xiaomi Mimo" +--- + + + Get your API key from the [Xiaomi Mimo Platform](https://platform.xiaomimimo.com/) + + +## Configuration + + + + ```yaml title="config.yaml" + name: My Config + version: 0.0.1 + schema: v1 + + models: + - name: + provider: mimo + model: mimo-v2-flash + apiKey: + ``` + + + ```json title="config.json" + { + "models": [ + { + "title": "", + "provider": "mimo", + "model": "mimo-v2-flash", + "apiKey": "" + } + ] + } + ``` + + diff --git a/docs/customize/model-providers/more/minimax.mdx b/docs/customize/model-providers/more/minimax.mdx index e0a56e2cfbc..57c786493f2 100644 --- a/docs/customize/model-providers/more/minimax.mdx +++ b/docs/customize/model-providers/more/minimax.mdx @@ -1,5 +1,5 @@ --- -title: "How to Configure MiniMax with Continue" +title: "How to Configure MiniMax with Shadow Code" sidebarTitle: "MiniMax" --- diff --git a/docs/customize/model-providers/more/mistral.mdx b/docs/customize/model-providers/more/mistral.mdx index bee2ec8d090..248946c12a4 100644 --- a/docs/customize/model-providers/more/mistral.mdx +++ b/docs/customize/model-providers/more/mistral.mdx @@ -4,7 +4,7 @@ slug: ../mistral --- - **Discover Mistral models [here](https://continue.dev/mistral)** + **Discover Mistral models here** @@ -44,7 +44,7 @@ slug: ../mistral - **Check out a more advanced configuration [here](https://continue.dev/mistral/codestral?view=config)** + **Check out a more advanced configuration here** diff --git a/docs/customize/model-providers/more/moonshot.mdx b/docs/customize/model-providers/more/moonshot.mdx index a7df91854c4..a2e8fc04b78 100644 --- a/docs/customize/model-providers/more/moonshot.mdx +++ b/docs/customize/model-providers/more/moonshot.mdx @@ -1,6 +1,6 @@ --- title: "Moonshot AI" -description: "Configure Moonshot AI's language models with Continue, including Kimi K2, Kimi K2.5, and Moonshot v1 models with competitive pricing" +description: "Configure Moonshot AI's language models with Shadow Code, including Kimi K2, Kimi K2.5, and Moonshot v1 models with competitive pricing" --- [Moonshot AI](https://platform.moonshot.cn/) provides high-quality large language model services with competitive pricing and excellent performance, including the Kimi series of models. diff --git a/docs/customize/model-providers/more/morph.mdx b/docs/customize/model-providers/more/morph.mdx index 8fc12f7b437..7397f0ebd80 100644 --- a/docs/customize/model-providers/more/morph.mdx +++ b/docs/customize/model-providers/more/morph.mdx @@ -1,6 +1,6 @@ --- title: "Morph" -description: "Configure Morph with Continue to access their optimized models for code application, embeddings, and reranking, designed for fast and accurate integration of AI-generated code changes" +description: "Configure Morph with Shadow Code to access their optimized models for code application, embeddings, and reranking, designed for fast and accurate integration of AI-generated code changes" --- Morph provides a fast apply model that helps you quickly and accurately apply code changes from chat suggestions to your files. It's optimized for speed and precision when integrating generated code into your existing codebase. You can sign up for Morph's generous free tier [here](https://morphllm.com/dashboard). Then, update your configuration file as follows: diff --git a/docs/customize/model-providers/more/msty.mdx b/docs/customize/model-providers/more/msty.mdx index 6e15cc030bd..c3f99b2c88a 100644 --- a/docs/customize/model-providers/more/msty.mdx +++ b/docs/customize/model-providers/more/msty.mdx @@ -1,9 +1,9 @@ --- title: "Msty" -description: "Configure Msty with Continue to easily run both online and local open-source models like Llama-2 and DeepSeek Coder through their user-friendly application for Windows, Mac, and Linux" +description: "Configure Msty with Shadow Code to easily run both online and local open-source models like Llama-2 and DeepSeek Coder through their user-friendly application for Windows, Mac, and Linux" --- -[Msty](https://msty.app/) is an application for Windows, Mac, and Linux that makes it really easy to run online as well as local open-source models, including Llama-2, DeepSeek Coder, etc. No need to fidget with your terminal, run a command, or anything. Just download the app from the website, click a button, and you are up and running. Continue can then be configured to use the `Msty` LLM class: +[Msty](https://msty.app/) is an application for Windows, Mac, and Linux that makes it really easy to run online as well as local open-source models, including Llama-2, DeepSeek Coder, etc. No need to fidget with your terminal, run a command, or anything. Just download the app from the website, click a button, and you are up and running. Shadow Code can then be configured to use the `Msty` LLM class: @@ -84,4 +84,4 @@ If you need to send custom headers for authentication, you may use the `requestO -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Msty.ts) +View the source diff --git a/docs/customize/model-providers/more/ncompass.mdx b/docs/customize/model-providers/more/ncompass.mdx index e1e0a03a8f1..3718a8bdfe1 100644 --- a/docs/customize/model-providers/more/ncompass.mdx +++ b/docs/customize/model-providers/more/ncompass.mdx @@ -1,9 +1,9 @@ --- title: "NCompass" -description: "Configure NCompass Technologies with Continue to access their fast inference engine for open-source models like Google's Gemma 3 Coder" +description: "Configure NCompass Technologies with Shadow Code to access their fast inference engine for open-source models like Google's Gemma 3 Coder" --- -The nCompass Technologies API exposes an extremely fast inference engine for open-source language models. You can sign up [here](https://app.ncompass.tech/api-settings), copy your API key on the initial welcome screen, and then hit the play button on any model from the [nCompass Models list](https://ncompass.tech/models). Change `~/.continue/config.json` to look like this: +The nCompass Technologies API exposes an extremely fast inference engine for open-source language models. You can sign up [here](https://app.ncompass.tech/api-settings), copy your API key on the initial welcome screen, and then hit the play button on any model from the [nCompass Models list](https://ncompass.tech/models). Change `~/.shadow-code/config.json` to look like this: @@ -31,4 +31,4 @@ The nCompass Technologies API exposes an extremely fast inference engine for ope -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/NCompass.ts) +View the source diff --git a/docs/customize/model-providers/more/nebius.mdx b/docs/customize/model-providers/more/nebius.mdx index a678c551246..214b0ca2b2f 100644 --- a/docs/customize/model-providers/more/nebius.mdx +++ b/docs/customize/model-providers/more/nebius.mdx @@ -1,6 +1,6 @@ --- title: "Nebius" -description: "Configure Nebius AI Studio with Continue to access their language model offerings, including DeepSeek R1 for chat and BAAI embeddings models" +description: "Configure Nebius AI Studio with Shadow Code to access their language model offerings, including DeepSeek R1 for chat and BAAI embeddings models" --- You can get an API key from the [Nebius AI Studio API keys page](https://studio.nebius.ai/settings/api-keys) diff --git a/docs/customize/model-providers/more/novita.mdx b/docs/customize/model-providers/more/novita.mdx index c661fc78611..40795250fd7 100644 --- a/docs/customize/model-providers/more/novita.mdx +++ b/docs/customize/model-providers/more/novita.mdx @@ -1,9 +1,9 @@ --- title: "Novita" -description: "Configure Novita AI with Continue to access their affordable and reliable inference platform for language models like Llama 3.1, offering scalable LLM API services" +description: "Configure Novita AI with Shadow Code to access their affordable and reliable inference platform for language models like Llama 3.1, offering scalable LLM API services" --- -[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) today!. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), copy your API key on the [Key Management](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), and then hit the play button on any model from the [Novita AI Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link). Change `~/.continue/config.json` to look like this: +[Novita AI](https://novita.ai?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) offers an affordable, reliable, and simple inference platform with scalable [LLM API](https://novita.ai/docs/model-api/reference/introduction.html), empowering developers to build AI applications. Try the [Novita AI Llama 3 API Demo](https://novita.ai/model-api/product/llm-api/playground/meta-llama-llama-3.1-70b-instruct?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link) today!. You can sign up [here](https://novita.ai/user/login?&redirect=/&utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), copy your API key on the [Key Management](https://novita.ai/settings/key-management?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link), and then hit the play button on any model from the [Novita AI Models list](https://novita.ai/llm-api?utm_source=github_continuedev&utm_medium=github_readme&utm_campaign=github_link). Change `~/.shadow-code/config.json` to look like this: @@ -35,4 +35,4 @@ description: "Configure Novita AI with Continue to access their affordable and r -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Novita.ts) +View the source diff --git a/docs/customize/model-providers/more/openvino_model_server.mdx b/docs/customize/model-providers/more/openvino_model_server.mdx index 1ff6f3bdf7f..e3ac32213d0 100644 --- a/docs/customize/model-providers/more/openvino_model_server.mdx +++ b/docs/customize/model-providers/more/openvino_model_server.mdx @@ -1,6 +1,6 @@ --- title: "OpenVINO Model Server" -description: "Configure OpenVINO Model Server with Continue to use Intel-optimized models for CPU, iGPU, GPU and NPU via the OpenAI-compatible API, supporting code completion with models like CodeLlama and Qwen" +description: "Configure OpenVINO Model Server with Shadow Code to use Intel-optimized models for CPU, iGPU, GPU and NPU via the OpenAI-compatible API, supporting code completion with models like CodeLlama and Qwen" --- diff --git a/docs/customize/model-providers/more/ovhcloud.mdx b/docs/customize/model-providers/more/ovhcloud.mdx index 61c6c24857e..8bf9933954b 100644 --- a/docs/customize/model-providers/more/ovhcloud.mdx +++ b/docs/customize/model-providers/more/ovhcloud.mdx @@ -1,6 +1,6 @@ --- title: "OVHcloud" -description: "Configure OVHcloud AI Endpoints with Continue to access their GDPR-compliant serverless inference API for models like Qwen, Llama, Mistral, and Deepseek, with strong security and data privacy features" +description: "Configure OVHcloud AI Endpoints with Shadow Code to access their GDPR-compliant serverless inference API for models like Qwen, Llama, Mistral, and Deepseek, with strong security and data privacy features" --- OVHcloud AI Endpoints is a serverless inference API that provides access to a curated selection of models (e.g., Llama, Mistral, Qwen, Deepseek). It is designed with security and data privacy in mind and is compliant with GDPR. diff --git a/docs/customize/model-providers/more/relace.mdx b/docs/customize/model-providers/more/relace.mdx index 3d46969f927..54d5ad7c407 100644 --- a/docs/customize/model-providers/more/relace.mdx +++ b/docs/customize/model-providers/more/relace.mdx @@ -1,6 +1,6 @@ --- title: "Relace" -description: "Configure Relace with Continue to access their Fast Apply model, which helps you quickly and reliably apply chat suggestions to your codebase" +description: "Configure Relace with Shadow Code to access their Fast Apply model, which helps you quickly and reliably apply chat suggestions to your codebase" --- Relace provides a fast apply model through their API that helps you reliably and almost instantly apply chat suggestions to your codebase. You can sign up and obtain an API key [here](https://app.relace.ai/settings/api-keys). Then, change your configuration file to look like this: @@ -35,4 +35,4 @@ Relace provides a fast apply model through their API that helps you reliably and -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Relace.ts) +View the source diff --git a/docs/customize/model-providers/more/replicatellm.mdx b/docs/customize/model-providers/more/replicatellm.mdx index 51c5385f667..a497ed71d12 100644 --- a/docs/customize/model-providers/more/replicatellm.mdx +++ b/docs/customize/model-providers/more/replicatellm.mdx @@ -1,9 +1,9 @@ --- title: "Replicate" -description: "Configure Replicate with Continue to access newly released language models or deploy your own through their platform, with support for various models including CodeLLama" +description: "Configure Replicate with Shadow Code to access newly released language models or deploy your own through their platform, with support for various models including CodeLLama" --- -Replicate is a great option for newly released language models or models that you've deployed through their platform. Sign up for an account [here](https://replicate.ai/), copy your API key, and then select any model from the [Replicate Streaming List](https://replicate.com/collections/streaming-language-models). Change `~/.continue/config.json` to look like this: +Replicate is a great option for newly released language models or models that you've deployed through their platform. Sign up for an account [here](https://replicate.ai/), copy your API key, and then select any model from the [Replicate Streaming List](https://replicate.com/collections/streaming-language-models). Change `~/.shadow-code/config.json` to look like this: @@ -37,4 +37,4 @@ Replicate is a great option for newly released language models or models that yo If you don't specify the `model` parameter, it will default to `replicate/llama-2-70b-chat:58d078176e02c219e11eb4da5a02a7830a283b14cf8f94537af893ccff5ee781`. -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Replicate.ts) +View the source diff --git a/docs/customize/model-providers/more/sagemaker.mdx b/docs/customize/model-providers/more/sagemaker.mdx index 43505d55b34..00d3d7060d1 100644 --- a/docs/customize/model-providers/more/sagemaker.mdx +++ b/docs/customize/model-providers/more/sagemaker.mdx @@ -1,6 +1,6 @@ --- title: "Amazon SageMaker" -description: "Configure Amazon SageMaker with Continue to use deployed LLM endpoints for both chat and embedding models, supporting LMI and HuggingFace TEI deployments with AWS credentials" +description: "Configure Amazon SageMaker with Shadow Code to use deployed LLM endpoints for both chat and embedding models, supporting LMI and HuggingFace TEI deployments with AWS credentials" --- SageMaker can be used for both chat and embedding models. Chat models are supported for endpoints deployed with [LMI](https://docs.djl.ai/docs/serving/serving/docs/lmi/index.html), and embedding models are supported for endpoints deployed with [HuggingFace TEI](https://huggingface.co/blog/sagemaker-huggingface-embedding) diff --git a/docs/customize/model-providers/more/scaleway.mdx b/docs/customize/model-providers/more/scaleway.mdx index 0e054ae6af4..6f55ebe5c90 100644 --- a/docs/customize/model-providers/more/scaleway.mdx +++ b/docs/customize/model-providers/more/scaleway.mdx @@ -1,6 +1,6 @@ --- title: "Scaleway" -description: "Configure Scaleway Generative APIs with Continue to access AI models hosted in European data centers, offering low latency, data privacy, and EU AI Act compliance with models like Qwen2.5-Coder and BGE-Multilingual-Gemma2" +description: "Configure Scaleway Generative APIs with Shadow Code to access AI models hosted in European data centers, offering low latency, data privacy, and EU AI Act compliance with models like Qwen2.5-Coder and BGE-Multilingual-Gemma2" --- diff --git a/docs/customize/model-providers/more/siliconflow.mdx b/docs/customize/model-providers/more/siliconflow.mdx index 952d46d6407..dfcb028c7b5 100644 --- a/docs/customize/model-providers/more/siliconflow.mdx +++ b/docs/customize/model-providers/more/siliconflow.mdx @@ -1,6 +1,6 @@ --- title: "SiliconFlow" -description: "Configure SiliconFlow with Continue to access their AI model platform, featuring Qwen's Coder models for chat and autocomplete, along with various embedding and reranking models" +description: "Configure SiliconFlow with Shadow Code to access their AI model platform, featuring Qwen's Coder models for chat and autocomplete, along with various embedding and reranking models" --- diff --git a/docs/customize/model-providers/more/tensorix.mdx b/docs/customize/model-providers/more/tensorix.mdx index 6e2d7c23940..eca8ae60b0b 100644 --- a/docs/customize/model-providers/more/tensorix.mdx +++ b/docs/customize/model-providers/more/tensorix.mdx @@ -1,6 +1,6 @@ --- title: "Tensorix" -description: "Configure Tensorix with Continue to access DeepSeek, Llama, Qwen, GLM, and other models through a single OpenAI-compatible API gateway" +description: "Configure Tensorix with Shadow Code to access DeepSeek, Llama, Qwen, GLM, and other models through a single OpenAI-compatible API gateway" --- [Tensorix](https://tensorix.ai) is an OpenAI-compatible API gateway that provides access to DeepSeek, Llama, Qwen, GLM, MiniMax, and other models. Pay-as-you-go with no subscription required. @@ -90,4 +90,4 @@ We recommend configuring **deepseek/deepseek-chat-v3.1** as your chat model. Tensorix provides access to various embedding models. [Click here](https://tensorix.ai/models) to see a list of available models. -[View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Tensorix.ts) +View the source diff --git a/docs/customize/model-providers/more/textgenwebui.mdx b/docs/customize/model-providers/more/textgenwebui.mdx index 032e6a6814f..ba9c08ccffd 100644 --- a/docs/customize/model-providers/more/textgenwebui.mdx +++ b/docs/customize/model-providers/more/textgenwebui.mdx @@ -1,6 +1,6 @@ --- title: "Text Generation WebUI" -description: "Configure Text Generation WebUI with Continue to use its comprehensive open-source language model UI and local server through its OpenAI-compatible API" +description: "Configure Text Generation WebUI with Shadow Code to use its comprehensive open-source language model UI and local server through its OpenAI-compatible API" --- TextGenWebUI is a comprehensive, open-source language model UI and local server. You can set it up with an OpenAI-compatible server plugin, and then configure it like this: diff --git a/docs/customize/model-providers/more/together.mdx b/docs/customize/model-providers/more/together.mdx index 03b01b779a2..7311524bd3a 100644 --- a/docs/customize/model-providers/more/together.mdx +++ b/docs/customize/model-providers/more/together.mdx @@ -4,7 +4,7 @@ sidebarTitle: "Together AI" --- - **Discover Together AI models [here](https://continue.dev/togetherai)** + **Discover Together AI models here** @@ -44,5 +44,5 @@ sidebarTitle: "Together AI" - **Check out a more advanced configuration [here](https://continue.dev/togetherai/qwen3-coder-480b-a35b-instruct-fp8?view=config)** + **Check out a more advanced configuration here** \ No newline at end of file diff --git a/docs/customize/model-providers/more/venice.mdx b/docs/customize/model-providers/more/venice.mdx index 1f48802cb4b..7725e4bb9d0 100644 --- a/docs/customize/model-providers/more/venice.mdx +++ b/docs/customize/model-providers/more/venice.mdx @@ -1,11 +1,11 @@ --- title: "Venice AI" -description: "Configure Venice AI with Continue to access this privacy-focused generative AI platform that supports open-source LLMs without storing private user data" +description: "Configure Venice AI with Shadow Code to access this privacy-focused generative AI platform that supports open-source LLMs without storing private user data" --- Venice.AI is a privacy focused generative AI platform, allowing users to interact with open-source LLMs without storing any private user data. To get started with Venice's API, either purchase a pro account, stake $VVV to obtain daily inference allotments or fund your account with USD and head over to https://venice.ai/settings/api. Venice hosts state of the art open-source AI models and supports the OpenAI API standard, allowing users to easily interact with the platform. Learn more about the Venice API at https://venice.ai/api. -Change `~/.continue/config.json` to look like the following. +Change `~/.shadow-code/config.json` to look like the following. ```json title="config.json" { diff --git a/docs/customize/model-providers/more/vllm.mdx b/docs/customize/model-providers/more/vllm.mdx index 3d15982dbb7..0fef1c80814 100644 --- a/docs/customize/model-providers/more/vllm.mdx +++ b/docs/customize/model-providers/more/vllm.mdx @@ -1,6 +1,6 @@ --- title: "vLLM" -description: "Configure vLLM's high-performance inference library with Continue for chat, autocomplete, and embeddings, including setup instructions for Llama3.1, Qwen2.5-Coder, and Nomic Embed models" +description: "Configure vLLM's high-performance inference library with Shadow Code for chat, autocomplete, and embeddings, including setup instructions for Llama3.1, Qwen2.5-Coder, and Nomic Embed models" --- vLLM is an open-source library for fast LLM inference which typically is used to serve multiple users at the same time. It can also be used to run a large model on multiple GPU:s (e.g. when it doesn´t fit in a single GPU). Run their OpenAI-compatible server using `vllm serve`. See their [server documentation](https://docs.vllm.ai/en/latest/serving/openai_compatible_server.html) and the [engine arguments documentation](https://docs.vllm.ai/en/latest/usage/engine_args.html). @@ -112,8 +112,8 @@ We recommend configuring **Nomic Embed Text** as your embeddings model. ## Reranking Model -Continue automatically handles vLLM's response format (which uses `results` instead of `data`). +Shadow Code automatically handles vLLM's response format (which uses `results` instead of `data`). [Click here](../../model-roles/reranking) to see a list of reranking model providers. -The continue implementation uses [OpenAI](../top-level/openai) under the hood. [View the source](https://github.com/continuedev/continue/blob/main/core/llm/llms/Vllm.ts) +The continue implementation uses [OpenAI](../top-level/openai) under the hood. View the source diff --git a/docs/customize/model-providers/more/watsonx.mdx b/docs/customize/model-providers/more/watsonx.mdx index 7681b5b272b..25258c40a5a 100644 --- a/docs/customize/model-providers/more/watsonx.mdx +++ b/docs/customize/model-providers/more/watsonx.mdx @@ -1,6 +1,6 @@ --- title: "IBM WatsonX" -description: "How to configure IBM's watsonx models in Continue, including authentication methods, deployment options, and support for chat, autocomplete, embeddings, and reranking models" +description: "How to configure IBM's watsonx models in Shadow Code, including authentication methods, deployment options, and support for chat, autocomplete, embeddings, and reranking models" --- watsonx, developed by IBM, offers a variety of pre-trained AI foundation models that can be used for natural language processing (NLP), computer vision, and speech recognition tasks. @@ -13,7 +13,7 @@ Accessing watsonx models can be done either through watsonx SaaS on IBM Cloud or To get started with watsonx SaaS, visit the [registration page](https://dataplatform.cloud.ibm.com/registration/stepone?context=wx). If you do not have an existing IBM Cloud account, you can sign up for a free trial. -To authenticate to watsonx.ai SaaS with Continue, you will need to create a project and [set up an API key](https://www.ibm.com/docs/en/mas-cd/continuous-delivery?topic=cli-creating-your-cloud-api-key). Then, in continue: +To authenticate to watsonx.ai SaaS with Shadow Code, you will need to create a project and [set up an API key](https://www.ibm.com/docs/en/mas-cd/continuous-delivery?topic=cli-creating-your-cloud-api-key). Then, in continue: - Set **apiBase** to your watsonx SaaS endpoint, e.g. `https://us-south.ml.cloud.ibm.com` for US South region. - Set **projectId** to your watsonx project ID. @@ -21,7 +21,7 @@ To authenticate to watsonx.ai SaaS with Continue, you will need to create a proj ### watsonx.ai Software -To authenticate to your watsonx.ai Software instance with Continue, you can use either `username/password` or `ZenApiKey` method: +To authenticate to your watsonx.ai Software instance with Shadow Code, you can use either `username/password` or `ZenApiKey` method: 1. _Option 1_ (Recommended): using `ZenApiKey` authentication: - Set **apiBase** to your watsonx software endpoint, e.g. `https://cpd-watsonx.apps.example.com`. diff --git a/docs/customize/model-providers/more/xAI.mdx b/docs/customize/model-providers/more/xAI.mdx index e14c1a1145b..b4d7ec18d31 100644 --- a/docs/customize/model-providers/more/xAI.mdx +++ b/docs/customize/model-providers/more/xAI.mdx @@ -3,7 +3,7 @@ title: xAI slug: ../xai --- -**Discover xAI models [here](https://continue.dev/xai)** +**Discover xAI models here** Get an API key from the [xAI Console](https://console.x.ai/) @@ -41,5 +41,5 @@ slug: ../xai **Check out a more advanced configuration - [here](https://continue.dev/xai/grok-code-fast-1?view=config)** + here** diff --git a/docs/customize/model-providers/more/zai.mdx b/docs/customize/model-providers/more/zai.mdx index 1b5e59c0a7c..e1bbc945205 100644 --- a/docs/customize/model-providers/more/zai.mdx +++ b/docs/customize/model-providers/more/zai.mdx @@ -1,6 +1,6 @@ --- title: "Z.ai" -description: "Configure Z.ai's GLM models with Continue, including GLM-5, GLM-4.7, and GLM-4.5" +description: "Configure Z.ai's GLM models with Shadow Code, including GLM-5, GLM-4.7, and GLM-4.5" --- [Z.ai](https://z.ai/) (formerly Zhipu AI) provides the GLM family of large language models with strong multilingual capabilities. diff --git a/docs/customize/model-providers/overview.mdx b/docs/customize/model-providers/overview.mdx index 7ba030dcb4d..17dc3c5c97e 100644 --- a/docs/customize/model-providers/overview.mdx +++ b/docs/customize/model-providers/overview.mdx @@ -1,6 +1,6 @@ --- title: "Model Providers Overview" -description: "Continue supports a wide range of AI model providers to power different features like chat, code editing, autocompletion, and embeddings. This overview helps you navigate through the available options and find the right provider for your needs." +description: "Shadow Code supports a wide range of AI model providers to power different features like chat, code editing, autocompletion, and embeddings. This overview helps you navigate through the available options and find the right provider for your needs." --- ## Popular Model Providers @@ -24,7 +24,7 @@ These are the most commonly used model providers that offer a wide range of capa ## Additional Model Providers -Beyond the top-level providers, Continue supports many other options: +Beyond the top-level providers, Shadow Code supports many other options: ### Hosted Services @@ -88,4 +88,4 @@ For more detailed configuration, visit the specific provider pages linked above. ## Change Your Model Provider -Continue allows you to choose your favorite or even add multiple model providers. This allows you to use different models for different tasks, or to try another model if you’re not happy with the results from your current model. Continue supports all of the popular model providers, including OpenAI, Anthropic, Microsoft/Azure, Mistral, and more. You can even self host your own model provider if you’d like. [Learn more about model providers](./top-level/openai). +Shadow Code allows you to choose your favorite or even add multiple model providers. This allows you to use different models for different tasks, or to try another model if you’re not happy with the results from your current model. Shadow Code supports all of the popular model providers, including OpenAI, Anthropic, Microsoft/Azure, Mistral, and more. You can even self host your own model provider if you’d like. [Learn more about model providers](./top-level/openai). diff --git a/docs/customize/model-providers/top-level/anthropic.mdx b/docs/customize/model-providers/top-level/anthropic.mdx index ce6fea3cc1b..598093c5507 100644 --- a/docs/customize/model-providers/top-level/anthropic.mdx +++ b/docs/customize/model-providers/top-level/anthropic.mdx @@ -1,11 +1,11 @@ --- -title: "How to Configure Anthropic Claude Models with Continue" +title: "How to Configure Anthropic Claude Models with Shadow Code" slug: ../anthropic sidebarTitle: "Anthropic" --- - **Discover Anthropic models [here](https://continue.dev/anthropic)** + **Discover Anthropic models here** @@ -45,7 +45,7 @@ sidebarTitle: "Anthropic" - **Check out a more advanced configuration [here](https://continue.dev/anthropic/claude-sonnet-4-6?view=config)** + **Check out a more advanced configuration here** ## How to Enable Prompt Caching with Claude diff --git a/docs/customize/model-providers/top-level/azure.mdx b/docs/customize/model-providers/top-level/azure.mdx index 4fc3f86db88..c8e014df2ed 100644 --- a/docs/customize/model-providers/top-level/azure.mdx +++ b/docs/customize/model-providers/top-level/azure.mdx @@ -1,5 +1,5 @@ --- -title: "How to Configure Azure AI Foundry with Continue" +title: "How to Configure Azure AI Foundry with Shadow Code" slug: ../azure sidebarTitle: "Azure AI Foundry" --- diff --git a/docs/customize/model-providers/top-level/bedrock.mdx b/docs/customize/model-providers/top-level/bedrock.mdx index a263aba3276..7e14e97b64a 100644 --- a/docs/customize/model-providers/top-level/bedrock.mdx +++ b/docs/customize/model-providers/top-level/bedrock.mdx @@ -1,11 +1,11 @@ --- -title: "How to Configure Amazon Bedrock with Continue" +title: "How to Configure Amazon Bedrock with Shadow Code" slug: ../bedrock sidebarTitle: "Amazon Bedrock" --- - **Discover Amazon Bedrock models [here](https://continue.dev/amazon)** + **Discover Amazon Bedrock models here** @@ -50,7 +50,7 @@ sidebarTitle: "Amazon Bedrock" - **Check out a more advanced configuration [here](https://continue.dev/amazon/us-anthropic-claude-sonnet-4-20250514-v1?view=config)** + **Check out a more advanced configuration here** ## How to Enable Prompt Caching with Amazon Bedrock diff --git a/docs/customize/model-providers/top-level/gemini.mdx b/docs/customize/model-providers/top-level/gemini.mdx index 835979d7dfd..2be7a9f73bd 100644 --- a/docs/customize/model-providers/top-level/gemini.mdx +++ b/docs/customize/model-providers/top-level/gemini.mdx @@ -1,5 +1,5 @@ --- -title: "How to Configure Gemini with Continue" +title: "How to Configure Gemini with Shadow Code" slug: ../gemini sidebarTitle: "Gemini" --- diff --git a/docs/customize/model-providers/top-level/inception.mdx b/docs/customize/model-providers/top-level/inception.mdx index 4c48e709c17..1fc1f4b140a 100644 --- a/docs/customize/model-providers/top-level/inception.mdx +++ b/docs/customize/model-providers/top-level/inception.mdx @@ -1,11 +1,11 @@ --- -title: "How to Configure Inception with Continue" +title: "How to Configure Inception with Shadow Code" slug: ../inception sidebarTitle: "Inception" --- - **Discover Inception models [here](https://continue.dev/inceptionlabs)** + **Discover Inception models here** @@ -45,5 +45,5 @@ sidebarTitle: "Inception" - **Check out a more advanced configuration [here](https://continue.dev/inceptionlabs/mercury-coder?view=config)** + **Check out a more advanced configuration here** diff --git a/docs/customize/model-providers/top-level/lmstudio.mdx b/docs/customize/model-providers/top-level/lmstudio.mdx index e16b7460764..95ae8066df9 100644 --- a/docs/customize/model-providers/top-level/lmstudio.mdx +++ b/docs/customize/model-providers/top-level/lmstudio.mdx @@ -3,7 +3,7 @@ title: "LM Studio" --- - **Discover LM Studio models [here](https://continue.dev/lmstudio)** + **Discover LM Studio models here** @@ -47,5 +47,5 @@ title: "LM Studio" - **Check out a more advanced configuration [here](https://continue.dev/lmstudio/qwen-qwen3-coder-30b?view=config)** + **Check out a more advanced configuration here** \ No newline at end of file diff --git a/docs/customize/model-providers/top-level/ollama.mdx b/docs/customize/model-providers/top-level/ollama.mdx index 8155dba53f9..174cfd1a3ca 100644 --- a/docs/customize/model-providers/top-level/ollama.mdx +++ b/docs/customize/model-providers/top-level/ollama.mdx @@ -1,11 +1,11 @@ --- -title: "How to Configure Ollama with Continue" +title: "How to Configure Ollama with Shadow Code" slug: ../ollama sidebarTitle: "Ollama" --- - **Discover Ollama models [here](https://continue.dev/lmstudio)** + **Discover Ollama models here** @@ -45,7 +45,7 @@ sidebarTitle: "Ollama" - **Check out a more advanced configuration [here](https://continue.dev/ollama/qwen3-coder-30b?view=config)** + **Check out a more advanced configuration here** ## How to Configure Model Capabilities in Ollama @@ -95,7 +95,7 @@ Ollama models usually have their capabilities auto-detected correctly. However, ### "Model requires more system memory" -Continue may set a higher default context length than other Ollama tools, causing this error even when the model works elsewhere. Fix by reducing `contextLength`: +Shadow Code may set a higher default context length than other Ollama tools, causing this error even when the model works elsewhere. Fix by reducing `contextLength`: ```yaml title="config.yaml" models: diff --git a/docs/customize/model-providers/top-level/openai.mdx b/docs/customize/model-providers/top-level/openai.mdx index 4d7326b479e..d14727d43db 100644 --- a/docs/customize/model-providers/top-level/openai.mdx +++ b/docs/customize/model-providers/top-level/openai.mdx @@ -1,11 +1,11 @@ --- -title: "How to Configure OpenAI Models with Continue" +title: "How to Configure OpenAI Models with Shadow Code" slug: ../openai sidebarTitle: "OpenAI" --- - **Discover OpenAI models [here](https://continue.dev/openai)** + **Discover OpenAI models here** @@ -45,7 +45,7 @@ sidebarTitle: "OpenAI" - **Check out a more advanced configuration [here](https://continue.dev/openai/gpt-5?view=config)** + **Check out a more advanced configuration here** ## OpenAI API compatible providers @@ -134,7 +134,7 @@ To force usage of `completions` instead of `chat/completions` endpoint you can s ### How to Disable the Responses API -By default, Continue uses OpenAI's `/responses` endpoint for o-series and gpt-5 models. If you encounter "organization must be verified" errors related to reasoning summaries or streaming, you can force the use of `/chat/completions` instead: +By default, Shadow Code uses OpenAI's `/responses` endpoint for o-series and gpt-5 models. If you encounter "organization must be verified" errors related to reasoning summaries or streaming, you can force the use of `/chat/completions` instead: diff --git a/docs/customize/model-providers/top-level/openrouter.mdx b/docs/customize/model-providers/top-level/openrouter.mdx index b20f9022864..264b6b1fff5 100644 --- a/docs/customize/model-providers/top-level/openrouter.mdx +++ b/docs/customize/model-providers/top-level/openrouter.mdx @@ -1,10 +1,10 @@ --- -title: "How to Configure OpenRouter with Continue" +title: "How to Configure OpenRouter with Shadow Code" sidebarTitle: "OpenRouter" --- - **Discover OpenRouter models [here](https://continue.dev/openrouter)** + **Discover OpenRouter models here** @@ -44,7 +44,7 @@ sidebarTitle: "OpenRouter" - **Check out a more advanced configuration [here](https://continue.dev/openrouter/qwen3-coder?view=config)** + **Check out a more advanced configuration here** ## Optional configuration @@ -94,7 +94,7 @@ For example, to prevent extra long prompts from being compressed, you can explic OpenRouter models may require explicit capability configuration because the proxy doesn't always preserve the function calling support of the original model. - Continue automatically uses system message tools for models that don't support + Shadow Code automatically uses system message tools for models that don't support native function calling, so Agent mode should work even without explicit capability configuration. However, you can still override capabilities if needed. diff --git a/docs/customize/model-providers/top-level/tetrate_agent_router_service.mdx b/docs/customize/model-providers/top-level/tetrate_agent_router_service.mdx index 0ec01e984a0..03399cddacd 100644 --- a/docs/customize/model-providers/top-level/tetrate_agent_router_service.mdx +++ b/docs/customize/model-providers/top-level/tetrate_agent_router_service.mdx @@ -24,9 +24,9 @@ Go to the [API keys page](https://router.tetrate.ai/api-keys) to get your key Tetrate get API key - + - Choose a configuration method below. -- If you use the Continue VS Code extension, install version `>=1.2.3`. +- If you use the Shadow Code VS Code extension, install version `>=1.2.3`. @@ -55,7 +55,7 @@ Go to the [API keys page](https://router.tetrate.ai/api-keys) to get your key Use a Tetrate model block in your local agent configuration: -```yaml title="~/.continue/config.yaml" +```yaml title="~/.shadow-code/config.yaml" name: Local Agent version: 1.0.0 schema: v1 @@ -75,7 +75,7 @@ context: Or define the model directly: -```yaml title="~/.continue/config.yaml" +```yaml title="~/.shadow-code/config.yaml" name: Local Agent version: 1.0.0 schema: v1 @@ -108,7 +108,7 @@ context: Name the file `my-claude-4-model.yaml` so you can reference it in the Agent. -```yaml title="~/.continue/models/my-claude-4-model.yaml" +```yaml title="~/.shadow-code/models/my-claude-4-model.yaml" name: Claude Sonnet 4 version: 1.0.1 schema: v1 @@ -126,7 +126,7 @@ models: ``` Reference it as `my-claude-4-model` in your Agent configuration: -```yaml title="~/.continue/agents/simple-agent.yaml" +```yaml title="~/.shadow-code/agents/simple-agent.yaml" name: Simple Agent version: 1.0.0 schema: v1 diff --git a/docs/customize/model-providers/top-level/vertexai.mdx b/docs/customize/model-providers/top-level/vertexai.mdx index d85c1104313..c3d9cb357e0 100644 --- a/docs/customize/model-providers/top-level/vertexai.mdx +++ b/docs/customize/model-providers/top-level/vertexai.mdx @@ -1,5 +1,5 @@ --- -title: "How to Configure Vertex AI with Continue" +title: "How to Configure Vertex AI with Shadow Code" slug: ../vertexai sidebarTitle: "Vertex AI" --- diff --git a/docs/customize/model-roles.mdx b/docs/customize/model-roles.mdx index 7349ad53943..b030d43dfaa 100644 --- a/docs/customize/model-roles.mdx +++ b/docs/customize/model-roles.mdx @@ -2,7 +2,7 @@ title: "Model roles" sidebarTitle: Overview icon: "circle-info" -description: "Learn about the different model roles in Continue including chat, autocomplete, edit, apply, embeddings, and reranking for customizing your AI coding agent's capabilities" +description: "Learn about the different model roles in Shadow Code including chat, autocomplete, edit, apply, embeddings, and reranking for customizing your AI coding agent's capabilities" --- diff --git a/docs/customize/model-roles/00-intro.mdx b/docs/customize/model-roles/00-intro.mdx index b2345ec8b4c..7755c4f2b7c 100644 --- a/docs/customize/model-roles/00-intro.mdx +++ b/docs/customize/model-roles/00-intro.mdx @@ -6,7 +6,7 @@ sidebar_position: 0 sidebar_label: Introduction --- -Models in Continue can be configured to be used for various roles in the extension. +Models in Shadow Code can be configured to be used for various roles in the extension. - [`chat`](./chat.mdx): Used for chat conversations in the extension sidebar - [`autocomplete`](./autocomplete): Used for autocomplete code suggestions in the editor diff --git a/docs/customize/model-roles/apply.mdx b/docs/customize/model-roles/apply.mdx index 6e351f73a92..c6edd3b157a 100644 --- a/docs/customize/model-roles/apply.mdx +++ b/docs/customize/model-roles/apply.mdx @@ -13,13 +13,13 @@ When editing code, Chat and Edit model output often doesn't clearly align with e For the latest Apply model recommendations, see our [comprehensive model recommendations](/customize/models#recommended-models). -We recommend [Morph Fast Apply](https://morphllm.com) or [Relace's Instant Apply model](https://continue.dev/relace/instant-apply) for the fastest Apply experience. You can sign up for Morph's free tier [here](https://morphllm.com/dashboard) or get a Relace API key [here](https://app.relace.ai/settings/api-keys). +We recommend [Morph Fast Apply](https://morphllm.com) or Relace's Instant Apply model for the fastest Apply experience. You can sign up for Morph's free tier [here](https://morphllm.com/dashboard) or get a Relace API key [here](https://app.relace.ai/settings/api-keys). However, most Chat models can also be used for applying code changes. We recommend smaller/cheaper models for the task, such as Claude 3.5 Haiku. ## Prompt templating -You can customize the prompt template used for applying code changes by setting the `promptTemplates.apply` property in your model configuration. Continue uses [Handlebars syntax](https://handlebarsjs.com/guide/) for templating. +You can customize the prompt template used for applying code changes by setting the `promptTemplates.apply` property in your model configuration. Shadow Code uses [Handlebars syntax](https://handlebarsjs.com/guide/) for templating. Available variables for the apply template: diff --git a/docs/customize/model-roles/autocomplete.mdx b/docs/customize/model-roles/autocomplete.mdx index b0a792b5c72..72dae424c60 100644 --- a/docs/customize/model-roles/autocomplete.mdx +++ b/docs/customize/model-roles/autocomplete.mdx @@ -1,7 +1,7 @@ --- -title: "Autocomplete Role in Continue Models" +title: "Autocomplete Role in Shadow Code Models" sidebarTitle: "Autocomplete Role" -description: "Learn how the autocomplete role works in Continue, which models to use, and how to customize prompt templates for inline code suggestions." +description: "Learn how the autocomplete role works in Shadow Code, which models to use, and how to customize prompt templates for inline code suggestions." keywords: [autocomplete, model, role] sidebar_position: 2 --- @@ -11,7 +11,7 @@ import { ModelRecommendations } from '/snippets/ModelRecommendations.jsx' An "autocomplete model" is an LLM that is trained on a special format called fill-in-the-middle (FIM). This format is designed to be given the prefix and suffix of a code file and predict what goes between. This task is very specific, which on one hand means that the models can be smaller (even a 3B parameter model can perform well). On the other hand, this means that Chat models, though larger, will often perform poorly even with extensive prompting. -In Continue, autocomplete models are used to display inline [Autocomplete](../../ide-extensions/autocomplete/quick-start) suggestions as you type. Autocomplete models are designated by adding the `autocomplete` to the model's `roles` in `config.yaml`. +In Shadow Code, autocomplete models are used to display inline [Autocomplete](../../ide-extensions/autocomplete/quick-start) suggestions as you type. Autocomplete models are designated by adding the `autocomplete` to the model's `roles` in `config.yaml`. ## Recommended Autocomplete models @@ -21,7 +21,7 @@ Visit the [Autocomplete Deep Dive](../deep-dives/autocomplete) for detailed setu ## Prompt templating -You can customize the prompt template used when autocomplete happens by setting the `promptTemplates.autocomplete` property in your model configuration. Continue uses [Handlebars syntax](https://handlebarsjs.com/guide/) for templating. +You can customize the prompt template used when autocomplete happens by setting the `promptTemplates.autocomplete` property in your model configuration. Shadow Code uses [Handlebars syntax](https://handlebarsjs.com/guide/) for templating. Available variables for the apply template: diff --git a/docs/customize/model-roles/chat.mdx b/docs/customize/model-roles/chat.mdx index 348876fb7df..9c755b165eb 100644 --- a/docs/customize/model-roles/chat.mdx +++ b/docs/customize/model-roles/chat.mdx @@ -10,7 +10,7 @@ import { ModelRecommendations } from '/snippets/ModelRecommendations.jsx' A "chat model" is an LLM that is trained to respond in a conversational format. Because they should be able to answer general questions and generate complex code, the best chat models are typically large, often 405B+ parameters. -In Continue, these models are used for normal [Chat](../../ide-extensions/chat/quick-start). The selected chat model will also be used for [Edit](../../ide-extensions/edit/quick-start) and [Apply](./apply.mdx) if no `edit` or `apply` models are specified, respectively. +In Shadow Code, these models are used for normal [Chat](../../ide-extensions/chat/quick-start). The selected chat model will also be used for [Edit](../../ide-extensions/edit/quick-start) and [Apply](./apply.mdx) if no `edit` or `apply` models are specified, respectively. ## Recommended Chat models diff --git a/docs/customize/model-roles/edit.mdx b/docs/customize/model-roles/edit.mdx index ba88138fd63..8592b31220f 100644 --- a/docs/customize/model-roles/edit.mdx +++ b/docs/customize/model-roles/edit.mdx @@ -8,7 +8,7 @@ import { ModelRecommendations } from '/snippets/ModelRecommendations.jsx' It's often useful to select a different model to respond to Edit instructions than for Chat instructions, as Edits are often more code-specific and may require less conversational readability. -In Continue, you can add `edit` to a model's roles to specify that it can be used for Edit requests. If no edit models are specified, the selected `chat` model is used. +In Shadow Code, you can add `edit` to a model's roles to specify that it can be used for Edit requests. If no edit models are specified, the selected `chat` model is used. ```yaml title="config.yaml" name: My Config @@ -32,7 +32,7 @@ Generally, our recommendations for Edit overlap with recommendations for Chat. ## Prompt templating -You can customize the prompt template used for editing code by setting the `promptTemplates.edit` property in your model configuration. Continue uses [Handlebars syntax](https://handlebarsjs.com/guide/) for templating. +You can customize the prompt template used for editing code by setting the `promptTemplates.edit` property in your model configuration. Shadow Code uses [Handlebars syntax](https://handlebarsjs.com/guide/) for templating. Available variables for the edit template: diff --git a/docs/customize/model-roles/embeddings.mdx b/docs/customize/model-roles/embeddings.mdx index 977c77a1c45..8938d184fae 100644 --- a/docs/customize/model-roles/embeddings.mdx +++ b/docs/customize/model-roles/embeddings.mdx @@ -7,7 +7,7 @@ sidebar_position: 5 An "embeddings model" is trained to convert a piece of text into a vector, which can later be rapidly compared to other vectors to determine similarity between the pieces of text. Embeddings models are typically much smaller than LLMs, and will be extremely fast and cheap in comparison. -In Continue, embeddings are generated during indexing and then used by [codebase awareness](/guides/codebase-documentation-awareness) to perform similarity search over your codebase. +In Shadow Code, embeddings are generated during indexing and then used by [codebase awareness](/guides/codebase-documentation-awareness) to perform similarity search over your codebase. You can add `embed` to a model's `roles` to specify that it can be used to embed. @@ -66,7 +66,7 @@ See [here](../model-providers/top-level/ollama) for instructions on how to use O ### Transformers.js (currently VS Code only) -[Transformers.js](https://huggingface.co/docs/transformers.js/index) is a JavaScript port of the popular [Transformers](https://huggingface.co/transformers/) library. It allows embeddings to be calculated entirely locally. The model used is `all-MiniLM-L6-v2`, which is shipped alongside the Continue extension. +[Transformers.js](https://huggingface.co/docs/transformers.js/index) is a JavaScript port of the popular [Transformers](https://huggingface.co/transformers/) library. It allows embeddings to be calculated entirely locally. The model used is `all-MiniLM-L6-v2`, which is shipped alongside the Shadow Code extension. diff --git a/docs/customize/model-roles/intro.mdx b/docs/customize/model-roles/intro.mdx index 6e9b2f87591..7f3a213deea 100644 --- a/docs/customize/model-roles/intro.mdx +++ b/docs/customize/model-roles/intro.mdx @@ -1,6 +1,6 @@ --- title: "Intro to Roles" -description: "Models in Continue can be configured to be used for various roles in the extension." +description: "Models in Shadow Code can be configured to be used for various roles in the extension." sidebarTitle: "Introduction" icon: "book-open" --- diff --git a/docs/customize/model-roles/reranking.mdx b/docs/customize/model-roles/reranking.mdx index 9f9d81239f6..3ac6b7a3a23 100644 --- a/docs/customize/model-roles/reranking.mdx +++ b/docs/customize/model-roles/reranking.mdx @@ -7,7 +7,7 @@ sidebar_position: 6 A "reranking model" is trained to take two pieces of text (often a user question and a document) and return a relevancy score between 0 and 1, estimating how useful the document will be in answering the question. Rerankers are typically much smaller than LLMs, and will be extremely fast and cheap in comparison. -In Continue, rerankers are designated using the `rerank` role and used by [codebase awareness](/guides/codebase-documentation-awareness) in order to select the most relevant code snippets after vector search. +In Shadow Code, rerankers are designated using the `rerank` role and used by [codebase awareness](/guides/codebase-documentation-awareness) in order to select the most relevant code snippets after vector search. ## Recommended reranking models diff --git a/docs/customize/models.mdx b/docs/customize/models.mdx index be896b3b63a..cd2826552fe 100644 --- a/docs/customize/models.mdx +++ b/docs/customize/models.mdx @@ -22,7 +22,7 @@ import { ModelRecommendations } from "/snippets/ModelRecommendations.jsx"; ## Learn More About Models -Continue supports [many model providers](/customize/model-providers/top-level/openai), including Anthropic, OpenAI, Gemini, Ollama, Amazon Bedrock, Azure, xAI, and more. Models can have various roles like `chat`, `edit`, `apply`, `autocomplete`, `embed`, and `rerank`. +Shadow Code supports [many model providers](/customize/model-providers/top-level/openai), including Anthropic, OpenAI, Gemini, Ollama, Amazon Bedrock, Azure, xAI, and more. Models can have various roles like `chat`, `edit`, `apply`, `autocomplete`, `embed`, and `rerank`. Read more about [model roles](/customize/model-roles), [model capabilities](/customize/deep-dives/model-capabilities) and view [`models`](/reference#models) in the YAML Reference. @@ -30,104 +30,104 @@ Read more about [model roles](/customize/model-roles), [model capabilities](/cus # Frontier Models -[Claude Sonnet 4.6](https://continue.dev/anthropic/claude-sonnet-4-6) from Anthropic +Claude Sonnet 4.6 from Anthropic 1. Get your API key from [Anthropic](https://console.anthropic.com/) -2. Add [Claude Sonnet 4.6](https://continue.dev/anthropic/claude-sonnet-4-6) to your Continue config +2. Add Claude Sonnet 4.6 to your Shadow Code config 3. Add `ANTHROPIC_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Qwen Coder 3 480B](https://continue.dev/openrouter/qwen3-coder) from Qwen +Qwen Coder 3 480B from Qwen 1. Get your API key from [OpenRouter](https://openrouter.ai/settings/keys) -2. Add [Qwen Coder 3 480B](https://continue.dev/openrouter/qwen3-coder) your Continue config +2. Add Qwen Coder 3 480B your Shadow Code config 3. Add `OPENROUTER_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[GPT-5](https://continue.dev/openai/gpt-5) from OpenAI +GPT-5 from OpenAI 1. Get your API key from [OpenAI](https://platform.openai.com) -2. Add [GPT-5](https://continue.dev/openai/gpt-5) your Continue config +2. Add GPT-5 your Shadow Code config 3. Add `OPENAI_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Kimi K2](https://continue.dev/openrouter/kimi-k2) from Moonshot AI +Kimi K2 from Moonshot AI 1. Get your API key from [OpenRouter](https://openrouter.ai/settings/keys) -2. Add [Kimi K2](https://continue.dev/openrouter/kimi-k2) your Continue config +2. Add Kimi K2 your Shadow Code config 3. Add `OPENROUTER_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Gemini 3.1 Pro](https://continue.dev/google/gemini-3.1-pro-preview) from Google +Gemini 3.1 Pro from Google 1. Get your API key from [Google AI Studio](https://aistudio.google.com) -2. Add [Gemini 3.1 Pro](https://continue.dev/google/gemini-3.1-pro-preview) your Continue config +2. Add Gemini 3.1 Pro your Shadow Code config 3. Add `GEMINI_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Grok Code Fast 1](https://continue.dev/xai/grok-code-fast-1) from xAI +Grok Code Fast 1 from xAI 1. Get your API key from [xAI](https://console.x.ai/) -2. Add [Grok Code Fast 1](https://continue.dev/xai/grok-code-fast-1) your Continue config +2. Add Grok Code Fast 1 your Shadow Code config 3. Add `XAI_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Devstral Medium](https://continue.dev/mistral/devstral-medium) from Mistral AI +Devstral Medium from Mistral AI 1. Get your API key from [Mistral AI](https://console.mistral.ai/) -2. Add [Devstral Medium](https://continue.dev/mistral/devstral-medium) your Continue config +2. Add Devstral Medium your Shadow Code config 3. Add `MISTRAL_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension -[gpt-oss-120b](https://continue.dev/openrouter/gpt-oss-120b) from OpenAI +gpt-oss-120b from OpenAI 1. Get your API key from [OpenRouter](https://openrouter.ai/settings/keys) -2. Add [gpt-oss-120b](https://continue.dev/openrouter/gpt-oss-120b) your Continue config +2. Add gpt-oss-120b your Shadow Code config 3. Add `OPENROUTER_API_KEY` to your environment or config -4. Click `Reload config` in the config selector in the Continue IDE extension +4. Click `Reload config` in the config selector in the Shadow Code IDE extension ### Local Models -Need a quick setup walkthrough? Check out [Using Ollama with Continue: A Developer's Guide](https://docs.continue.dev/guides/ollama-guide). +Need a quick setup walkthrough? Check out Using Ollama with Shadow Code: A Developer's Guide. These models can be run on your computer if you have enough VRAM. Their limited tool calling and reasoning capabilities will make it challenging to use agent mode. -[Qwen3 Coder 30B](https://continue.dev/ollama/qwen3-coder-30b) +Qwen3 Coder 30B -1. Add [Qwen3 Coder 30B](https://continue.dev/ollama/qwen3-coder-30b) your Continue config -2. Run the model with [Ollama](https://docs.continue.dev/guides/ollama-guide#using-ollama-with-continue-a-developers-guide) -3. Click `Reload config` in the config selector in the Continue IDE extension +1. Add Qwen3 Coder 30B your Shadow Code config +2. Run the model with Ollama +3. Click `Reload config` in the config selector in the Shadow Code IDE extension -[gpt-oss-20b](https://continue.dev/ollama/gpt-oss-20b) +gpt-oss-20b -1. Add [gpt-oss-20b](https://continue.dev/ollama/gpt-oss-20b) your Continue config -2. Run the model with [Ollama](https://docs.continue.dev/guides/ollama-guide#using-ollama-with-continue-a-developers-guide) -3. Click `Reload config` in the config selector in the Continue IDE extension +1. Add gpt-oss-20b your Shadow Code config +2. Run the model with Ollama +3. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Devstral Small 27B](https://continue.dev/ollama/devstral) +Devstral Small 27B -1. Add [Devstral Small](https://continue.dev/ollama/devstral) your Continue config -2. Run the model with [Ollama](https://docs.continue.dev/guides/ollama-guide#using-ollama-with-continue-a-developers-guide) -3. Click `Reload config` in the config selector in the Continue IDE extension +1. Add Devstral Small your Shadow Code config +2. Run the model with Ollama +3. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Qwen2.5-Coder 7B](https://continue.dev/ollama/qwen2.5-coder-7b) from Qwen +Qwen2.5-Coder 7B from Qwen -1. Add [Qwen2.5-Coder 7B](https://continue.dev/ollama/qwen2.5-coder-7b) your Continue config -2. Run the model with [Ollama](https://docs.continue.dev/guides/ollama-guide#using-ollama-with-continue-a-developers-guide) -3. Click `Reload config` in the config selector in the Continue IDE extension +1. Add Qwen2.5-Coder 7B your Shadow Code config +2. Run the model with Ollama +3. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Gemma 3 4B](https://continue.dev/ollama/gemma3-4b) from Google +Gemma 3 4B from Google -1. Add [Gemma 3 4B](https://continue.dev/ollama/gemma3-4b) your Continue config -2. Run the model with [Ollama](https://docs.continue.dev/guides/ollama-guide#using-ollama-with-continue-a-developers-guide) -3. Click `Reload config` in the config selector in the Continue IDE extension +1. Add Gemma 3 4B your Shadow Code config +2. Run the model with Ollama +3. Click `Reload config` in the config selector in the Shadow Code IDE extension -[Qwen2.5-Coder 1.5B](https://continue.dev/ollama/qwen2.5-coder-1.5b) from Qwen +Qwen2.5-Coder 1.5B from Qwen -1. Add [Qwen2.5-Coder 1.5B](https://continue.dev/ollama/qwen2.5-coder-1.5b) your Continue config -2. Run the model with [Ollama](https://docs.continue.dev/guides/ollama-guide#using-ollama-with-continue-a-developers-guide) -3. Click `Reload config` in the config selector in the Continue IDE extension +1. Add Qwen2.5-Coder 1.5B your Shadow Code config +2. Run the model with Ollama +3. Click `Reload config` in the config selector in the Shadow Code IDE extension diff --git a/docs/customize/overview.mdx b/docs/customize/overview.mdx index ca11854c606..86826a9360c 100644 --- a/docs/customize/overview.mdx +++ b/docs/customize/overview.mdx @@ -1,19 +1,19 @@ --- title: "Customization Overview" -description: "Learn how to customize Continue with model providers, rules, prompts, and tools" +description: "Learn how to customize Shadow Code with model providers, rules, prompts, and tools" --- -Continue can be deeply customized to fit your specific development workflow and preferences. This guide covers the main ways you can customize Continue to enhance your coding experience. +Shadow Code can be deeply customized to fit your specific development workflow and preferences. This guide covers the main ways you can customize Shadow Code to enhance your coding experience. ## Change Your Model Provider -Continue allows you to choose your favorite or even add multiple model providers. This allows you to use different models for different tasks, or to try another model if you're not happy with the results from your current model. Continue supports all of the popular model providers, including OpenAI, Anthropic, Microsoft/Azure, Mistral, and more. You can even self host your own model provider if you'd like. +Shadow Code allows you to choose your favorite or even add multiple model providers. This allows you to use different models for different tasks, or to try another model if you're not happy with the results from your current model. Shadow Code supports all of the popular model providers, including OpenAI, Anthropic, Microsoft/Azure, Mistral, and more. You can even self host your own model provider if you'd like. [Learn more about model providers →](/customize/model-providers/overview) ## Select Different Models for Specific Tasks -Different Continue features can use different models. We call these _model roles_. For example, you can use a different model for Chat mode than you do for Autocomplete. +Different Shadow Code features can use different models. We call these _model roles_. For example, you can use a different model for Chat mode than you do for Autocomplete. [Learn more about model roles →](/customize/model-roles) @@ -38,7 +38,7 @@ Give your agent the power of tools using [Agent mode in the extensions](/ide-ext ## Deep Dives -Detailed technical explanations of Continue's internal workings and advanced configuration options. +Detailed technical explanations of Shadow Code's internal workings and advanced configuration options. [Read Deep Dives →](/customize/deep-dives/configuration) @@ -55,7 +55,7 @@ Whatever you choose, you'll probably start by editing your configuration. ## Edit Your Configuration -You can easily access your configuration from the Continue Chat sidebar. Open the sidebar by pressing `cmd/ctrl` + `L` (VS Code) or `cmd/ctrl` + `J` (JetBrains) and click the Agent selector above the main chat input. Then, you can hover over an agent and click the `gear` icon. +You can easily access your configuration from the Shadow Code Chat sidebar. Open the sidebar by pressing `cmd/ctrl` + `L` (VS Code) or `cmd/ctrl` + `J` (JetBrains) and click the Agent selector above the main chat input. Then, you can hover over an agent and click the `gear` icon. ![configure](/images/customize/images/configure-continue-a5c8c79f3304c08353f3fc727aa5da7e.png) diff --git a/docs/customize/rules.mdx b/docs/customize/rules.mdx index 7eaede59c8e..82de7a8f82a 100644 --- a/docs/customize/rules.mdx +++ b/docs/customize/rules.mdx @@ -17,8 +17,8 @@ Your agent detects rules and applies the specified rules while in [Agent](/ide-e ## Where to Manage Rules - -- Create files in `.continue/rules` folder + +- Create files in `.shadow-code/rules` folder - Edit directly in your file system - Version controlled alongside your code - Best for project-specific rules (e.g., "remember to generate migrations after modifying the db") diff --git a/docs/faqs.mdx b/docs/faqs.mdx index a3851566aac..a320f81695a 100644 --- a/docs/faqs.mdx +++ b/docs/faqs.mdx @@ -1,6 +1,6 @@ --- title: "FAQs" -description: "Frequently asked questions about Continue" +description: "Frequently asked questions about Shadow Code" --- ## Networking Issues @@ -38,11 +38,11 @@ If you're seeing a `fetch failed` error and your network requires custom certifi You may also set `requestOptions.caBundlePath` to an array of paths to multiple certificates. -**_Windows VS Code Users_**: Installing the [win-ca](https://marketplace.visualstudio.com/items?itemName=ukoloff.win-ca) extension may help Continue use the Windows certificate store, but `requestOptions.caBundlePath` is the most reliable fix. +**_Windows VS Code Users_**: Installing the [win-ca](https://marketplace.visualstudio.com/items?itemName=ukoloff.win-ca) extension may help Shadow Code use the Windows certificate store, but `requestOptions.caBundlePath` is the most reliable fix. ### Common SSL certificate errors -If your logs include errors such as `unable to verify the first certificate`, `self signed certificate in certificate chain`, `certificate verify failed`, or `CERT_UNTRUSTED`, Continue was able to reach the endpoint but could not verify the TLS certificate chain it returned. +If your logs include errors such as `unable to verify the first certificate`, `self signed certificate in certificate chain`, `certificate verify failed`, or `CERT_UNTRUSTED`, Shadow Code was able to reach the endpoint but could not verify the TLS certificate chain it returned. In most cases, the fix is to export the root or intermediate CA certificate for that endpoint and set `requestOptions.caBundlePath` in your model configuration. If the server also requires mutual TLS, add `requestOptions.clientCertificate` as well. @@ -54,11 +54,11 @@ If you are using VS Code and require requests to be made through a proxy, you ar ### code-server -Continue can be used in [code-server](https://coder.com/), but if you are running across an error in the logs that includes "This is likely because the editor is not running in a secure context", please see [their documentation on securely exposing code-server](https://coder.com/docs/code-server/latest/guide#expose-code-server). +Shadow Code can be used in [code-server](https://coder.com/), but if you are running across an error in the logs that includes "This is likely because the editor is not running in a secure context", please see [their documentation on securely exposing code-server](https://coder.com/docs/code-server/latest/guide#expose-code-server). ## Changes to Configs Not Showing in VS Code -If you've made changes to a config (adding, modifying, or removing it) but the changes aren't appearing in the Continue extension in VS Code, try reloading the VS Code window: +If you've made changes to a config (adding, modifying, or removing it) but the changes aren't appearing in the Shadow Code extension in VS Code, try reloading the VS Code window: 1. Open the command palette (`cmd/ctrl` + `shift` + `P`) 2. Type "Reload Window" @@ -66,9 +66,9 @@ If you've made changes to a config (adding, modifying, or removing it) but the c This will restart VS Code and reload all extensions, which should make your config changes visible. -## I installed Continue, but don't see the sidebar window +## I installed Shadow Code, but don't see the sidebar window -By default the Continue window is on the left side of VS Code, but it can be dragged to right side as well, which we recommend in our tutorial. In the situation where you have previously installed Continue and moved it to the right side, it may still be there. You can reveal Continue either by using cmd/ctrl+L or by clicking the button in the top right of VS Code to open the right sidebar. +By default the Shadow Code window is on the left side of VS Code, but it can be dragged to right side as well, which we recommend in our tutorial. In the situation where you have previously installed Shadow Code and moved it to the right side, it may still be there. You can reveal Shadow Code either by using cmd/ctrl+L or by clicking the button in the top right of VS Code to open the right sidebar. ## I'm getting a 404 error from OpenAI @@ -76,25 +76,25 @@ If you have entered a valid API key and model, but are still getting a 404 error ## I'm getting a 404 error from OpenRouter -If you have entered a valid API key and model, but are still getting a 404 error from OpenRouter, this may be because models that do not support function calling will return an error to Continue when a request is sent. Example error: `HTTP 404 Not Found from https://openrouter.ai/api/v1/chat/completions` +If you have entered a valid API key and model, but are still getting a 404 error from OpenRouter, this may be because models that do not support function calling will return an error to Shadow Code when a request is sent. Example error: `HTTP 404 Not Found from https://openrouter.ai/api/v1/chat/completions` ## Indexing issues If you are having persistent errors with indexing, our recommendation is to rebuild your index from scratch. Note that for large codebases this may take some time. -This can be accomplished using the following command: `Continue: Rebuild codebase index`. +This can be accomplished using the following command: `Shadow Code: Rebuild codebase index`. ## Agent mode is unavailable or tools aren't working If Agent mode is grayed out or tools aren't functioning properly, this is likely due to model capability configuration issues. - Continue uses system message tools as a fallback for models without native tool support, so most models should work with Agent mode automatically. + Shadow Code uses system message tools as a fallback for models without native tool support, so most models should work with Agent mode automatically. ### Check if your model has tool support -1. Not all models support native tool/function calling, but Continue will automatically use system message tools as a fallback +1. Not all models support native tool/function calling, but Shadow Code will automatically use system message tools as a fallback 2. Try adding `capabilities: ["tool_use"]` to your model config to force tool support 3. Verify your provider supports function calling or that system message tools are working correctly @@ -116,7 +116,7 @@ If you can't upload images: ### Add capabilities -If Continue's autodetection isn't working correctly, you can manually add capabilities in your `config.yaml`: +If Shadow Code's autodetection isn't working correctly, you can manually add capabilities in your `config.yaml`: ```yaml models: @@ -134,7 +134,7 @@ Some proxy services (like OpenRouter) or custom deployments may not preserve too ### Verifying Current Capabilities -To see what capabilities Continue detected for your model: +To see what capabilities Shadow Code detected for your model: 1. Check the mode selector tooltips - they indicate if tools are available 2. Try uploading an image - if disabled, the model lacks `image_input` @@ -144,13 +144,13 @@ See the [Model Capabilities guide](/customize/deep-dives/model-capabilities) for ## Android Studio - "Nothing to show" in Chat -This can be fixed by selecting `Actions > Choose Boot runtime for the IDE` then selecting the latest version, and then restarting Android Studio. [See this thread](https://github.com/continuedev/continue/issues/596#issuecomment-1789327178) for details. +This can be fixed by selecting `Actions > Choose Boot runtime for the IDE` then selecting the latest version, and then restarting Android Studio. See this thread for details. ## I received a "Codebase indexing disabled - Your Linux system lacks required CPU features (AVX2, FMA)" notification We use LanceDB as our vector database for codebase search features. On x64 Linux systems, LanceDB requires specific CPU features (FMA and AVX2) which may not be available on older processors. -Most Continue features will work normally, including autocomplete and chat. However, commands that rely on codebase indexing, such as `@codebase`, `@files`, and `@folder`, will be disabled. +Most Shadow Code features will work normally, including autocomplete and chat. However, commands that rely on codebase indexing, such as `@codebase`, `@files`, and `@folder`, will be disabled. For more details about this requirement, see the [LanceDB issue #2195](https://github.com/lancedb/lance/issues/2195). @@ -186,7 +186,7 @@ When connecting to Ollama on another machine: ``` - Restart Ollama: `sudo systemctl restart ollama` -2. **Update your Continue config**: +2. **Update your Shadow Code config**: ```yaml models: - name: llama3 @@ -223,7 +223,7 @@ netsh interface portproxy add v4tov4 listenport=11434 listenaddress=0.0.0.0 conn ### Docker container can't connect to host Ollama -When running Continue or other tools in Docker that need to connect to Ollama on the host: +When running Shadow Code or other tools in Docker that need to connect to Ollama on the host: **Windows/Mac**: Use `host.docker.internal`: ```yaml @@ -268,7 +268,7 @@ If you're getting parse errors with remote Ollama: ### Managing Local Secrets and Environment Variables -For running Continue completely offline without internet access, see the [Running Continue Without Internet guide](/guides/running-continue-without-internet). +For running Shadow Code completely offline without internet access, see the [Running Shadow Code Without Internet guide](/guides/running-continue-without-internet). #### How to reference secrets in config.yaml @@ -282,17 +282,17 @@ models: #### Where secrets are resolved from -When Continue encounters `${{ secrets.X }}`, it searches these sources **in order**: +When Shadow Code encounters `${{ secrets.X }}`, it searches these sources **in order**: 1. **Workspace `.env` file**: `/.env` -2. **Workspace Continue `.env` file**: `/.continue/.env` -3. **Global `.env` file**: `~/.continue/.env` +2. **Workspace Shadow Code `.env` file**: `/.shadow-code/.env` +3. **Global `.env` file**: `~/.shadow-code/.env` 4. **Process environment variables**: Standard system environment variables -**IDE extensions (VS Code, JetBrains) cannot read your shell environment variables.** Setting `export OPENAI_API_KEY=...` in your terminal will not make the key available to Continue running inside your IDE. You must use a `.env` file instead. +**IDE extensions (VS Code, JetBrains) cannot read your shell environment variables.** Setting `export OPENAI_API_KEY=...` in your terminal will not make the key available to Shadow Code running inside your IDE. You must use a `.env` file instead. -Process environment variables (source 4) only work with the [Continue CLI](/cli/configuration), where you can pass them directly: `export OPENAI_API_KEY=sk-... && cn` +Process environment variables (source 4) only work with the [Shadow Code CLI](/cli/configuration), where you can pass them directly: `export OPENAI_API_KEY=sk-... && cn` #### Creating `.env` files @@ -311,10 +311,10 @@ CUSTOM_API_URL=https://api.example.com ## How do I reset the state of the extension? -Continue stores its data in the `~/.continue` directory (`%USERPROFILE%\.continue` on Windows). +Shadow Code stores its data in the `~/.shadow-code` directory (`%USERPROFILE%\.continue` on Windows). If you'd like to perform a clean reset of the extension, including removing all configuration files, indices, etc, you can remove this directory, uninstall, and then reinstall. ## Still having trouble? -You can also join [GitHub Discussions](https://github.com/continuedev/continue/discussions) for additional support. Alternatively, you can create a GitHub issue [here](https://github.com/continuedev/continue/issues/new?assignees=&labels=bug&projects=&template=bug-report-%F0%9F%90%9B.md&title=), providing details of your problem, and we'll be able to help you out more quickly. +You can also join GitHub Discussions for additional support. Alternatively, you can create a GitHub issue here, providing details of your problem, and we'll be able to help you out more quickly. diff --git a/docs/guides/atlassian-mcp-continue-cookbook.mdx b/docs/guides/atlassian-mcp-continue-cookbook.mdx index 6f2f5bfe936..bf61ea5f9e2 100644 --- a/docs/guides/atlassian-mcp-continue-cookbook.mdx +++ b/docs/guides/atlassian-mcp-continue-cookbook.mdx @@ -1,7 +1,7 @@ --- -title: "Jira Issues and Confluence Pages with Atlassian MCP and Continue" -description: "Use Continue and the Atlassian Rovo MCP to search, summarize, and manage Jira issues, Confluence pages, and Compass components with natural language prompts." -sidebarTitle: "Atlassian Workflows with Continue" +title: "Jira Issues and Confluence Pages with Atlassian MCP and Shadow Code" +description: "Use Shadow Code and the Atlassian Rovo MCP to search, summarize, and manage Jira issues, Confluence pages, and Compass components with natural language prompts." +sidebarTitle: "Atlassian Workflows with Shadow Code" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -10,7 +10,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - An Atlassian workflow assistant that uses Continue with the Atlassian Rovo MCP to: + An Atlassian workflow assistant that uses Shadow Code with the Atlassian Rovo MCP to: - Search and summarize Jira issues across projects - Find and digest Confluence documentation - Create and update Jira issues with natural language @@ -33,7 +33,7 @@ Before starting, ensure you have: For all options, first: - + @@ -43,7 +43,7 @@ For all options, first: - To use agents in headless mode, you need a [Continue API key](https://continue.dev/settings/api-keys). + To use agents in headless mode, you need a Shadow Code API key. All data access respects your existing Jira, Confluence, and Compass user permissions. @@ -57,11 +57,11 @@ For all options, first: | Agent | Best For | Use Cases | Link | |-------|----------|-----------|------| -| **Jira Agent** | Issue Management | Search issues, create stories, sprint planning, status updates, bulk operations | [View Agent](https://continue.dev/continuedev/atlassian-continuous-ai-jira-agent) | -| **Confluence Agent** | Documentation | Search pages, summarize docs, create/update pages, manage spaces | [View Agent](https://continue.dev/continuedev/atlassian-continuous-ai-confluence-agent) | +| **Jira Agent** | Issue Management | Search issues, create stories, sprint planning, status updates, bulk operations | View Agent | +| **Confluence Agent** | Documentation | Search pages, summarize docs, create/update pages, manage spaces | View Agent | - **Cross-product workflows**: Both agents can work with Jira, Confluence, and Compass. Choose based on your primary focus area, or create your own agent using the [Atlassian MCP](https://continue.dev/atlassian/atlassian-mcp). The Atlassian MCP can work with Jira, Compass, or Confluence. + **Cross-product workflows**: Both agents can work with Jira, Confluence, and Compass. Choose based on your primary focus area, or create your own agent using the Atlassian MCP. The Atlassian MCP can work with Jira, Compass, or Confluence. @@ -157,7 +157,7 @@ For all options, first: - Launch Continue and ask: + Launch Shadow Code and ask: ``` List my Jira projects ``` @@ -168,8 +168,8 @@ For all options, first: - To use Atlassian MCP with Continue CLI, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + To use Atlassian MCP with Shadow Code CLI, you need either: + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration along with the Atlassian MCP for Jira, Confluence, and Compass operations. @@ -181,7 +181,7 @@ For all options, first: ## Jira Workflows - **Using the Jira Agent**: All examples below use the [Jira Agent](https://continue.dev/continuedev/atlassian-continuous-ai-jira-agent) which is optimized for issue management. + **Using the Jira Agent**: All examples below use the Jira Agent which is optimized for issue management. Use natural language to explore, triage, and manage Jira issues. The agent calls Atlassian MCP tools under the hood. @@ -255,7 +255,7 @@ Use natural language to explore, triage, and manage Jira issues. The agent calls ## Confluence Workflows - **Using the Confluence Agent**: All examples below use the [Confluence Agent](https://continue.dev/continuedev/atlassian-continuous-ai-confluence-agent) which is optimized for documentation management. + **Using the Confluence Agent**: All examples below use the Confluence Agent which is optimized for documentation management. Access, search, and manage your team's documentation directly from Continue. @@ -484,11 +484,11 @@ After completing this guide, you have a complete **AI-powered Atlassian workflow Official Atlassian MCP documentation - - How MCP works with Continue agents + + How MCP works with Shadow Code agents - - Continue documentation and guides + + Shadow Code documentation and guides Get help and share feedback diff --git a/docs/guides/chrome-devtools-mcp-performance.mdx b/docs/guides/chrome-devtools-mcp-performance.mdx index 22a061c966a..7f445ffd578 100644 --- a/docs/guides/chrome-devtools-mcp-performance.mdx +++ b/docs/guides/chrome-devtools-mcp-performance.mdx @@ -22,7 +22,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - [Performance Insights](https://developer.chrome.com/docs/devtools/performance-insights) with AI-powered analysis - [Screenshot Debugging](https://developer.chrome.com/docs/devtools/device-mode) for visual regression detection -This guide shows you how to leverage these features through natural language with Continue CLI! +This guide shows you how to leverage these features through natural language with Shadow Code CLI! @@ -41,14 +41,14 @@ This cookbook teaches you to: - Chrome browser installed - Web project with a running development server (or deployed URL) - Node.js 20+ installed -- [Continue CLI](https://docs.continue.dev/guides/cli) -- [Chrome DevTools MCP](https://continue.dev) configured +- Shadow Code CLI +- [Chrome DevTools MCP]() configured ## Quick Setup For all options, first: - + @@ -77,7 +77,7 @@ After completing **Quick Setup** above, you have two paths to get started: This agent includes: - **Optimized prompts** for performance analysis and debugging - **Built-in rules** for consistent formatting and error handling - - **[Chrome DevTools MCP](https://continue.dev/google/chrome-devtools-mcp)** for reliable browser automation + - **Chrome DevTools MCP** for reliable browser automation @@ -142,7 +142,7 @@ After completing **Quick Setup** above, you have two paths to get started: To use the pre-built agent, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables - **Chrome browser** installed on your system - **Node.js 20+** to run the MCP via npx @@ -204,11 +204,11 @@ Please check the LCP of web.dev. ## Performance Analysis Recipes -Now you can use natural language prompts to analyze web performance. The Continue agent automatically calls the appropriate Chrome DevTools MCP tools. +Now you can use natural language prompts to analyze web performance. The Shadow Code agent automatically calls the appropriate Chrome DevTools MCP tools. **Where to run these workflows:** - - **IDE Extensions**: Use Continue in VS Code, JetBrains, or other supported IDEs + - **IDE Extensions**: Use Shadow Code in VS Code, JetBrains, or other supported IDEs - **Terminal (TUI mode)**: Run `cn` to enter interactive mode, then type your prompts - **CLI (headless mode)**: Use `cn -p "your prompt"` for headless commands @@ -401,11 +401,11 @@ This example demonstrates a **Continuous AI workflow** where performance validat Navigate to **Repository Settings → Secrets and variables → Actions** and add: -- `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) +- `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys ### Create Workflow File -This workflow automatically validates web performance on pull requests using the Continue CLI in headless mode. It records performance traces, extracts Core Web Vitals, and posts a summary report as a PR comment. +This workflow automatically validates web performance on pull requests using the Shadow Code CLI in headless mode. It records performance traces, extracts Core Web Vitals, and posts a summary report as a PR comment. Create `.github/workflows/performance-check.yml` in your repository: @@ -789,8 +789,8 @@ Set up automated regression detection: Official Chrome DevTools MCP repository - - Explore Continue documentation and guides + + Explore Shadow Code documentation and guides Complete Chrome DevTools documentation diff --git a/docs/guides/cli.mdx b/docs/guides/cli.mdx index 5e1337767f9..d7974cd29e9 100644 --- a/docs/guides/cli.mdx +++ b/docs/guides/cli.mdx @@ -1,7 +1,7 @@ --- -title: "How to Use Continue CLI (cn)" -sidebarTitle: "Continue CLI (cn)" -description: "Learn how to use Continue's command-line interface for context engineering, automated coding tasks, and headless development workflows with customizable models, rules, and tools" +title: "How to Use Shadow Code CLI (cn)" +sidebarTitle: "Shadow Code CLI (cn)" +description: "Learn how to use Shadow Code's command-line interface for context engineering, automated coding tasks, and headless development workflows with customizable models, rules, and tools" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -29,7 +29,7 @@ cn cn -p "Generate a conventional commit name for the current git changes" ``` -## How to Use Continue CLI - Basic Usage +## How to Use Shadow Code CLI - Basic Usage Out of the box, `cn` comes with tools that let it understand your codebase, edit files, run terminal commands, and more (if you approve). You can ask `cn` to: @@ -51,11 +51,11 @@ In headless mode, `cn` will only output its final response, making it perfect fo echo "$(git diff) Generate a conventional commit name for the current git changes" | cn -p > commit-message.txt ``` -## How to Configure Continue CLI +## How to Configure Shadow Code CLI `cn` uses [`config.yaml`](/reference), the exact same configuration file as Continue. This means that you can use your existing local configuration. -To switch between configurations, you can use the `/config` slash command in `cn`, or you can start it with the `--config` flag (e.g. `cn --config continuedev/default-cli-config` or `cn --config ~/.continue/config.yaml`). +To switch between configurations, you can use the `/config` slash command in `cn`, or you can start it with the `--config` flag (e.g. `cn --config continuedev/default-cli-config` or `cn --config ~/.shadow-code/config.yaml`). ### How to Add Custom Models @@ -63,15 +63,15 @@ Learn how to add custom models [here](/customize/overview). Then, you can use th ### How to Configure Rules -`cn` supports [rules](/customize/deep-dives/rules) in the same way as the Continue IDE extensions. You can also use the `--rule` flag to manually include a rule. For example, `cn --rule nate/spanish` will tell `cn` to always speak in Spanish. +`cn` supports [rules](/customize/deep-dives/rules) in the same way as the Shadow Code IDE extensions. You can also use the `--rule` flag to manually include a rule. For example, `cn --rule nate/spanish` will tell `cn` to always speak in Spanish. ### How to Configure Tools -`cn` supports MCP tools, which can be configured in the [same way](/customize/deep-dives/mcp) as with the Continue IDE extensions. +`cn` supports MCP tools, which can be configured in the [same way](/customize/deep-dives/mcp) as with the Shadow Code IDE extensions. #### How to Set Tool Permissions -`cn` includes a tool permission system to make sure you approve of the agent's actions. It will begin with minimal permissions but as you approve tool calls, it will add policies to `~/.continue/permissions.yaml` to remember your preferences. +`cn` includes a tool permission system to make sure you approve of the agent's actions. It will begin with minimal permissions but as you approve tool calls, it will add policies to `~/.shadow-code/permissions.yaml` to remember your preferences. If you want to explicitly allow or deny tools for a single session, you can use the command line flags `--allow`, `--ask`, and `--exclude`. For example: @@ -88,6 +88,6 @@ cn --exclude Fetch ## Troubleshooting -Run `cn` with the `--verbose` flag to see more detailed logs. These will be output to `~/.continue/logs/cn.log`. +Run `cn` with the `--verbose` flag to see more detailed logs. These will be output to `~/.shadow-code/logs/cn.log`. -If you have feedback on the beta, please [file a GitHub issue](https://github.com/continuedev/continue/issues). +If you have feedback on the beta, please file a GitHub issue. diff --git a/docs/guides/codebase-documentation-awareness.mdx b/docs/guides/codebase-documentation-awareness.mdx index ccac285f2aa..63e9f84d747 100644 --- a/docs/guides/codebase-documentation-awareness.mdx +++ b/docs/guides/codebase-documentation-awareness.mdx @@ -21,7 +21,7 @@ Agent mode can use built-in tools to navigate and understand your code: ### Create Rules to Help the Agent Understand Your Codebase -Rules guide agent mode's behavior and understanding. Place markdown files in `.continue/rules` in your project to provide context: +Rules guide agent mode's behavior and understanding. Place markdown files in `.shadow-code/rules` in your project to provide context: ```markdown # Project Architecture @@ -87,7 +87,7 @@ You can use the `gh` CLI to: #### DeepWiki MCP -[DeepWiki MCP](https://continue.dev/deepwiki/deepwiki-mcp) lets agent mode explore any public GitHub repository. +DeepWiki MCP lets agent mode explore any public GitHub repository. Once configured, agent mode can explore repositories like: @@ -130,7 +130,7 @@ Always cite documentation when explaining concepts. #### Context7 MCP -[Context7 MCP](https://continue.dev/upstash/context7-mcp) enables agent mode to search and retrieve information from public documentation: +Context7 MCP enables agent mode to search and retrieve information from public documentation: Agent mode can then answer questions like: @@ -170,7 +170,7 @@ If you were previously using the `@Codebase` or `@Docs` context providers, here' The `@Codebase` context provider has been deprecated. Instead: 1. **Use built-in tools**: Agent mode can now use file exploration and search tools to understand your codebase -2. **Add rules**: Create `.continue/rules` files to provide context about your project structure +2. **Add rules**: Create `.shadow-code/rules` files to provide context about your project structure 3. **Use MCP servers**: For external codebases, use DeepWiki MCP or custom MCP servers ### Migrating from @Docs @@ -181,7 +181,7 @@ The `@Docs` context provider has been deprecated. Instead: 2. **Add documentation links in rules**: Create rules that reference documentation URLs 3. **Use custom MCP servers**: For internal documentation, create an MCP server with access to your docs -The new approach provides better integration with Continue's Agent mode features and more intelligent context selection. +The new approach provides better integration with Shadow Code's Agent mode features and more intelligent context selection. ## Next Steps diff --git a/docs/guides/configuring-models-rules-tools.mdx b/docs/guides/configuring-models-rules-tools.mdx index 91a98bb2392..f54e9524fd7 100644 --- a/docs/guides/configuring-models-rules-tools.mdx +++ b/docs/guides/configuring-models-rules-tools.mdx @@ -1,11 +1,11 @@ --- title: "Configuring Models, Rules, and Tools" -description: "Learn how to work with Continue's configuration system. Understand how to use models, rules, and tools, create local configurations, and organize your setup for maximum reusability." +description: "Learn how to work with Shadow Code's configuration system. Understand how to use models, rules, and tools, create local configurations, and organize your setup for maximum reusability." --- ## What Are Models, Rules, and Tools? -Continue configs are built from three main types of configuration: +Shadow Code configs are built from three main types of configuration: @@ -52,7 +52,7 @@ Perfect for project-specific setups like TypeScript rules for web apps or the Pl ## Shared Blocks -Continue uses a slug in the format of `owner/item-name` to resolve blocks. +Shadow Code uses a slug in the format of `owner/item-name` to resolve blocks. For example, to use the Claude 4 Sonnet model, you'd reference it as `anthropic/claude-4-sonnet`. @@ -75,15 +75,15 @@ Organize your local configurations using these directories: - `.continue/models` + `.shadow-code/models` - `.continue/rules` + `.shadow-code/rules` - `.continue/mcpServers` + `.shadow-code/mcpServers` @@ -97,11 +97,11 @@ When configuring a local model or MCP server, you can use the same mustache nota -`.env` file at your project root, or in `/.continue/.env` +`.env` file at your project root, or in `/.shadow-code/.env` -`.env` file in `~/.continue/.env` +`.env` file in `~/.shadow-code/.env` diff --git a/docs/guides/continue-docs-mcp-cookbook.mdx b/docs/guides/continue-docs-mcp-cookbook.mdx index 3d04240b13c..39ab70c084a 100644 --- a/docs/guides/continue-docs-mcp-cookbook.mdx +++ b/docs/guides/continue-docs-mcp-cookbook.mdx @@ -1,48 +1,48 @@ --- -title: "Contributing to Continue with Model Context Protocol (MCP)" -description: "Use the Continue Docs MCP to write cookbooks, guides, and documentation with AI-powered workflows." -sidebarTitle: "Continue Docs MCP Cookbook" +title: "Contributing to Shadow Code with Model Context Protocol (MCP)" +description: "Use the Shadow Code Docs MCP to write cookbooks, guides, and documentation with AI-powered workflows." +sidebarTitle: "Shadow Code Docs MCP Cookbook" --- - Master using the Continue Docs MCP to contribute documentation, create cookbooks, and maintain consistency across Continue's docs - all through natural language prompts. + Master using the Shadow Code Docs MCP to contribute documentation, create cookbooks, and maintain consistency across Shadow Code's docs - all through natural language prompts. ## Prerequisites Before starting, ensure you have: -- Read: [Contributing to Continue Documentation](/CONTRIBUTING) for setup instructions -- Forked the [continuedev/continue](https://github.com/continuedev/continue) repository -- Node.js 20+ and Continue CLI installed +- Read: [Contributing to Shadow Code Documentation](/CONTRIBUTING) for setup instructions +- Forked the repository +- Node.js 20+ and Shadow Code CLI installed This cookbook assumes you've read the setup in the [CONTRIBUTING guide](/CONTRIBUTING). If you haven't, start there first. -## Continue Docs MCP Setup +## Shadow Code Docs MCP Setup - Use the pre-built [Docs Assistant - Mintlify agent](https://continue.dev/continuedev/docs-mintlify) that includes the Continue Docs MCP and is ready to use immediately. + Use the pre-built Docs Assistant - Mintlify agent that includes the Shadow Code Docs MCP and is ready to use immediately. ```bash - # From your Continue docs directory + # From your Shadow Code docs directory cn --config continuedev/docs-mintlify ``` This agent includes: - - **Continue Docs MCP** for searching Continue documentation + - **Shadow Code Docs MCP** for searching Shadow Code documentation - **Mintlify formatting rules** for proper component usage - **Documentation-focused prompts** for common tasks If you want to customize, create your own agent and add: - 1. [Continue Docs MCP](https://continue.dev/continuedev/continue-docs-mcp) - 2. [Mintlify Technical Writing Rule](https://continue.dev/mintlify/technical-writing-rule) + 1. Shadow Code Docs MCP + 2. Mintlify Technical Writing Rule See the [CONTRIBUTING guide](/CONTRIBUTING) for details. @@ -50,16 +50,16 @@ Before starting, ensure you have: --- -## What is the Continue Docs MCP? +## What is the Shadow Code Docs MCP? - - A Model Context Protocol server built with [Mintlify's MCP generation](https://www.mintlify.com/blog/generate-mcp-servers-for-your-docs) that enables semantic search across Continue documentation. [Learn more →](/reference/continue-mcp) + + A Model Context Protocol server built with [Mintlify's MCP generation](https://www.mintlify.com/blog/generate-mcp-servers-for-your-docs) that enables semantic search across Shadow Code documentation. [Learn more →](/reference/continue-mcp) The MCP helps you: - **Find examples** from existing documentation - **Maintain consistency** with established patterns -- **Source accurate information** about Continue features +- **Source accurate information** about Shadow Code features - **Write better documentation** faster --- @@ -68,7 +68,7 @@ The MCP helps you: ### 🆕 Creating a New Cookbook -Cookbooks show how to use Continue CLI with specific tools or services. Here's how to create one using the Continue Docs MCP: +Cookbooks show how to use Shadow Code CLI with specific tools or services. Here's how to create one using the Shadow Code Docs MCP: @@ -78,7 +78,7 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h **Prompt:** ``` - "Show me the structure of existing MCP cookbooks in the Continue docs. + "Show me the structure of existing MCP cookbooks in the Shadow Code docs. I want to create a cookbook for GitHub MCP." ``` @@ -88,14 +88,14 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h **Prompt:** ``` - "Using the Continue Docs MCP, find information about how GitHub MCP works. + "Using the Shadow Code Docs MCP, find information about how GitHub MCP works. Then search the web for the official GitHub MCP documentation and combine both sources to create a cookbook following the same structure as the dlt cookbook." ``` The agent will: - - Search Continue docs for MCP patterns + - Search Shadow Code docs for MCP patterns - Fetch GitHub MCP official documentation - Combine both sources - Generate a cookbook with consistent formatting @@ -125,7 +125,7 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h - **Pro Tip:** The agent uses the Continue Docs MCP to maintain consistency with existing cookbooks automatically. + **Pro Tip:** The agent uses the Shadow Code Docs MCP to maintain consistency with existing cookbooks automatically. --- @@ -144,7 +144,7 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h **Prompt:** ``` "Update the MCP tools documentation at docs/customization/mcp-tools.mdx - to include information about a new MCP server. Use the Continue + to include information about a new MCP server. Use the Shadow Code Docs MCP to find examples of how other MCP servers are documented, then add a similar section for the new server." ``` @@ -159,8 +159,8 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h **Prompt:** ``` - "I want to create a guide for setting up Continue with Amazon Bedrock. - Search the Continue docs for similar model provider setup guides and + "I want to create a guide for setting up Shadow Code with Amazon Bedrock. + Search the Shadow Code docs for similar model provider setup guides and show me the common structure they follow." ``` @@ -175,7 +175,7 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h - Configuration options - Troubleshooting common issues - Use the Continue Docs MCP to find accurate information about Continue's + Use the Shadow Code Docs MCP to find accurate information about Shadow Code's model provider configuration." ``` @@ -196,7 +196,7 @@ Cookbooks show how to use Continue CLI with specific tools or services. Here's h **Prompt:** ``` -"Create a cookbook for using Continue with PostgreSQL MCP. Follow the same +"Create a cookbook for using Shadow Code with PostgreSQL MCP. Follow the same structure as the dlt cookbook, but customize it for database operations. Include these sections: @@ -205,7 +205,7 @@ Include these sections: 3. Common database tasks (schema exploration, query writing, migrations) 4. Troubleshooting database connection issues -Use the Continue Docs MCP to find MCP configuration patterns and search the +Use the Shadow Code Docs MCP to find MCP configuration patterns and search the web for PostgreSQL MCP documentation." ``` @@ -213,7 +213,7 @@ web for PostgreSQL MCP documentation." **Prompt:** ``` -"Create a cookbook showing how to use Continue to analyze Sentry errors. +"Create a cookbook showing how to use Shadow Code to analyze Sentry errors. The cookbook should demonstrate: 1. Setting up Sentry MCP integration @@ -230,15 +230,15 @@ and adapt it for Sentry." **Prompt:** ``` -"I want to create a cookbook for using Continue to work with OpenAPI specs. +"I want to create a cookbook for using Shadow Code to work with OpenAPI specs. Show how to: -1. Load OpenAPI specs into Continue's context +1. Load OpenAPI specs into Shadow Code's context 2. Generate API client code from specs 3. Create tests based on API endpoints 4. Update documentation when APIs change -Use the Continue Docs MCP to find how context providers work, then combine +Use the Shadow Code Docs MCP to find how context providers work, then combine that with OpenAPI MCP information." ``` @@ -249,17 +249,17 @@ that with OpenAPI MCP information." ```bash - "Use the Continue Docs MCP to understand how agents - work in Continue, then search the web for Anthropic's + "Use the Shadow Code Docs MCP to understand how agents + work in Shadow Code, then search the web for Anthropic's Computer Use MCP. Create a cookbook showing how to - combine Continue agents with Computer Use for + combine Shadow Code agents with Computer Use for automated testing." ``` ```bash - "Find all mentions of MCP servers across Continue + "Find all mentions of MCP servers across Shadow Code documentation. Then create a comprehensive reference page that links to all MCP-related guides and configurations." @@ -278,7 +278,7 @@ that with OpenAPI MCP information." ```bash "Extract all code examples showing MCP server - configuration from the Continue docs. Format them + configuration from the Shadow Code docs. Format them as a single reference page with explanations for each pattern." ``` @@ -335,7 +335,7 @@ The agent will: ## Automated Documentation Checks with GitHub Actions -Add automated documentation checks to your PR workflow using the Continue Docs MCP agent: +Add automated documentation checks to your PR workflow using the Shadow Code Docs MCP agent: ```yaml title=".github/workflows/docs-check.yml" name: Documentation Check @@ -362,7 +362,7 @@ jobs: with: node-version: '20' - - name: Install Continue CLI + - name: Install Shadow Code CLI run: npm i -g @continuedev/cli - name: Analyze Changes for Documentation Needs @@ -378,7 +378,7 @@ jobs: # Create detailed diff git diff origin/${{ github.base_ref }}..HEAD > changes.diff - # Use Continue agent to check if docs need updates + # Use Shadow Code agent to check if docs need updates PROMPT="Review these code changes and determine if documentation updates are needed. Changed files: $(cat changed_files.txt | tr '\n' ' ') @@ -438,9 +438,9 @@ EOF ### Resources - [Contributing to Docs](/CONTRIBUTING) -- [Continue Docs MCP Cookbook](/guides/continue-docs-mcp-cookbook) +- [Shadow Code Docs MCP Cookbook](/guides/continue-docs-mcp-cookbook) -*This analysis was generated using the Continue Docs MCP agent.* +*This analysis was generated using the Shadow Code Docs MCP agent.* EOF gh pr comment ${{ github.event.pull_request.number }} --body-file pr-comment.md @@ -454,7 +454,7 @@ EOF ``` - This workflow uses the Continue Docs MCP agent to analyze code changes and automatically comment on PRs when documentation updates are recommended. + This workflow uses the Shadow Code Docs MCP agent to analyze code changes and automatically comment on PRs when documentation updates are recommended. --- @@ -478,12 +478,12 @@ Want to create documentation MCPs for your own projects? Mintlify makes it easy: - Share your docs MCP so others can use it with Continue agents. + Share your docs MCP so others can use it with Shadow Code agents. - The [Continue Docs MCP](/reference/continue-mcp) itself was built this way! + The [Shadow Code Docs MCP](/reference/continue-mcp) itself was built this way! --- @@ -492,22 +492,22 @@ Want to create documentation MCPs for your own projects? Mintlify makes it easy: | Task | Prompt | |:-----|:-------| -| **Find structure** | "Show me the structure of existing cookbooks in Continue docs" | +| **Find structure** | "Show me the structure of existing cookbooks in Shadow Code docs" | | **Create cookbook** | "Create a cookbook for [tool] MCP following the dlt cookbook structure" | -| **Update docs** | "Update [file] to include information about [feature], using Continue Docs MCP to find examples" | +| **Update docs** | "Update [file] to include information about [feature], using Shadow Code Docs MCP to find examples" | | **Add navigation** | "Add [file] to docs.json under the [section] section" | -| **Check consistency** | "Review this file for consistency with other Continue documentation" | +| **Check consistency** | "Review this file for consistency with other Shadow Code documentation" | | **Fix formatting** | "Review for Mintlify formatting issues and fix any problems" | -| **Extract examples** | "Find all examples of [topic] in Continue docs" | -| **Research feature** | "Use Continue Docs MCP to explain how [feature] works in Continue" | +| **Extract examples** | "Find all examples of [topic] in Shadow Code docs" | +| **Research feature** | "Use Shadow Code Docs MCP to explain how [feature] works in Shadow Code" | --- ## Resources -### Continue Documentation +### Shadow Code Documentation - [Contributing Guide](/CONTRIBUTING) - Setup and submission process -- [Continue Docs MCP Reference](/reference/continue-mcp) - MCP server details +- [Shadow Code Docs MCP Reference](/reference/continue-mcp) - MCP server details - [Understanding Configs](/guides/understanding-configs) - How configs work ### Mintlify @@ -523,7 +523,7 @@ Want to create documentation MCPs for your own projects? Mintlify makes it easy: Set up your environment - + Install pre-built agent diff --git a/docs/guides/custom-code-rag.mdx b/docs/guides/custom-code-rag.mdx index 61ded639db4..e29c867d3c4 100644 --- a/docs/guides/custom-code-rag.mdx +++ b/docs/guides/custom-code-rag.mdx @@ -22,7 +22,7 @@ If you use `voyage-code-3`, it has a maximum context length of 16,000 tokens, wh 1. Truncate the file when it goes over the context length: in this case you will always have 1 chunk per file. 2. Split the file into chunks of a fixed length: starting at the top of the file, add lines in your current chunk until it reaches the limit, then start a new chunk. -3. Use a recursive, abstract syntax tree (AST)-based strategy: this is the most exact, but most complex. In most cases you can achieve high quality results by using (1) or (2), but if you'd like to try this you can find a reference example in [our code chunker](https://github.com/continuedev/continue/blob/main/core/indexing/chunk/code.ts) or in [LlamaIndex](https://docs.llamaindex.ai/en/stable/api_reference/node_parsers/code/). +3. Use a recursive, abstract syntax tree (AST)-based strategy: this is the most exact, but most complex. In most cases you can achieve high quality results by using (1) or (2), but if you'd like to try this you can find a reference example in our code chunker or in [LlamaIndex](https://docs.llamaindex.ai/en/stable/api_reference/node_parsers/code/). As usual in this guide, we recommend starting with the strategy that gives 80% of the benefit with 20% of the effort. @@ -64,7 +64,7 @@ In the beginning, you should probably run it by hand. Once you are confident tha ## Step 6: How to set up an MCP server -To integrate your custom RAG system with Continue, you'll create an MCP (Model Context Protocol) server. MCP provides a standardized way for AI tools to access external resources. +To integrate your custom RAG system with Shadow Code, you'll create an MCP (Model Context Protocol) server. MCP provides a standardized way for AI tools to access external resources. ### Create your MCP server @@ -96,7 +96,7 @@ async def search_codebase(query: str, limit: int = 10) -> list[TextContent]: # Query your vector database results = table.search(query).limit(limit).to_list() - # Format results for Continue + # Format results for Shadow Code formatted_results = [] for result in results: formatted_results.append(TextContent( @@ -125,9 +125,9 @@ if __name__ == "__main__": stdio_server(app).run() ``` -### Configure Continue to use your MCP server +### Configure Shadow Code to use your MCP server -Add your MCP server to Continue's configuration: +Add your MCP server to Shadow Code's configuration: **config.yaml:** ```yaml diff --git a/docs/guides/dlt-mcp-continue-cookbook.mdx b/docs/guides/dlt-mcp-continue-cookbook.mdx index 949d0ae1d95..b9f5d89b95d 100644 --- a/docs/guides/dlt-mcp-continue-cookbook.mdx +++ b/docs/guides/dlt-mcp-continue-cookbook.mdx @@ -1,7 +1,7 @@ --- -title: "Building Data Pipelines with dlt MCP and Continue" +title: "Building Data Pipelines with dlt MCP and Shadow Code" description: "Set up an AI-powered data engineering workflow that helps you develop, debug, and inspect dlt data pipelines using natural language commands." -sidebarTitle: "dlt Data Pipelines with Continue" +sidebarTitle: "dlt Data Pipelines with Shadow Code" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -10,7 +10,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - An AI-powered data pipeline development system that uses Continue's AI agent with dlt + An AI-powered data pipeline development system that uses Shadow Code's AI agent with dlt MCP to inspect pipeline execution, retrieve schemas, analyze datasets, and debug load errors - all through simple natural language prompts @@ -24,7 +24,7 @@ Before starting, ensure you have: For all options, first: - + @@ -35,13 +35,13 @@ For all options, first: - To use agents in headless mode, you need a [Continue API key](https://continue.dev/settings/api-keys). + To use agents in headless mode, you need a Shadow Code API key. ## dlt MCP Workflow Options - Skip the manual setup and use our pre-built [dlt Agent](https://continue.dev/continuedev/dlt-agent) that includes + Skip the manual setup and use our pre-built dlt Agent that includes the dlt MCP and optimized data pipeline workflows for more consistent results. You can customize it for your specific needs. @@ -74,22 +74,22 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - **Why Use the Agent?** The pre-built [dlt Agent](https://continue.dev/continuedev/dlt-agent) provides consistent pipeline development workflows and handles MCP configuration automatically, making it easier to get started with AI-powered data engineering. You can customize this agent later to fit your team's specific workflow. + **Why Use the Agent?** The pre-built dlt Agent provides consistent pipeline development workflows and handles MCP configuration automatically, making it easier to get started with AI-powered data engineering. You can customize this agent later to fit your team's specific workflow. - + Add the dlt MCP to your configuration: **Installation methods:** 1. **Quick CLI install**: `cn --mcp dlthub/dlt-mcp` - 2. **Manual configuration**: Add the MCP to your `~/.continue/config.json` under the `mcpServers` section + 2. **Manual configuration**: Add the MCP to your `~/.shadow-code/config.json` under the `mcpServers` section - Once installed, dlt MCP tools become available to your Continue agent for all prompts. + Once installed, dlt MCP tools become available to your Shadow Code agent for all prompts. @@ -112,8 +112,8 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - To use the pre-built [dlt Agent](https://continue.dev/continuedev/dlt-agent), you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + To use the pre-built dlt Agent, you need either: + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration along with the pre-configured dlt MCP for pipeline operations. @@ -131,24 +131,24 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - Query dataset records from destination databases - Analyze load errors, timings, and file sizes - **[dlt+ MCP](https://continue.dev/dlthub/dlt-plus-mcp)** extends these capabilities with cloud-based features for production deployments: + **dlt+ MCP** extends these capabilities with cloud-based features for production deployments: - Connect to dlt+ Projects and manage deployments - Monitor pipeline runs across multiple environments - Access centralized logging and observability - Collaborate with team members on pipeline development - For local development and getting started, **[dlt MCP](https://continue.dev/dlthub/dlt-mcp)** is the right choice. Consider **[dlt+ MCP](https://continue.dev/dlthub/dlt-plus-mcp)** when you need production deployment features and team collaboration. + For local development and getting started, **dlt MCP** is the right choice. Consider **dlt+ MCP** when you need production deployment features and team collaboration. --- ## Pipeline Development Recipes -Now you can use natural language prompts to develop and debug your dlt pipelines. The Continue agent automatically calls the appropriate dlt MCP tools. +Now you can use natural language prompts to develop and debug your dlt pipelines. The Shadow Code agent automatically calls the appropriate dlt MCP tools. **Where to run these workflows:** - - **IDE Extensions**: Use Continue in VS Code, JetBrains, or other supported IDEs + - **IDE Extensions**: Use Shadow Code in VS Code, JetBrains, or other supported IDEs - **Terminal (TUI mode)**: Run `cn` to enter interactive mode, then type your prompts - **CLI (headless mode)**: Use `cn -p "your prompt"` for headless commands @@ -246,22 +246,22 @@ Show me what columns were added or modified. ## Continuous Data Pipelines with GitHub Actions - This example demonstrates a **Continuous AI workflow** where data pipeline validation runs automatically in your CI/CD pipeline in headless mode using the [dlt Assistant agent](https://continue.dev/dlthub/dlt-assistant). Consider customizing this agent to add your organization's specific validation rules. + This example demonstrates a **Continuous AI workflow** where data pipeline validation runs automatically in your CI/CD pipeline in headless mode using the dlt Assistant agent. Consider customizing this agent to add your organization's specific validation rules. ### Add GitHub Secrets Navigate to **Repository Settings → Secrets and variables → Actions** and add: -- `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) +- `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys - Any required database credentials for your destination - The workflow uses the pre-built [dlt Agent](https://continue.dev/continuedev/dlt-agent) with `--agent continuedev/dlt-agent`. This agent comes pre-configured with the dlt MCP and optimized rules for pipeline operations. You can customize the validation rules and prompts for your specific pipeline requirements. + The workflow uses the pre-built dlt Agent with `--agent continuedev/dlt-agent`. This agent comes pre-configured with the dlt MCP and optimized rules for pipeline operations. You can customize the validation rules and prompts for your specific pipeline requirements. ### Create Workflow File -This workflow automatically validates your dlt data pipelines on pull requests using the Continue CLI in [headless mode](/cli/headless-mode). It inspects pipeline schemas, checks for errors, and posts a summary report as a PR comment. The workflow can also be triggered manually via `workflow_dispatch`. +This workflow automatically validates your dlt data pipelines on pull requests using the Shadow Code CLI in [headless mode](/cli/headless-mode). It inspects pipeline schemas, checks for errors, and posts a summary report as a PR comment. The workflow can also be triggered manually via `workflow_dispatch`. Create `.github/workflows/dlt-pipeline-validation.yml` in your repository: @@ -297,10 +297,10 @@ jobs: pip install dlt echo "✅ dlt installed" - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | npm install -g @continuedev/cli - echo "✅ Continue CLI installed" + echo "✅ Shadow Code CLI installed" - name: Validate Pipeline Schema run: | @@ -341,7 +341,7 @@ jobs: ## Pipeline Development Best Practices -Implement automated pipeline quality checks using Continue's rule system. See the [Rules deep dive](/customize/deep-dives/rules) for authoring tips. +Implement automated pipeline quality checks using Shadow Code's rule system. See the [Rules deep dive](/customize/deep-dives/rules) for authoring tips. ```bash @@ -427,15 +427,15 @@ After completing this guide, you have a complete **AI-powered data pipeline deve Complete dlt platform documentation - - Explore Continue documentation and guides + + Explore Shadow Code documentation and guides - Learn about AI agents, MCP, and Continue integration + Learn about AI agents, MCP, and Shadow Code integration @@ -48,19 +48,19 @@ This process utilizes the **Continue CLI** (`cn`) in **headless mode** to analyz ## Prerequisites - - Continue CLI requires Node.js 20 or higher. + + Shadow Code CLI requires Node.js 20 or higher. - - Get your API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) and set: + + Get your API key from continue.dev/settings/api-keys and set: ```bash export CONTINUE_API_KEY=your_key_here ``` - You can use the Continue CLI in headless mode without interactive login by setting the `CONTINUE_API_KEY` environment variable. + You can use the Shadow Code CLI in headless mode without interactive login by setting the `CONTINUE_API_KEY` environment variable. @@ -77,7 +77,7 @@ The documentation generation process follows these sequential steps: - Validate environment, install Continue CLI, and set up authentication with API keys. + Validate environment, install Shadow Code CLI, and set up authentication with API keys. @@ -89,7 +89,7 @@ The documentation generation process follows these sequential steps: - Use Continue CLI with custom rules to analyze changes and generate or update documentation files. + Use Shadow Code CLI with custom rules to analyze changes and generate or update documentation files. Use an agent configuration with rules specific for documentation writing in your project and fine-tune it to work for your team's standards. @@ -120,9 +120,9 @@ This example uses a manual workflow dispatch that requires two inputs: the repos **Required Inputs:** - **repository:** The repository you are operating on (format: `owner/repo`) - **branch_name:** The name of the branch you have code changes on and want to generate documentation for -- **continue_config:** The Continue agent configuration to use +- **continue_config:** The Shadow Code agent configuration to use - Consider setting a default value if you have a default config your'd like to use -- **continue_org:** The Continue org to use +- **continue_org:** The Shadow Code org to use - Consider setting a default value if you have a default org your'd like to use @@ -140,12 +140,12 @@ on: description: 'Branch name to generate docs for' required: true continue_config: - description: 'Continue agent configuration to use' + description: 'Shadow Code agent configuration to use' required: true # Set a default value if you have a default config your'd like to use # default: 'agent-config-name' continue_org: - description: 'Continue org to use' + description: 'Shadow Code org to use' required: true # Set a default value if you have a default org your'd like to use # default: 'your-org-name' @@ -183,14 +183,14 @@ jobs: with: node-version: '18' - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | - echo "Installing Continue CLI..." + echo "Installing Shadow Code CLI..." npm i -g @continuedev/cli - - name: Verify Continue CLI installation + - name: Verify Shadow Code CLI installation run: | - echo "Checking Continue CLI version..." + echo "Checking Shadow Code CLI version..." cn --version || exit 1 - name: Set branch name @@ -214,9 +214,9 @@ jobs: echo "Creating branch: ${{ steps.branch.outputs.branch_name }}" git checkout -b "${{ steps.branch.outputs.branch_name }}" - - name: Generate documentation with Continue CLI + - name: Generate documentation with Shadow Code CLI run: | - echo "Running Continue agent to generate documentation..." + echo "Running Shadow Code agent to generate documentation..." cn --config / \ --auto \ --allow Write \ @@ -252,7 +252,7 @@ jobs: Make sure to set your `CONTINUE_API_KEY` environment variable before running local scripts to enable headless mode. -When using the Continue CLI on your local machine, you can build workflows in various ways, one of which is by simply creating shell scripts that you can run, which call the CLI. +When using the Shadow Code CLI on your local machine, you can build workflows in various ways, one of which is by simply creating shell scripts that you can run, which call the CLI. The below shell script snippet shows the final part of a docs updating shell script that can be used to generate documentation for the code changes in a branch of a git repository. @@ -283,8 +283,8 @@ cat context.txt echo "Creating branch: $DOCS_BRANCH_NAME" git checkout -b "$DOCS_BRANCH_NAME" -# Generate documentation with Continue CLI -echo "Running Continue agent to generate documentation..." +# Generate documentation with Shadow Code CLI +echo "Running Shadow Code agent to generate documentation..." cn -config "$CONTINUE_ORG/$CONTINUE_CONFIG" \ --auto \ --allow Write \ @@ -344,11 +344,11 @@ The workflow above is a basic example and can be enhanced in various ways to fit ## Next Steps -Ready to implement automated documentation with Continue CLI? Here are some helpful resources to get you started: +Ready to implement automated documentation with Shadow Code CLI? Here are some helpful resources to get you started: - - Learn the fundamentals of using Continue CLI for automated coding tasks and headless workflows. + + Learn the fundamentals of using Shadow Code CLI for automated coding tasks and headless workflows. @@ -356,10 +356,10 @@ Ready to implement automated documentation with Continue CLI? Here are some help - Checkout this video from Tetrate about using Continue Agents to help with writing your docs. + Checkout this video from Tetrate about using Shadow Code Agents to help with writing your docs. - - Explore Continue documentation and guides. + + Explore Shadow Code documentation and guides. diff --git a/docs/guides/github-mcp-continue-cookbook.mdx b/docs/guides/github-mcp-continue-cookbook.mdx index 1c892f260ff..f16a32c6ae4 100644 --- a/docs/guides/github-mcp-continue-cookbook.mdx +++ b/docs/guides/github-mcp-continue-cookbook.mdx @@ -1,7 +1,7 @@ --- -title: "GitHub Issues and PRs with GitHub MCP and Continue" -description: "Use Continue and the GitHub MCP to list, summarize, and act on open issues and recently merged pull requests with natural language prompts." -sidebarTitle: "GitHub Issues with Continue" +title: "GitHub Issues and PRs with GitHub MCP and Shadow Code" +description: "Use Shadow Code and the GitHub MCP to list, summarize, and act on open issues and recently merged pull requests with natural language prompts." +sidebarTitle: "GitHub Issues with Shadow Code" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -10,7 +10,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - A GitHub workflow assistant that uses Continue with the GitHub MCP to: + A GitHub workflow assistant that uses Shadow Code with the GitHub MCP to: - List, filter, and summarize open issues - Review and summarize recently merged PRs - Post comments with AI-generated summaries or checklists @@ -29,7 +29,7 @@ Before starting, ensure you have: For all options, first: - + @@ -42,14 +42,14 @@ For all options, first: - To use agents in headless mode, you need a [Continue API key](https://continue.dev/settings/api-keys). + To use agents in headless mode, you need a Shadow Code API key. For write actions (e.g., posting comments), your token must include the relevant GitHub scopes. ## GitHub MCP Workflow Options - Use the GitHub MCP with Continue CLI for quick setup. + Use the GitHub MCP with Shadow Code CLI for quick setup. After ensuring you meet the **Prerequisites** above, you have two paths to get started: @@ -106,7 +106,7 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - Launch Continue and ask: + Launch Shadow Code and ask: ``` List the 5 most recently updated open issues in this repository. ``` @@ -116,8 +116,8 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - To use GitHub MCP with Continue CLI, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + To use GitHub MCP with Shadow Code CLI, you need either: + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration along with the GitHub MCP for issue and PR operations. @@ -132,7 +132,7 @@ Use natural language to explore, triage, and act on open issues. The agent calls **Where to run these workflows:** - - **IDE Extensions**: Use Continue in VS Code, JetBrains, or other supported IDEs + - **IDE Extensions**: Use Shadow Code in VS Code, JetBrains, or other supported IDEs - **Terminal (TUI mode)**: Run `cn` to enter interactive mode, then type your prompts - **CLI (headless mode)**: Use `cn -p "your prompt" --auto` for automation @@ -244,7 +244,7 @@ Run headless commands on a schedule or in PRs to keep teams informed. Repository Settings → Secrets and variables → Actions: -- `CONTINUE_API_KEY`: From [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) +- `CONTINUE_API_KEY`: From continue.dev/settings/api-keys - `GITHUB_TOKEN`: A token with permissions to read issues/PRs and post comments ### Example Workflow @@ -272,7 +272,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: "18" - - name: Install Continue CLI + - name: Install Shadow Code CLI run: npm i -g @continuedev/cli - name: Weekly Issue Triage Summary @@ -336,10 +336,10 @@ After completing this guide, you have a complete **AI-powered GitHub workflow sy Anthropic GitHub MCP server - - How MCP works with Continue agents + + How MCP works with Shadow Code agents - + Pre-configured agent for GitHub workflows diff --git a/docs/guides/github-pr-review-bot.mdx b/docs/guides/github-pr-review-bot.mdx index 6eee9c009e1..0aeb2574dad 100644 --- a/docs/guides/github-pr-review-bot.mdx +++ b/docs/guides/github-pr-review-bot.mdx @@ -1,6 +1,6 @@ --- -title: "Code Review Bot with Continue and GitHub Actions" -description: "Set up automated, context-aware pull request reviews using Continue CLI in GitHub Actions - privacy-first with custom rules" +title: "Code Review Bot with Shadow Code and GitHub Actions" +description: "Set up automated, context-aware pull request reviews using Shadow Code CLI in GitHub Actions - privacy-first with custom rules" sidebarTitle: "Pull Request Review Bot" --- @@ -17,17 +17,17 @@ sidebarTitle: "Pull Request Review Bot" - All logs and processing happen in your runner: Continue CLI runs in GitHub Actions → code to your LLM provider (OpenAI, Anthropic, etc.). No hosted Continue service reads your code. + All logs and processing happen in your runner: Shadow Code CLI runs in GitHub Actions → code to your LLM provider (OpenAI, Anthropic, etc.). No hosted Shadow Code service reads your code. - Define team-specific rules in `.continue/rules/` that automatically apply to every pull request. + Define team-specific rules in `.shadow-code/rules/` that automatically apply to every pull request. - Leverage Continue's AI agent for intelligent, context-aware reviews with full control over your configuration. + Leverage Shadow Code's AI agent for intelligent, context-aware reviews with full control over your configuration. @@ -37,15 +37,15 @@ sidebarTitle: "Pull Request Review Bot" Before starting, ensure you have: - A GitHub repository with pull requests -- A Continue account +- A Shadow Code account - Read: [Understanding Configs](/guides/understanding-configs) -- A Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) -- Continue assistant configured for code reviews (or use our recommended default) +- A Shadow Code API key from continue.dev/settings/api-keys +- Shadow Code assistant configured for code reviews (or use our recommended default) **Want to customize the review bot?** - You can remix the default review bot configuration at [continue.dev/continuedev/review-bot](https://continue.dev/continuedev/review-bot) to create your own personalized version with custom prompts, rules, and behaviors. + You can remix the default review bot configuration at continue.dev/continuedev/review-bot to create your own personalized version with custom prompts, rules, and behaviors. ## Quick Setup (10 Minutes) @@ -57,7 +57,7 @@ Before starting, ensure you have: Navigate to your repository settings: **Settings → Secrets and variables → Actions** **Required Secrets:** - - `CONTINUE_API_KEY` - Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) + - `CONTINUE_API_KEY` - Your Shadow Code API key from continue.dev/settings/api-keys **Optional (for better permissions):** - **Variables** tab: `APP_ID` - GitHub App ID (for enhanced API rate limits) @@ -87,7 +87,7 @@ Before starting, ensure you have: Create a GitHub Actions workflow file at `.github/workflows/code-review.yml` with the provided configuration. ```yaml -name: Continue Code Review +name: Shadow Code Code Review on: pull_request: @@ -127,7 +127,7 @@ jobs: with: node-version: '20' - - name: Install Continue CLI + - name: Install Shadow Code CLI run: npm i -g @continuedev/cli - name: Get Pull Request Details @@ -150,15 +150,15 @@ jobs: # Get changed files gh pr view $PR_NUMBER --json files -q '.files[].path' > changed_files.txt - - name: Run Continue Review + - name: Run Shadow Code Review env: CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} GH_TOKEN: ${{ steps.app-token.outputs.token || github.token }} run: | # Check if custom rules exist - if [ -d ".continue/rules" ]; then - echo "📋 Found custom rules in .continue/rules/" - RULES_CONTEXT="Apply the custom rules found in .continue/rules/ directory." + if [ -d ".shadow-code/rules" ]; then + echo "📋 Found custom rules in .shadow-code/rules/" + RULES_CONTEXT="Apply the custom rules found in .shadow-code/rules/ directory." else echo "ℹ️ No custom rules found. Using general best practices." RULES_CONTEXT="Review for general best practices, security issues, and code quality." @@ -186,7 +186,7 @@ jobs: Format as markdown suitable for a GitHub pull request comment." - # Run Continue CLI in headless mode + # Run Shadow Code CLI in headless mode cn --config continuedev/review-bot \ -p "$PROMPT" \ --auto > review_output.md @@ -208,7 +208,7 @@ jobs: cat >> review_comment.md <<'EOF' --- - *Powered by [Continue](https://continue.dev) • Need a focused review? Comment `@review-bot check for [specific concern]`* + *Powered by [Shadow Code]() • Need a focused review? Comment `@review-bot check for [specific concern]`* EOF # Check for existing review comment @@ -230,12 +230,12 @@ jobs: -Define your team's standards in `.continue/rules/`: +Define your team's standards in `.shadow-code/rules/`: - Create `.continue/rules/security.md`: + Create `.shadow-code/rules/security.md`: ```markdown --- @@ -255,7 +255,7 @@ alwaysApply: true ``` - Create `.continue/rules/typescript.md`: + Create `.shadow-code/rules/typescript.md`: ```markdown --- @@ -274,7 +274,7 @@ alwaysApply: true ``` -Create `.continue/rules/testing.md`: +Create `.shadow-code/rules/testing.md`: ```markdown --- @@ -293,7 +293,7 @@ description: "Testing Requirements" ``` -Create `.continue/rules/python.md`: +Create `.shadow-code/rules/python.md`: ```markdown --- @@ -320,9 +320,9 @@ The workflow follows these steps: 1. **Pull Request Created/Updated** - A pull request is opened or synchronized 2. **Workflow Triggered** - GitHub Actions workflow starts automatically -3. **Load Custom Rules** - Reads your team's rules from `.continue/rules/` +3. **Load Custom Rules** - Reads your team's rules from `.shadow-code/rules/` 4. **Get Pull Request Diff** - Fetches the diff and list of changed files -5. **Continue CLI Analyzes Code** - AI agent reviews the code with your rules +5. **Shadow Code CLI Analyzes Code** - AI agent reviews the code with your rules 6. **Post or Update Review Comment** - Creates or updates a single PR comment with feedback ## Interactive Commands @@ -340,12 +340,12 @@ The workflow will respond with a targeted review based on your request. ## Advanced Configuration - + By default, the workflow uses the `continuedev/review-bot` config optimized for code reviews. Replace `continuedev/review-bot` with your own config: ```yaml - - name: Run Continue Review + - name: Run Shadow Code Review env: CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} CONTINUE_ORG: your-org-name # Add your org @@ -395,7 +395,7 @@ The workflow will respond with a targeted review based on your request. echo "skip=false" >> $GITHUB_OUTPUT fi - - name: Run Continue Review + - name: Run Shadow Code Review if: steps.size-check.outputs.skip != 'true' # ... rest of review step ``` @@ -404,17 +404,17 @@ The workflow will respond with a targeted review based on your request. ## Troubleshooting - + - The workflow installs the CLI automatically, but ensure Node.js 20+ is available -- Check the "Install Continue CLI" step logs for errors +- Check the "Install Shadow Code CLI" step logs for errors - Verify your `CONTINUE_API_KEY` is valid - Check that GitHub token has required permissions -- Verify your Continue config is accessible -- Check Continue CLI logs in the workflow run +- Verify your Shadow Code config is accessible +- Check Shadow Code CLI logs in the workflow run - Try running locally: `cn -p "Test prompt" --auto` @@ -452,13 +452,13 @@ Here's what a typical review comment looks like: > - Proper async/await usage throughout > > ### Recommendations -> 1. Move `secretKey` to environment variables (see `.continue/rules/security.md`) +> 1. Move `secretKey` to environment variables (see `.shadow-code/rules/security.md`) > 2. Add rate limiting middleware to prevent brute force attacks > 3. Consider adding integration tests for the auth flow > 4. Document the JWT payload structure > > --- -> *Powered by [Continue](https://continue.dev) • Need a focused review? Comment `@review-bot check for security`* +> *Powered by [Shadow Code]() • Need a focused review? Comment `@review-bot check for security`* ## What You've Built @@ -483,7 +483,7 @@ After completing this setup, you have an **AI-powered code review system** that: 2. **Refine rules** - Add more custom rules specific to your codebase 3. **Customize prompts** - Adjust the review prompt to match your team's style 4. **Add metrics** - Track review effectiveness over time -5. **Create team config** - Set up a shared Continue config for consistent reviews +5. **Create team config** - Set up a shared Shadow Code config for consistent reviews ## Inspiration & Resources @@ -491,8 +491,8 @@ After completing this setup, you have an **AI-powered code review system** that: Original inspiration - Privacy-first AI code reviews - - Learn more about Continue CLI capabilities + + Learn more about Shadow Code CLI capabilities Deep dive into custom rules diff --git a/docs/guides/how-to-self-host-a-model.mdx b/docs/guides/how-to-self-host-a-model.mdx index a36745b90d1..6149a91dcfd 100644 --- a/docs/guides/how-to-self-host-a-model.mdx +++ b/docs/guides/how-to-self-host-a-model.mdx @@ -1,6 +1,6 @@ --- title: "How to Self-Host a Model" -description: "Learn how to deploy and self-host open-source language models using HuggingFace TGI, vLLM, SkyPilot, Anyscale Private Endpoints, or Lambda for use with Continue" +description: "Learn how to deploy and self-host open-source language models using HuggingFace TGI, vLLM, SkyPilot, Anyscale Private Endpoints, or Lambda for use with Shadow Code" --- - [HuggingFace TGI](https://github.com/continuedev/deploy-os-code-llm#tgi) @@ -11,7 +11,7 @@ description: "Learn how to deploy and self-host open-source language models usin ## How to Self-Host an Open-Source Model -For many cases, either Continue will have a built-in provider or the API you use will be OpenAI-compatible, in which case you can use the "openai" provider and change the "baseUrl" to point to the server. +For many cases, either Shadow Code will have a built-in provider or the API you use will be OpenAI-compatible, in which case you can use the "openai" provider and change the "baseUrl" to point to the server. However, if neither of these are the case, you will need to wire up a new LLM object. diff --git a/docs/guides/instinct.mdx b/docs/guides/instinct.mdx index b05e6bbb649..d2c4b366c74 100644 --- a/docs/guides/instinct.mdx +++ b/docs/guides/instinct.mdx @@ -1,6 +1,6 @@ --- -title: "Using Instinct with Ollama in Continue" -description: "Learn how to run Instinct, Continue's leading open Next Edit model, on your own hardware with Ollama" +title: "Using Instinct with Ollama in Shadow Code" +description: "Learn how to run Instinct, Shadow Code's leading open Next Edit model, on your own hardware with Ollama" --- @@ -36,4 +36,4 @@ models: - uses: continuedev/instinct ``` -Alternatively, you can just click to add the block at https://continue.dev/continuedev/instinct. +Alternatively, you can just click to add the block at diff --git a/docs/guides/netlify-mcp-continuous-deployment.mdx b/docs/guides/netlify-mcp-continuous-deployment.mdx index 86348ffb8a7..69178fdfa93 100644 --- a/docs/guides/netlify-mcp-continuous-deployment.mdx +++ b/docs/guides/netlify-mcp-continuous-deployment.mdx @@ -26,7 +26,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - [Identity](https://docs.netlify.com/visitor-access/identity/) for user authentication - [Large Media](https://docs.netlify.com/large-media/overview/) for Git LFS support -This guide shows you how to leverage these features through natural language with Continue CLI! +This guide shows you how to leverage these features through natural language with Shadow Code CLI! @@ -44,7 +44,7 @@ This cookbook teaches you to: - GitHub repository with a web project - [Netlify account](https://netlify.com) (free tier works) - Node.js 22+ installed (required for Netlify) -- [Continue CLI](https://docs.continue.dev/guides/cli) +- Shadow Code CLI - Netlify MCP configured - Netlify Development Rules (recommended) @@ -60,7 +60,7 @@ This cookbook teaches you to: For all options, first: - + @@ -154,7 +154,7 @@ After completing **Quick Setup** above, you have two paths to get started: To use the pre-built agent, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration along with the Netlify MCP for deployment operations. @@ -456,7 +456,7 @@ Unlike Google Analytics, Netlify Analytics: Navigate to **Repository Settings → Secrets and variables → Actions** and add: -- `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) +- `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys - `NETLIFY_AUTH_TOKEN`: Your Netlify personal access token - `NETLIFY_SITE_ID`: Your Netlify site ID @@ -479,17 +479,17 @@ jobs: with: node-version: "22" - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | npm install -g @continuedev/cli - echo "✅ Continue CLI installed" + echo "✅ Shadow Code CLI installed" - - name: Authenticate Continue CLI + - name: Authenticate Shadow Code CLI env: CONTINUE_API_KEY: ${{ secrets.CONTINUE_API_KEY }} run: | cn auth login --api-key "$CONTINUE_API_KEY" - echo "✅ Continue CLI authenticated" + echo "✅ Shadow Code CLI authenticated" - name: Deploy and Test Performance id: perf @@ -702,7 +702,7 @@ The Netlify Performance Rules enforce: ## Next Steps -- Install the Netlify MCP in your Continue configuration +- Install the Netlify MCP in your Shadow Code configuration - Set up [Performance Monitoring](https://docs.netlify.com/analytics/get-started/) - Configure [A/B Testing](https://docs.netlify.com/split-testing/overview/) @@ -711,4 +711,4 @@ The Netlify Performance Rules enforce: - [Netlify MCP](https://docs.netlify.com/) - [Core Web Vitals Guide](https://web.dev/vitals/) - [Netlify Analytics Documentation](https://docs.netlify.com/analytics/) -- [Continue Performance Guides](https://docs.continue.dev/guides) +- Shadow Code Performance Guides diff --git a/docs/guides/notion-continue-guide.mdx b/docs/guides/notion-continue-guide.mdx index 3cbba1da65a..3d7f387a698 100644 --- a/docs/guides/notion-continue-guide.mdx +++ b/docs/guides/notion-continue-guide.mdx @@ -1,7 +1,7 @@ --- -title: "Developer & Team Workflows with Notion + Continue CLI" -description: "Use Continue CLI with Notion to generate docs, manage tasks, and automate project workflows – all through natural-language prompts." -sidebarTitle: "Notion with Continue" +title: "Developer & Team Workflows with Notion + Shadow Code CLI" +description: "Use Shadow Code CLI with Notion to generate docs, manage tasks, and automate project workflows – all through natural-language prompts." +sidebarTitle: "Notion with Shadow Code" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -21,7 +21,7 @@ import CLIInstall from '/snippets/cli-install.mdx' ## What You'll Learn This guide teaches you to: -- Use natural language to connect to the Notion API directly with Continue CLI for powerful automation +- Use natural language to connect to the Notion API directly with Shadow Code CLI for powerful automation - Configure Notion API access with proper permissions and security - Run prompts in both TUI (interactive) and headless modes - Create automated workflows that generate docs, manage tasks, and sync data @@ -30,21 +30,21 @@ This guide teaches you to: Before starting, ensure you have: -- [Continue CLI](https://docs.continue.dev/cli/quickstart) installed +- Shadow Code CLI installed - A [Notion workspace](https://notion.so) with Editor (or higher) access - Node.js 18+ installed locally -- [Continue account](https://continue.dev) +- [Shadow Code account]() - **Agent usage requires credits** – create a Continue API key at - [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) + **Agent usage requires credits** – create a Shadow Code API key at + continue.dev/settings/api-keys and store it as a secret. - + Verify installation: @@ -55,7 +55,7 @@ Before starting, ensure you have: 1. Go to **[Notion Integrations](https://www.notion.so/my-integrations)** - 2. Click **+ New integration** → give it a name (e.g. "Continue Integration") + 2. Click **+ New integration** → give it a name (e.g. "Shadow Code Integration") 3. Select your workspace 4. Under **Content Capabilities**, enable: - ✅ Read content @@ -116,11 +116,11 @@ Before starting, ensure you have: -## Running Continue CLI with Notion API +## Running Shadow Code CLI with Notion API - Continue CLI offers two powerful modes for Notion automation: + Shadow Code CLI offers two powerful modes for Notion automation: **TUI mode** for interactive workflows and **Headless mode** for automated scripts. @@ -185,10 +185,10 @@ Before starting, ensure you have: - - Environment variable `NOTION_API_KEY` must be set before running Continue CLI - - Continue automatically uses the API key to authenticate with Notion + - Environment variable `NOTION_API_KEY` must be set before running Shadow Code CLI + - Shadow Code automatically uses the API key to authenticate with Notion - No need for manual curl commands - just reference "the API key stored in this session" - - For complex workflows, Continue maintains the API connection throughout + - For complex workflows, Shadow Code maintains the API connection throughout - Consider creating aliases or scripts for frequently used prompts @@ -197,7 +197,7 @@ Before starting, ensure you have: ## Quick Start Example -Working example that demonstrates the power of Continue with Notion API: +Working example that demonstrates the power of Shadow Code with Notion API: @@ -368,7 +368,7 @@ With the Notion API configured, you can use natural language prompts to automate - Ensure the integration has the correct permissions (Read, Write, Insert) **Connection Issues:** - - Verify Continue CLI has internet access + - Verify Shadow Code CLI has internet access - Check network connectivity to api.notion.com - Ensure your Notion workspace allows API access diff --git a/docs/guides/ollama-guide.mdx b/docs/guides/ollama-guide.mdx index 20791c59481..c7e19188b77 100644 --- a/docs/guides/ollama-guide.mdx +++ b/docs/guides/ollama-guide.mdx @@ -1,6 +1,6 @@ --- -title: "Using Ollama with Continue: A Developer's Guide" -description: "Complete guide to setting up Ollama with Continue for local AI development. Learn installation, configuration, model selection, performance optimization, and troubleshooting for privacy-focused offline coding assistance" +title: "Using Ollama with Shadow Code: A Developer's Guide" +description: "Complete guide to setting up Ollama with Shadow Code for local AI development. Learn installation, configuration, model selection, performance optimization, and troubleshooting for privacy-focused offline coding assistance" --- ## What Are the Prerequisites for Using Ollama @@ -10,7 +10,7 @@ Before getting started, ensure your system meets these requirements: - Operating System: macOS, Linux, or Windows - RAM: Minimum 8GB (16GB+ recommended) - Storage: At least 10GB free space -- Continue extension installed +- Shadow Code extension installed ## How to Install Ollama - Step-by-Step @@ -78,15 +78,15 @@ ollama list a different size. -## How to Configure Ollama with Continue +## How to Configure Ollama with Shadow Code -There are multiple ways to configure Ollama models in Continue: +There are multiple ways to configure Ollama models in Shadow Code: ### Method 1: Using Model Blocks in config.yaml The easiest way is to use [pre-configured model blocks](/reference#models) in your configuration: -```yaml title="~/.continue/configs/config.yaml" +```yaml title="~/.shadow-code/configs/config.yaml" name: My Local Config version: 0.0.1 schema: v1 @@ -98,7 +98,7 @@ models: **Important**: Blocks only provide configuration - you still need to pull - the model locally. The block `ollama/deepseek-r1-32b` configures Continue + the model locally. The block `ollama/deepseek-r1-32b` configures Shadow Code to use `model: deepseek-r1:32b`, but the actual model must be installed: ```bash # Check what the block expects (view on continue.dev) @@ -111,9 +111,9 @@ models: ### Method 2: Using Autodetect -Continue can automatically detect available Ollama models. You can configure this in your YAML: +Shadow Code can automatically detect available Ollama models. You can configure this in your YAML: -```yaml title="~/.continue/config.yaml" +```yaml title="~/.shadow-code/config.yaml" models: - name: Autodetect provider: ollama @@ -130,12 +130,12 @@ Or use it through the GUI: 1. Click on the model selector dropdown 2. Select "Autodetect" option -3. Continue will scan for available Ollama models +3. Shadow Code will scan for available Ollama models 4. Select your desired model from the detected list The Autodetect feature scans your local Ollama installation and lists all - available models. When set to `AUTODETECT`, Continue will dynamically populate + available models. When set to `AUTODETECT`, Shadow Code will dynamically populate the model list based on what's installed locally via `ollama list`. This is useful for quickly switching between models without manual configuration. For any roles not covered by the detected models, you may need to manually @@ -274,7 +274,7 @@ To get the best performance from Ollama: #### "Model requires more system memory to run" -Continue may use a higher default context length than other tools. Reduce `contextLength` in your config (e.g., to 2048), or try a smaller model. See [Ollama provider troubleshooting](/customize/model-providers/top-level/ollama#troubleshooting) for details. +Shadow Code may use a higher default context length than other tools. Reduce `contextLength` in your config (e.g., to 2048), or try a smaller model. See [Ollama provider troubleshooting](/customize/model-providers/top-level/ollama#troubleshooting) for details. #### "404 model not found, try pulling it first" @@ -319,7 +319,7 @@ ollama pull deepseek-r1:32b **Solution**: Create a config file: ```yaml -# ~/.continue/configs/config.yaml +# ~/.shadow-code/configs/config.yaml name: Local Config version: 0.0.1 schema: v1 @@ -360,14 +360,14 @@ class User(BaseModel): @app.post("/users/") async def create_user(user: User): - # Continue will help complete this implementation + # Shadow Code will help complete this implementation # Use Cmd+I (Mac) or Ctrl+I (Windows/Linux) to generate code pass ``` ### How to Use Ollama for Code Review -Use Continue with Ollama to: +Use Shadow Code with Ollama to: - Analyze code quality - Suggest improvements @@ -376,8 +376,8 @@ Use Continue with Ollama to: ## Conclusion -Ollama with Continue provides a powerful local development environment for AI-assisted coding. You now have complete control over your AI models, ensuring privacy and enabling offline development workflows. +Ollama with Shadow Code provides a powerful local development environment for AI-assisted coding. You now have complete control over your AI models, ensuring privacy and enabling offline development workflows. --- -_This guide is based on Ollama v0.11.x and Continue v1.1.x. Please check for updates regularly._ +_This guide is based on Ollama v0.11.x and Shadow Code v1.1.x. Please check for updates regularly._ diff --git a/docs/guides/overview.mdx b/docs/guides/overview.mdx index b336acf68cc..61623c3084e 100644 --- a/docs/guides/overview.mdx +++ b/docs/guides/overview.mdx @@ -1,27 +1,27 @@ --- -title: "How to Use Continue Guides" -description: "Comprehensive collection of practical guides for Continue including model setup, local development with Ollama, offline usage, self-hosting, custom context providers, and advanced customization tutorials" +title: "How to Use Shadow Code Guides" +description: "Comprehensive collection of practical guides for Shadow Code including model setup, local development with Ollama, offline usage, self-hosting, custom context providers, and advanced customization tutorials" --- ## What Model & Setup Guides Are Available -- [Using Ollama with Continue](/guides/ollama-guide) - Local AI development with Ollama +- [Using Ollama with Shadow Code](/guides/ollama-guide) - Local AI development with Ollama - [How to Self-Host a Model](/guides/how-to-self-host-a-model) - Self-hosting AI models -- [Running Continue Without Internet](/guides/running-continue-without-internet) - Offline development setup +- [Running Shadow Code Without Internet](/guides/running-continue-without-internet) - Offline development setup ## CLI & Automation -- [How to Use Continue CLI (cn)](/guides/cli) - Command-line interface for Continue -- [Pull Request Review Bot with GitHub Actions](/guides/github-pr-review-bot) - Set up automated, privacy-first code reviews using Continue CLI +- [How to Use Shadow Code CLI (cn)](/guides/cli) - Command-line interface for Shadow Code +- [Pull Request Review Bot with GitHub Actions](/guides/github-pr-review-bot) - Set up automated, privacy-first code reviews using Shadow Code CLI ## MCP Integration Cookbooks -Step-by-step guides for integrating Model Context Protocol (MCP) servers with Continue: +Step-by-step guides for integrating Model Context Protocol (MCP) servers with Shadow Code: - - Use the Continue Docs MCP to write cookbooks, guides, and documentation with AI-powered workflows + + Use the Shadow Code Docs MCP to write cookbooks, guides, and documentation with AI-powered workflows @@ -73,4 +73,4 @@ Step-by-step guides for integrating Model Context Protocol (MCP) servers with Co ## How to Contribute to Guides -Have a guide idea or found an issue? We welcome contributions! Check our [GitHub repository](https://github.com/continuedev/continue) to get involved. +Have a guide idea or found an issue? We welcome contributions! Check our GitHub repository to get involved. diff --git a/docs/guides/plan-mode-guide.mdx b/docs/guides/plan-mode-guide.mdx index 289efe38ad2..1089a3364c0 100644 --- a/docs/guides/plan-mode-guide.mdx +++ b/docs/guides/plan-mode-guide.mdx @@ -1,5 +1,5 @@ --- -title: "Using Plan Mode with Continue" +title: "Using Plan Mode with Shadow Code" description: "Plan Mode gives you a safe, read-only environment to explore your codebase, map out solutions, and collaborate with AI before making any changes. Think of it as your sandbox for understanding and strategy." --- @@ -106,7 +106,7 @@ You can switch to `Plan` in the mode selector below the chat input box. ## How Context Integration Works in Plan Mode -Context is the foundation of effective planning. Without proper context, AI models fall back on generic patterns, leading to plans that don't fit your specific system. Continue's [context system](/ide-extensions/chat/context-selection) transforms broad suggestions into actionable strategies: +Context is the foundation of effective planning. Without proper context, AI models fall back on generic patterns, leading to plans that don't fit your specific system. Shadow Code's [context system](/ide-extensions/chat/context-selection) transforms broad suggestions into actionable strategies: | Context Type | Usage | Best For | | :------------------- | :----------------------------------------------------- | :---------------------------- | diff --git a/docs/guides/posthog-github-continuous-ai.mdx b/docs/guides/posthog-github-continuous-ai.mdx index fb85b8d2c7c..adbed074ffa 100644 --- a/docs/guides/posthog-github-continuous-ai.mdx +++ b/docs/guides/posthog-github-continuous-ai.mdx @@ -1,7 +1,7 @@ --- title: "Building a Continuous AI Workflow with PostHog and GitHub" description: "Build an automated system that continuously monitors PostHog analytics, analyzes user behavior with AI, and creates GitHub issues automatically using PostHog MCP." -sidebarTitle: "PostHog Analytics with Continue CLI" +sidebarTitle: "PostHog Analytics with Shadow Code CLI" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -10,7 +10,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - A fully automated workflow that uses Continue CLI with the PostHog MCP to fetch analytics data, analyze user experience issues with AI, and automatically create GitHub + A fully automated workflow that uses Shadow Code CLI with the PostHog MCP to fetch analytics data, analyze user experience issues with AI, and automatically create GitHub issues with the GitHub CLI. @@ -31,36 +31,36 @@ Before starting, ensure you have: - GitHub repository where you want to create issues - [PostHog account](https://posthog.com) with [session recordings enabled](https://posthog.com/docs/session-replay/installation) and data collecting - Node.js 18+ installed locally -- [Continue CLI](https://docs.continue.dev/guides/cli) with **active credits** (required for API usage) +- Shadow Code CLI with **active credits** (required for API usage) - [GitHub CLI](https://cli.github.com/) installed (`gh` command) - + - - 1. Visit [Continue Organizations](https://continue.dev/settings/organizations) - 2. Sign up or log in to your Continue account + + 1. Visit Shadow Code Organizations + 2. Sign up or log in to your Shadow Code account 3. Navigate to your organization settings 4. Click **"API Keys"** and then **"+ New API Key"** 5. Copy the API key immediately (you won't see it again!) 6. Login to the CLI: `cn login` - - Continue CLI will securely store your API keys as secrets that can be referenced in prompts. + + Shadow Code CLI will securely store your API keys as secrets that can be referenced in prompts. - Continue CLI handles the complex API interactions - you just need to provide + Shadow Code CLI handles the complex API interactions - you just need to provide the right prompts! ## Step 1: Set Up Your Credentials -First, you'll need to gather your PostHog and GitHub API credentials and add them as secrets in Continue CLI. +First, you'll need to gather your PostHog and GitHub API credentials and add them as secrets in Shadow Code CLI. @@ -68,7 +68,7 @@ First, you'll need to gather your PostHog and GitHub API credentials and add the 1. Go to [Personal API Keys](https://app.posthog.com/settings/user-api-keys) in PostHog 2. Click **+ Create a personal API Key** - 3. Name it "Continue CLI Session Analysis" + 3. Name it "Shadow Code CLI Session Analysis" 4. Select these scopes: - `session_recording:read` - **Required** for accessing session data - `feature_flag:read` - **Required** for feature flag auditing @@ -81,8 +81,8 @@ First, you'll need to gather your PostHog and GitHub API credentials and add the 8. You'll also need your POSTHOG_AUTH_HEADER value, which is simply `Bearer YOUR_API_KEY` - **Continue Secrets**: The `POSTHOG_AUTH_HEADER` secret should be stored in - Continue's secure secrets storage. This keeps your API key safe and the MCP + **Shadow Code Secrets**: The `POSTHOG_AUTH_HEADER` secret should be stored in + Shadow Code's secure secrets storage. This keeps your API key safe and the MCP automatically connects to your default PostHog project. @@ -96,7 +96,7 @@ First, you'll need to gather your PostHog and GitHub API credentials and add the 4. Grant necessary permissions when prompted (`issues:write` is **required** for creating issues) - + You only need to configure the PostHog MCP credential - it automatically handles project selection. Set your PostHog API key as an environment variable: @@ -164,16 +164,16 @@ Replace `YOUR_API_KEY` with your Personal API Key from PostHog (phx_...). - + Add the PostHog MCP to your [local config](/reference#mcpservers). Add PostHog GitHub Continuous AI rules to your configuration: 1. Pass `--rule bekah-hawrot-weigel/posthog-github-continuous-ai-rules` to `cn` OR - 2. Copy the rules to your local `.continue/rules` folder. See the [Rules Guide](/customize/deep-dives/rules#how-to-create-rules). + 2. Copy the rules to your local `.shadow-code/rules` folder. See the [Rules Guide](/customize/deep-dives/rules#how-to-create-rules). - Use this prompt with Continue CLI to analyze PostHog data and create GitHub issues: + Use this prompt with Shadow Code CLI to analyze PostHog data and create GitHub issues: ```bash @@ -200,7 +200,7 @@ Replace `YOUR_API_KEY` with your Personal API Key from PostHog (phx_...). To use the pre-built agent, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration. @@ -220,7 +220,7 @@ Create missing labels in your repo at: **Settings → Labels → New label** - **What Continue CLI Does:** + **What Shadow Code CLI Does:** - Parses your analysis results automatically - Makes authenticated GitHub API calls using your stored token - Creates properly formatted issues with appropriate labels @@ -258,7 +258,7 @@ After completing this guide, you have a complete **Continuous AI system** that: **Protect Your API Keys:** - Store all credentials as GitHub Secrets, never in code - - Use Continue CLI's secure secret storage + - Use Shadow Code CLI's secure secret storage - Limit token scopes to minimum required permissions - Rotate API keys regularly (every 90 days recommended) - Monitor token usage for unusual activity @@ -267,7 +267,7 @@ After completing this guide, you have a complete **Continuous AI system** that: ## Example Use Cases -Here are practical examples of what you can build with PostHog MCP and Continue CLI: +Here are practical examples of what you can build with PostHog MCP and Shadow Code CLI: ### Session Recording Analysis (Current Implementation) @@ -285,7 +285,7 @@ The main workflow above focuses on analyzing session recordings to identify UX i - Identifies flags that may be candidates for removal or updates - Creates GitHub issues for flag cleanup tasks -**Example Continue CLI prompts:** +**Example Shadow Code CLI prompts:** ```bash # Get all feature flags and analyze them @@ -317,7 +317,7 @@ This workflow creates GitHub issues like: ### Advanced Prompts -Consider enhancing your workflow with these advanced Continue CLI prompts: +Consider enhancing your workflow with these advanced Shadow Code CLI prompts: @@ -347,5 +347,5 @@ Consider enhancing your workflow with these advanced Continue CLI prompts: - [PostHog Feature Flags](https://posthog.com/docs/feature-flags) - [PostHog Error Tracking](https://posthog.com/docs/error-tracking) - [GitHub CLI Documentation](https://cli.github.com/) -- [Continue CLI Guide](https://docs.continue.dev/guides/cli) +- Shadow Code CLI Guide - [Continuous AI Best Practices](https://blog.continue.dev/what-is-continuous-ai-a-developers-guide/) diff --git a/docs/guides/running-continue-without-internet.mdx b/docs/guides/running-continue-without-internet.mdx index 98e80b97476..1dac69103c1 100644 --- a/docs/guides/running-continue-without-internet.mdx +++ b/docs/guides/running-continue-without-internet.mdx @@ -1,9 +1,9 @@ --- -title: "How to Run Continue Without Internet" -description: "Learn how to set up Continue for air-gapped or offline environments using local models, including steps to disable telemetry and configure local model providers" +title: "How to Run Shadow Code Without Internet" +description: "Learn how to set up Shadow Code for air-gapped or offline environments using local models, including steps to disable telemetry and configure local model providers" --- -1. Download the latest .vsix file from the [GitHub Releases page](https://github.com/continuedev/continue/releases) and [install it to VS Code](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix). -2. Turn off "Allow Anonymous Telemetry" in the user settings. This will stop Continue from attempting requests to PostHog for [anonymous telemetry](https://docs.continue.dev/reference/telemetry). -3. In your `config.yaml` file (or through the Continue UI), set the default model to a local model. You can find available local model options [here](https://docs.continue.dev/reference/model-providers/ollama). +1. Download the latest .vsix file from the GitHub Releases page and [install it to VS Code](https://code.visualstudio.com/docs/editor/extension-marketplace#_install-from-a-vsix). +2. Turn off "Allow Anonymous Telemetry" in the user settings. This will stop Shadow Code from attempting requests to PostHog for anonymous telemetry. +3. In your `config.yaml` file (or through the Shadow Code UI), set the default model to a local model. You can find available local model options here. 4. Restart VS Code to ensure that the changes to `config.yaml` take effect. diff --git a/docs/guides/sanity-mcp-continue-cookbook.mdx b/docs/guides/sanity-mcp-continue-cookbook.mdx index 7595f4c30aa..79ef63d97cf 100644 --- a/docs/guides/sanity-mcp-continue-cookbook.mdx +++ b/docs/guides/sanity-mcp-continue-cookbook.mdx @@ -1,7 +1,7 @@ --- -title: "Content Management with Sanity MCP and Continue" +title: "Content Management with Sanity MCP and Shadow Code" description: "Set up an AI-powered content management workflow that helps you manage schemas, run GROQ queries, handle documentation, and perform migrations using natural language commands." -sidebarTitle: "Sanity CMS with Continue" +sidebarTitle: "Sanity CMS with Shadow Code" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -10,7 +10,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - An AI-powered content management system workflow that uses Continue's AI agent with Sanity + An AI-powered content management system workflow that uses Shadow Code's AI agent with Sanity MCP to manage schemas, execute GROQ queries, handle migrations, and maintain documentation - all through simple natural language prompts @@ -24,7 +24,7 @@ Before starting, ensure you have: For all options, first: - + @@ -35,7 +35,7 @@ For all options, first: - To use agents in headless mode, you need a [Continue API key](https://continue.dev/settings/api-keys) and proper environment variable configuration. + To use agents in headless mode, you need a Shadow Code API key and proper environment variable configuration. ## Getting Started with Sanity @@ -80,7 +80,7 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s ``` This agent includes: - - **[Sanity MCP](https://continue.dev/sanity/sanity-mcp)** pre-configured and ready to use + - **Sanity MCP** pre-configured and ready to use - **Content management rules** for best practices - **Schema optimization** guidelines @@ -104,14 +104,14 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - **Why Use the Agent?** The pre-built Sanity Assistant agent provides consistent content management workflows and handles MCP configuration automatically, making it easier to get started with AI-powered CMS operations. Results are more consistent and debugging is easier thanks to the [Sanity MCP](https://continue.dev/sanity/sanity-mcp) integration and pre-tested prompts. + **Why Use the Agent?** The pre-built Sanity Assistant agent provides consistent content management workflows and handles MCP configuration automatically, making it easier to get started with AI-powered CMS operations. Results are more consistent and debugging is easier thanks to the Sanity MCP integration and pre-tested prompts. - + Add the Sanity MCP to your configuration: @@ -119,7 +119,7 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s 1. **Quick CLI install**: `cn --mcp sanity/sanity-mcp` 2. **Manual configuration**: Add the MCP to your agent configuration - Once installed, Sanity MCP tools become available to your Continue agent for all prompts. + Once installed, Sanity MCP tools become available to your Shadow Code agent for all prompts. @@ -149,7 +149,7 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s To use the pre-built Sanity Assistant agent, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration along with the pre-configured Sanity MCP for content operations. Note that OAuth authentication will be required on first use. @@ -208,11 +208,11 @@ With everything set up, you're ready for your first AI-powered content conversat ## Content Management Recipes -Now you can use natural language prompts to manage your Sanity content and schemas. The Continue agent automatically calls the appropriate Sanity MCP tools. +Now you can use natural language prompts to manage your Sanity content and schemas. The Shadow Code agent automatically calls the appropriate Sanity MCP tools. **Where to run these workflows:** - - **IDE Extensions**: Use Continue in VS Code, JetBrains, or other supported IDEs + - **IDE Extensions**: Use Shadow Code in VS Code, JetBrains, or other supported IDEs - **Terminal (TUI mode)**: Run `cn` to enter interactive mode, then type your prompts - **CLI (headless mode)**: Use `cn -p "your prompt"` for headless commands @@ -342,19 +342,19 @@ suggest optimizations to improve response times. Navigate to **Repository Settings → Secrets and variables → Actions** and add: -- `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) +- `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys - `SANITY_PROJECT_ID`: Your Sanity project ID - `SANITY_DATASET`: Your Sanity dataset name (usually "production") - `SANITY_API_TOKEN`: Your Sanity API token with appropriate permissions - `MCP_USER_ROLE`: Your MCP user role (typically "admin" or "editor") - The workflow uses the [Sanity Assistant Agent](https://continue.dev/continuedev/sanity-assistant-agent) with environment variable authentication via [Sanity MCP Config](https://continue.dev/sanity/sanity-mcp-config). This enables headless mode operation without OAuth browser authentication. + The workflow uses the Sanity Assistant Agent with environment variable authentication via Sanity MCP Config. This enables headless mode operation without OAuth browser authentication. ### Create Workflow File -This workflow automatically validates your Sanity schemas and content on pull requests using the Continue CLI in [headless mode](/cli/headless-mode). It checks schema integrity, validates content relationships, and posts a summary report as a PR comment. +This workflow automatically validates your Sanity schemas and content on pull requests using the Shadow Code CLI in [headless mode](/cli/headless-mode). It checks schema integrity, validates content relationships, and posts a summary report as a PR comment. Create `.github/workflows/sanity-content-validation.yml` in your repository: @@ -389,10 +389,10 @@ jobs: npm install -g @sanity/cli echo "✅ Sanity CLI installed" - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | npm install -g @continuedev/cli - echo "✅ Continue CLI installed" + echo "✅ Shadow Code CLI installed" - name: Validate Schema Structure run: | @@ -435,12 +435,12 @@ jobs: ``` - Environment variables enable the MCP to authenticate without OAuth browser prompts. The [Sanity MCP Config](https://continue.dev/sanity/sanity-mcp-config) documentation provides detailed setup instructions for all required variables. + Environment variables enable the MCP to authenticate without OAuth browser prompts. The Sanity MCP Config documentation provides detailed setup instructions for all required variables. ## Content Management Best Practices -Implement automated content quality checks using Continue's rule system. See the [Rules deep dive](/customize/deep-dives/rules) for authoring tips. +Implement automated content quality checks using Shadow Code's rule system. See the [Rules deep dive](/customize/deep-dives/rules) for authoring tips. ```bash @@ -533,8 +533,8 @@ After completing this guide, you have a complete **AI-powered content management Complete Sanity platform documentation - - Explore Continue documentation and guides + + Explore Shadow Code documentation and guides - An automated error monitoring system that uses Continue CLI with Sentry MCP to analyze production errors, identify root causes with AI, and create detailed GitHub issues with suggested fixes. + An automated error monitoring system that uses Shadow Code CLI with Sentry MCP to analyze production errors, identify root causes with AI, and create detailed GitHub issues with suggested fixes. ## What You'll Learn @@ -30,17 +30,17 @@ Before starting, ensure you have: - GitHub repository where you want to create issues - [Sentry account](https://sentry.io) with an active project collecting errors - Node.js 18+ installed locally -- [Continue CLI](https://docs.continue.dev/guides/cli) with **active credits** (required for API usage) +- Shadow Code CLI with **active credits** (required for API usage) - [GitHub CLI](https://cli.github.com/) installed (`gh` command) - + - - 1. Visit [Continue Organizations](https://continue.dev/settings/organizations) - 2. Sign up or log in to your Continue account + + 1. Visit Shadow Code Organizations + 2. Sign up or log in to your Shadow Code account 3. Navigate to your organization settings 4. Click **"API Keys"** and then **"+ New API Key"** 5. Copy the API key immediately (you won't see it again!) @@ -49,7 +49,7 @@ Before starting, ensure you have: - Continue CLI handles complex error analysis and API interactions - you just need to provide the right prompts! + Shadow Code CLI handles complex error analysis and API interactions - you just need to provide the right prompts! ## Step 1: Set Up Your Credentials @@ -63,7 +63,7 @@ First, you'll need to gather your Sentry and GitHub API credentials. See [Sentry MCP Documentation](https://docs.sentry.io/product/sentry-mcp/) for detailed configuration options - The Sentry MCP supports multiple configuration methods. For Continue CLI, OAuth is recommended: + The Sentry MCP supports multiple configuration methods. For Shadow Code CLI, OAuth is recommended: **Option 1: OAuth Configuration (Recommended)** @@ -92,7 +92,7 @@ First, you'll need to gather your Sentry and GitHub API credentials. 1. Go to [User Auth Tokens](https://sentry.io/settings/account/api/auth-tokens/) in Sentry - For self-hosted Sentry, use: `https://YOUR-SENTRY-DOMAIN/settings/account/api/auth-tokens/` 2. Click **Create New Token** - 3. Name it "Continue CLI Error Analysis" + 3. Name it "Shadow Code CLI Error Analysis" 4. **Select these permission scopes** (required for full functionality): - `org:read` - **Required** - Access organization information - `project:read` - **Required** - Read project configurations @@ -131,7 +131,7 @@ First, you'll need to gather your Sentry and GitHub API credentials. - + Configure the [Sentry MCP](https://docs.sentry.io/product/sentry-mcp/) using OAuth: The MCP server will automatically prompt for OAuth authentication when you first use it. @@ -146,7 +146,7 @@ First, you'll need to gather your Sentry and GitHub API credentials. - Use this prompt template with Continue CLI to analyze Sentry errors: + Use this prompt template with Shadow Code CLI to analyze Sentry errors: ``` Analyze Sentry errors from the past 24 hours: @@ -187,7 +187,7 @@ First, you'll need to gather your Sentry and GitHub API credentials. ## Step 2: Analyze Sentry Errors with AI -Use Continue CLI to perform intelligent error analysis. Enter these prompts in the Continue CLI TUI: +Use Shadow Code CLI to perform intelligent error analysis. Enter these prompts in the Shadow Code CLI TUI: To run any of the example prompts below in headless mode, use `cn -p "prompt"` @@ -238,7 +238,7 @@ To run any of the example prompts below in headless mode, use `cn -p "prompt"` ## Step 3: Automate GitHub Issue Creation -Create actionable GitHub issues from Sentry errors. Enter this prompt in the Continue CLI TUI: +Create actionable GitHub issues from Sentry errors. Enter this prompt in the Shadow Code CLI TUI: **Prompt:** ``` @@ -266,12 +266,12 @@ For each unresolved Sentry error with 'critical' or 'high' severity: ## Step 4: Set Up Continuous Monitoring with GitHub Actions -Automate error monitoring with the [Sentry Release GitHub Action](https://docs.sentry.io/product/releases/setup/release-automation/github-actions/) and Continue CLI to create comprehensive, AI-powered issue descriptions: +Automate error monitoring with the [Sentry Release GitHub Action](https://docs.sentry.io/product/releases/setup/release-automation/github-actions/) and Shadow Code CLI to create comprehensive, AI-powered issue descriptions: - **Why Combine Sentry Releases with Continue CLI?** + **Why Combine Sentry Releases with Shadow Code CLI?** - **Release Tracking**: Associate errors with specific deployments - - **AI-Powered Analysis**: Continue CLI generates detailed issue descriptions with root cause analysis + - **AI-Powered Analysis**: Shadow Code CLI generates detailed issue descriptions with root cause analysis - **Better Context**: Link errors to commits and pull requests - **Automated Workflows**: Create issues with full stack traces and suggested fixes @@ -310,10 +310,10 @@ jobs: with: environment: production - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | npm install -g @continuedev/cli - echo "✅ Continue CLI installed" + echo "✅ Shadow Code CLI installed" - name: Authenticate GitHub CLI run: | @@ -329,7 +329,7 @@ jobs: run: | echo "🔍 Analyzing Sentry errors..." - # Use Continue CLI to analyze errors and generate comprehensive issue descriptions + # Use Shadow Code CLI to analyze errors and generate comprehensive issue descriptions cn -p "Using Sentry MCP, analyze errors from the past 6 hours for project $SENTRY_PROJECT: 1. Filter for unresolved errors with high or critical severity 2. Group similar errors to avoid duplicates @@ -371,7 +371,7 @@ jobs: **Required GitHub Secrets**: - - `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) + - `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys - `SENTRY_AUTH_TOKEN`: Your Sentry User Auth Token (needs scopes: `org:read`, `project:read`, `project:releases`, `event:read`) - `SENTRY_ORG`: Your Sentry organization slug - `SENTRY_PROJECT`: Your Sentry project slug @@ -384,7 +384,7 @@ jobs: **Workflow Best Practices**: - Run every 6 hours to catch critical errors quickly - Create Sentry releases on push to track error-to-deployment correlation - - Use Continue CLI to generate comprehensive, AI-powered issue descriptions + - Use Shadow Code CLI to generate comprehensive, AI-powered issue descriptions - Use duplicate detection to avoid creating multiple issues for the same error - Filter by severity to focus on high-impact issues - Include full error context and suggested fixes in issues @@ -412,7 +412,7 @@ After completing this guide, you have a complete **Sentry-powered error monitori ## Advanced Error Analysis Prompts -Enhance your workflow with these advanced Continue CLI prompts: +Enhance your workflow with these advanced Shadow Code CLI prompts: @@ -434,7 +434,7 @@ Enhance your workflow with these advanced Continue CLI prompts: **Protect Your API Keys**: - Store all credentials as GitHub Secrets, never in code - - Use Continue CLI's secure secret storage + - Use Shadow Code CLI's secure secret storage - Limit Sentry token scopes to minimum required permissions - Rotate API keys regularly (every 90 days recommended) - Monitor token usage for unusual activity @@ -459,7 +459,7 @@ See the [Sentry MCP GitHub Issues](https://github.com/getsentry/sentry-mcp/issue | Issue | Solution | |:------|:---------| | No errors returned | Verify your Sentry project has collected errors recently | -| OAuth prompt not appearing | Check that Continue CLI has proper MCP configuration | +| OAuth prompt not appearing | Check that Shadow Code CLI has proper MCP configuration | | Duplicate GitHub issues | Implement duplicate detection in your prompts | | Missing error context | Ensure your Sentry token has `event:read` scope | @@ -479,5 +479,5 @@ See the [Sentry MCP GitHub Issues](https://github.com/getsentry/sentry-mcp/issue - [Sentry MCP GitHub Repository](https://github.com/getsentry/sentry-mcp) - [GitHub CLI Documentation](https://cli.github.com/) -- [Continue CLI Guide](https://docs.continue.dev/guides/cli) +- Shadow Code CLI Guide - [Continuous AI Best Practices](https://blog.continue.dev/what-is-continuous-ai-a-developers-guide/) diff --git a/docs/guides/snyk-mcp-continue-cookbook.mdx b/docs/guides/snyk-mcp-continue-cookbook.mdx index cee4487bee6..ede81984060 100644 --- a/docs/guides/snyk-mcp-continue-cookbook.mdx +++ b/docs/guides/snyk-mcp-continue-cookbook.mdx @@ -1,7 +1,7 @@ --- -title: "Automated Security Scanning with Snyk MCP and Continue" +title: "Automated Security Scanning with Snyk MCP and Shadow Code" description: "Set up an AI-powered security workflow that automatically scans your code, dependencies, infrastructure, and containers using natural language commands." -sidebarTitle: "Snyk Security Scanning with Continue" +sidebarTitle: "Snyk Security Scanning with Shadow Code" --- import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' @@ -10,7 +10,7 @@ import CLIInstall from '/snippets/cli-install.mdx' - An automated security scanning system that uses Continue's AI agent with Snyk + An automated security scanning system that uses Shadow Code's AI agent with Snyk MCP to identify vulnerabilities in code, dependencies, infrastructure, and containers - all through simple natural language prompts @@ -21,7 +21,7 @@ import CLIInstall from '/snippets/cli-install.mdx' width="100%" height="400" src="https://www.youtube.com/embed/cwVnKOf3tVg" - title="Snyk MCP Continue Cookbook Demo" + title="Snyk MCP Shadow Code Cookbook Demo" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen @@ -37,7 +37,7 @@ Before starting, ensure you have: For all options, first: - + @@ -56,10 +56,10 @@ For all options, first: - **Important**: The Snyk MCP requires the Snyk CLI to be authenticated locally. Run `snyk auth` to authenticate before using the Continue agent with Snyk MCP. + **Important**: The Snyk MCP requires the Snyk CLI to be authenticated locally. Run `snyk auth` to authenticate before using the Shadow Code agent with Snyk MCP. - To use agents in headless mode, you need a [Continue API key](https://continue.dev/settings/api-keys). + To use agents in headless mode, you need a Shadow Code API key. ## Snyk Continuous AI Workflow Options @@ -105,7 +105,7 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s - + Add the Snyk MCP to your configuration: ```bash @@ -115,14 +115,14 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s **Installation methods:** 1. **Quick CLI install**: `cn --mcp snyk/snyk-mcp` - 2. **Manual configuration**: Add the MCP to your `~/.continue/config.json` under the `mcpServers` section + 2. **Manual configuration**: Add the MCP to your `~/.shadow-code/config.json` under the `mcpServers` section - Once installed, Snyk MCP tools become available to your Continue agent for all prompts. + Once installed, Snyk MCP tools become available to your Shadow Code agent for all prompts. The MCP will request authentication and folder trust permissions when first used. - This is handled automatically by the Continue agent. + This is handled automatically by the Shadow Code agent. @@ -151,7 +151,7 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s To use the pre-built agent, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration along with the pre-configured Snyk MCP for security scanning operations. @@ -162,12 +162,12 @@ After ensuring you meet the **Prerequisites** above, you have two paths to get s ## Security Scanning Recipes -Now you can use natural language prompts to run comprehensive security scans. The Continue agent automatically calls the appropriate Snyk MCP tools. +Now you can use natural language prompts to run comprehensive security scans. The Shadow Code agent automatically calls the appropriate Snyk MCP tools. **Where to run these workflows:** - - **IDE Extensions**: Use Continue in VS Code, JetBrains, or other supported IDEs + - **IDE Extensions**: Use Shadow Code in VS Code, JetBrains, or other supported IDEs - **Terminal (TUI mode)**: Run `cn` to enter interactive mode, then type your prompts - **CLI (headless mode)**: Use `cn -p "your prompt" --auto` for headless commands @@ -302,7 +302,7 @@ This example demonstrates a **Continuous AI workflow** where security scanning r Navigate to **Repository Settings → Secrets and variables → Actions** and add: -- `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) +- `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys - `SNYK_TOKEN`: Your Snyk authentication token from [app.snyk.io/account](https://app.snyk.io/account) ### Create Workflow File @@ -343,10 +343,10 @@ jobs: npm install -g snyk echo "✅ Snyk CLI installed" - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | npm install -g @continuedev/cli - echo "✅ Continue CLI installed" + echo "✅ Shadow Code CLI installed" - name: Validate Secrets run: | @@ -423,7 +423,7 @@ jobs: run: | echo "🤖 Generating AI-powered mitigation suggestions..." - # Create a summary of findings for Continue CLI + # Create a summary of findings for Shadow Code CLI FINDINGS_SUMMARY=$(cat snyk-code-results.json snyk-oss-results.json | jq -r ' if .runs then .runs[0].results[] | "Code Issue: \(.message.text) in \(.locations[0].physicalLocation.artifactLocation.uri) (Severity: \(.level))" @@ -439,7 +439,7 @@ jobs: echo "$FINDINGS_SUMMARY" echo "" - # Use Continue CLI to generate mitigation suggestions + # Use Shadow Code CLI to generate mitigation suggestions PROMPT="Analyze these Snyk security findings and provide specific, actionable mitigation steps for each issue. Focus on: 1) Root cause, 2) Immediate fix, 3) Long-term prevention. Findings: $FINDINGS_SUMMARY. Provide clear, prioritized recommendations." cn --agent continuedev/snyk-continuous-ai-agent -p "$PROMPT" --auto > mitigation-suggestions.md || { @@ -482,7 +482,7 @@ jobs: **Scan Details:** - 📊 Full report available in workflow artifacts - 🔍 Review the [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}) for complete details - - 🤖 Generated with Continue CLI + Snyk + - 🤖 Generated with Shadow Code CLI + Snyk _This is an automated security analysis. Please review and address the findings before merging._ EOF @@ -564,7 +564,7 @@ jobs: **About SNYK_TOKEN**: The workflow uses the SNYK_TOKEN in two ways: 1. **Direct Snyk CLI authentication** - Authenticates the Snyk CLI for running scans - 2. **Continue CLI access** - Available as an environment variable when Continue generates AI mitigation suggestions + 2. **Shadow Code CLI access** - Available as an environment variable when Shadow Code generates AI mitigation suggestions The `cn` agent automatically uses the SNYK_TOKEN when needed for Snyk MCP operations. @@ -572,14 +572,14 @@ jobs: This workflow demonstrates several advanced features: - **Changed Files Detection**: Only scans files modified in the PR - - **AI Mitigation**: Uses Continue CLI to generate actionable mitigation steps + - **AI Mitigation**: Uses Shadow Code CLI to generate actionable mitigation steps - **PR Comments**: Automatically posts mitigation suggestions as PR comments - **Comprehensive Reporting**: Generates detailed security reports with artifacts ## Security Guardrails -Implement automated security policies using Continue's rule system. See the [Rules deep dive](/customize/deep-dives/rules) for authoring tips. +Implement automated security policies using Shadow Code's rule system. See the [Rules deep dive](/customize/deep-dives/rules) for authoring tips. @@ -673,8 +673,8 @@ After completing this guide, you have a complete **AI-powered security system** Complete Snyk platform documentation - - Explore Continue documentation and guides + + Explore Shadow Code documentation and guides - A security audit workflow that uses Continue CLI with Supabase MCP to identify RLS vulnerabilities, generate secure policies, fix permission issues, and ensure your database follows security best practices. + A security audit workflow that uses Shadow Code CLI with Supabase MCP to identify RLS vulnerabilities, generate secure policies, fix permission issues, and ensure your database follows security best practices. ## What You'll Learn @@ -29,17 +29,17 @@ Before starting, ensure you have: - [Supabase account](https://supabase.com) with an active project - Node.js 18+ installed locally -- [Continue CLI](https://docs.continue.dev/guides/cli) with **active credits** (required for API usage) +- Shadow Code CLI with **active credits** (required for API usage) - Basic understanding of SQL and database concepts - + - - 1. Visit [Continue Organizations](https://continue.dev/settings/organizations) - 2. Sign up or log in to your Continue account + + 1. Visit Shadow Code Organizations + 2. Sign up or log in to your Shadow Code account 3. Navigate to your organization settings 4. Click **"API Keys"** and then **"+ New API Key"** 5. Copy the API key immediately (you won't see it again!) @@ -48,7 +48,7 @@ Before starting, ensure you have: - Continue CLI can analyze your database schema and generate complex SQL queries - you just need to describe what you want in plain language! + Shadow Code CLI can analyze your database schema and generate complex SQL queries - you just need to describe what you want in plain language! ## Step 1: Set Up Your Credentials @@ -157,7 +157,7 @@ First, you'll need to set up access to your Supabase project. - Navigate to your project directory and enter this prompt in the Continue CLI TUI: + Navigate to your project directory and enter this prompt in the Shadow Code CLI TUI: ``` Analyze my Supabase database schema and suggest performance optimizations @@ -175,7 +175,7 @@ First, you'll need to set up access to your Supabase project. - + Configure the [Supabase MCP](https://supabase.com/docs/guides/getting-started/mcp) using OAuth: The MCP server will automatically prompt for OAuth authentication when you first use it. @@ -190,7 +190,7 @@ First, you'll need to set up access to your Supabase project. - Use this prompt template with Continue CLI to analyze your database: + Use this prompt template with Shadow Code CLI to analyze your database: ``` Analyze my Supabase database: @@ -210,7 +210,7 @@ First, you'll need to set up access to your Supabase project. To use the pre-built agent, you need either: - - **Continue CLI Pro Plan** with the models add-on, OR + - **Shadow Code CLI Pro Plan** with the models add-on, OR - **Your own API keys** configured as environment variables The agent will automatically detect and use your configuration. For Supabase MCP: @@ -231,7 +231,7 @@ First, you'll need to set up access to your Supabase project. ## Step 2: Analyze Your Database with AI -Use Continue CLI to perform intelligent database analysis. Enter these prompts in the Continue CLI TUI: +Use Shadow Code CLI to perform intelligent database analysis. Enter these prompts in the Shadow Code CLI TUI: @@ -309,7 +309,7 @@ Use Continue CLI to perform intelligent database analysis. Enter these prompts i ## Step 3: Generate Database Migrations -Create and apply database migrations based on AI recommendations. Enter this prompt in the Continue CLI TUI: +Create and apply database migrations based on AI recommendations. Enter this prompt in the Shadow Code CLI TUI: **Example: Complete RLS Security Fix** @@ -355,7 +355,7 @@ The AI will generate a complete SQL migration file that: ## Step 4: Set Up Automated Database Monitoring -Automate database health checks with Continue CLI and GitHub Actions: +Automate database health checks with Shadow Code CLI and GitHub Actions: ```yaml name: Database Health Monitor @@ -376,10 +376,10 @@ jobs: with: node-version: "22" - - name: Install Continue CLI + - name: Install Shadow Code CLI run: | npm install -g @continuedev/cli - echo "✅ Continue CLI installed" + echo "✅ Shadow Code CLI installed" - name: Analyze Database Security env: @@ -387,7 +387,7 @@ jobs: run: | echo "🔍 Performing security audit..." - # Use Continue CLI to audit RLS and generate fixes + # Use Shadow Code CLI to audit RLS and generate fixes cn -p "Using Supabase MCP, perform a comprehensive RLS security audit: 1. Check all tables for RLS enablement 2. Identify tables with missing or weak RLS policies @@ -428,7 +428,7 @@ jobs: **Required GitHub Secrets**: - - `CONTINUE_API_KEY`: Your Continue API key from [continue.dev/settings/api-keys](https://continue.dev/settings/api-keys) + - `CONTINUE_API_KEY`: Your Shadow Code API key from continue.dev/settings/api-keys Add this at: **Repository Settings → Secrets and variables → Actions** @@ -449,7 +449,7 @@ After completing this guide, you have a complete **AI-powered database managemen ## Advanced Database Prompts -Enhance your workflow with these advanced Continue CLI prompts: +Enhance your workflow with these advanced Shadow Code CLI prompts: @@ -510,5 +510,5 @@ If you encounter connection issues: - [Supabase Database Guide](https://supabase.com/docs/guides/database) - [Supabase Security Best Practices](https://supabase.com/docs/guides/database/security) - [Model Context Protocol Docs](https://modelcontextprotocol.io/introduction) -- [Continue CLI Guide](https://docs.continue.dev/guides/cli) +- Shadow Code CLI Guide - [Continuous AI Best Practices](https://blog.continue.dev/what-is-continuous-ai-a-developers-guide/) \ No newline at end of file diff --git a/docs/guides/understanding-configs.mdx b/docs/guides/understanding-configs.mdx index 395e128a45a..07d9569b650 100644 --- a/docs/guides/understanding-configs.mdx +++ b/docs/guides/understanding-configs.mdx @@ -1,6 +1,6 @@ --- title: "How to Understand Configuration" -description: "Learn how to configure AI development assistance in Continue, including setup, management, and best practices" +description: "Learn how to configure AI development assistance in Shadow Code, including setup, management, and best practices" --- -Every developer has unique needs when it comes to AI assistance. Continue provides flexible configuration options to match your workflow, whether you want quick setup or full control. +Every developer has unique needs when it comes to AI assistance. Shadow Code provides flexible configuration options to match your workflow, whether you want quick setup or full control. ## How to Access Your Configuration To access your configuration: -1. Open the Continue Chat sidebar by pressing cmd/ctrl + L (VS Code) or cmd/ctrl + J (JetBrains) +1. Open the Shadow Code Chat sidebar by pressing cmd/ctrl + L (VS Code) or cmd/ctrl + J (JetBrains) 2. Click the Config selector above the main chat input 3. Hover over a config and click the `gear` icon to edit @@ -27,7 +27,7 @@ To access your configuration: ## Configuration Overview -Continue uses a `config.yaml` file that gives you complete control over every aspect of your experience, with all configuration stored directly on your machine. +Shadow Code uses a `config.yaml` file that gives you complete control over every aspect of your experience, with all configuration stored directly on your machine. ### Why Use Local Configuration? @@ -44,7 +44,7 @@ Local configuration lives in a single YAML file in your home directory: **File Locations:** -- macOS/Linux: `~/.continue/config.yaml` +- macOS/Linux: `~/.shadow-code/config.yaml` - Windows: `%USERPROFILE%\.continue\config.yaml` **Quick Access Method:** @@ -57,9 +57,9 @@ Local configuration lives in a single YAML file in your home directory: ### The Config Experience -When you edit your `config.yaml`, Continue provides intelligent autocomplete for all available options. Save the file, and Continue automatically reloads your configuration -- no restart required. +When you edit your `config.yaml`, Shadow Code provides intelligent autocomplete for all available options. Save the file, and Shadow Code automatically reloads your configuration -- no restart required. -The first time you use Continue, it generates a `config.yaml` with sensible defaults. From there, you can customize everything from model selection to context providers, slash commands, and more. +The first time you use Shadow Code, it generates a `config.yaml` with sensible defaults. From there, you can customize everything from model selection to context providers, slash commands, and more. For the complete configuration reference, see our [config.yaml documentation](/reference). @@ -78,12 +78,12 @@ For the complete configuration reference, see our [config.yaml documentation](/r **Config Not Loading?** - Verify file location matches your OS -- Check YAML syntax (Continue will show errors) +- Check YAML syntax (Shadow Code will show errors) - Ensure file permissions allow reading **Autocomplete Not Working?** -- Update to the latest Continue version +- Update to the latest Shadow Code version - Check that you're editing the correct file ## Next Steps @@ -95,4 +95,4 @@ Now that you understand both configuration approaches, you're ready to dive deep Remember, the best configuration is the one that helps you code more effectively. Start simple, experiment freely, and gradually refine your setup as you discover what works best for your workflow. -Happy coding with Continue! 🚀 +Happy coding with Shadow Code! 🚀 diff --git a/docs/home.mdx b/docs/home.mdx index 10e81775c41..e8966d7cfe1 100644 --- a/docs/home.mdx +++ b/docs/home.mdx @@ -1,20 +1,20 @@ --- title: "Introduction" -description: "Learn how Continue enables developers to embrace continuous AI, enabling them to build custom AI code agents with open source VS Code and JetBrains extensions featuring chat, autocomplete, edit, and agent capabilities" +description: "Learn how Shadow Code enables developers to embrace continuous AI, enabling them to build custom AI code agents with open source VS Code and JetBrains extensions featuring chat, autocomplete, edit, and agent capabilities" --- -## What is Continue? +## What is Shadow Code? -**Continue enables developers to create, share, and use custom AI code agents with our open source [VS Code](https://marketplace.visualstudio.com/items?itemName=Continue.continue) and [JetBrains](https://plugins.jetbrains.com/plugin/22707-continue-extension) extensions** +**Shadow Code enables developers to create, share, and use custom AI code agents with our open source [VS Code](https://marketplace.visualstudio.com/items?itemName=Continue.continue) and [JetBrains](https://plugins.jetbrains.com/plugin/22707-continue-extension) extensions** ## Key Features -**You can use Continue to build and run custom agents across your IDE, terminal, and CI.** +**You can use Shadow Code to build and run custom agents across your IDE, terminal, and CI.** -1. Get started with Continue in [VS Code](https://marketplace.visualstudio.com/items?itemName=Continue.continue) or [JetBrains](https://plugins.jetbrains.com/plugin/22707-continue-extension) extensions: +1. Get started with Shadow Code in [VS Code](https://marketplace.visualstudio.com/items?itemName=Continue.continue) or [JetBrains](https://plugins.jetbrains.com/plugin/22707-continue-extension) extensions: - [Agent mode](/ide-extensions/agent/quick-start) to work on development tasks together with AI - [Chat mode](/ide-extensions/chat/quick-start) to ask general questions and clarify code sections - [Edit mode](/ide-extensions/edit/quick-start) to modify code section without leaving your current file - [Autocomplete](/ide-extensions/autocomplete/quick-start) to receive inline code suggestions as you type -2. Try out [Continue CLI (cn)](https://docs.continue.dev/guides/cli) and give us feedback +2. Try out Shadow Code CLI (cn) and give us feedback diff --git a/docs/ide-extensions/agent/context-selection.mdx b/docs/ide-extensions/agent/context-selection.mdx index d4aad5cabf4..90b5535b44a 100644 --- a/docs/ide-extensions/agent/context-selection.mdx +++ b/docs/ide-extensions/agent/context-selection.mdx @@ -1,6 +1,6 @@ --- title: "Context Selection" -description: "Learn how Continue's agent mode selects relevant code context using file content, language server definitions, imports, and recent file history." +description: "Learn how Shadow Code's agent mode selects relevant code context using file content, language server definitions, imports, and recent file history." --- You can use the same methods to manually add context as [Chat](/ide-extensions/chat/context-selection). diff --git a/docs/ide-extensions/agent/how-it-works.mdx b/docs/ide-extensions/agent/how-it-works.mdx index 6638a5e1d7b..80ce0a56f85 100644 --- a/docs/ide-extensions/agent/how-it-works.mdx +++ b/docs/ide-extensions/agent/how-it-works.mdx @@ -12,8 +12,8 @@ The following handshake describes how Agent mode uses tools: 1. In Agent mode, available tools are sent along with `user` chat requests 2. The model can choose to include a tool call in its response 3. The user gives permission. This step is skipped if the policy for that tool is set to `Automatic` -4. Continue calls the tool using built-in functionality or the MCP server that offers that particular tool -5. Continue sends the result back to the model +4. Shadow Code calls the tool using built-in functionality or the MCP server that offers that particular tool +5. Shadow Code sends the result back to the model 6. The model responds, potentially with another tool call and step 2 begins again @@ -23,7 +23,7 @@ The following handshake describes how Agent mode uses tools: ## What Built-in Tools Are Available -Continue includes several built-in tools which provide the model access to IDE functionality. +Shadow Code includes several built-in tools which provide the model access to IDE functionality. ### What Tools Are Available in Plan Mode (Read-Only) @@ -48,5 +48,5 @@ In Agent mode, all tools are available including the read-only tools above plus: - **Create new file** (`create_new_file`): Create a new file within the project - **Edit file** (`edit_existing_file`): Make changes to existing files - **Run terminal command** (`run_terminal_command`): Run commands from the workspace root -- **Create Rule Block** (`create_rule_block`): Create a new rule block in `.continue/rules` +- **Create Rule Block** (`create_rule_block`): Create a new rule block in `.shadow-code/rules` - All other write/execute tools for modifying the codebase diff --git a/docs/ide-extensions/agent/how-to-customize.mdx b/docs/ide-extensions/agent/how-to-customize.mdx index ab655d8d812..f1ce683b42e 100644 --- a/docs/ide-extensions/agent/how-to-customize.mdx +++ b/docs/ide-extensions/agent/how-to-customize.mdx @@ -1,6 +1,6 @@ --- title: "How to Customize Agent Mode" -description: "Learn how to customize Agent Mode in Continue to better fit your workflow and coding style." +description: "Learn how to customize Agent Mode in Shadow Code to better fit your workflow and coding style." sidebarTitle: "Customize Agent Mode" --- @@ -31,7 +31,7 @@ You can add MCP servers to your configuration to give Agent mode access to more You can adjust the Agent mode's tool usage behavior to three options: -- **Ask First (default)**: Request user permission with "Cancel" and "Continue" buttons +- **Ask First (default)**: Request user permission with "Cancel" and "Shadow Code" buttons - **Automatic**: Automatically call the tool without requesting permission - **Excluded**: Do not send the tool to the model diff --git a/docs/ide-extensions/agent/model-setup.mdx b/docs/ide-extensions/agent/model-setup.mdx index c4fa7045cb3..aba846026e1 100644 --- a/docs/ide-extensions/agent/model-setup.mdx +++ b/docs/ide-extensions/agent/model-setup.mdx @@ -1,6 +1,6 @@ --- title: "Model Setup for Agent Mode" -description: "Learn how to set up models for Agent Mode in Continue, including recommended models and configuration options for optimal performance" +description: "Learn how to set up models for Agent Mode in Shadow Code, including recommended models and configuration options for optimal performance" sidebarTitle: "Model Setup" --- import { ModelRecommendations } from '/snippets/ModelRecommendations.jsx' @@ -9,11 +9,11 @@ The models you set up for Chat mode will be used with Agent mode if the model su ## How System Message Tools Work -Continue implements an innovative approach called **system message tools** that ensures consistent tool functionality across all models, regardless of their native capabilities. This allows Agent mode to work seamlessly with a wider range of models and providers. +Shadow Code implements an innovative approach called **system message tools** that ensures consistent tool functionality across all models, regardless of their native capabilities. This allows Agent mode to work seamlessly with a wider range of models and providers. ### How System Message Tools Function -Instead of relying solely on native tool calling APIs (which vary between providers), Continue converts tools into XML format and includes them in the system message. The model generates tool calls as structured XML within its response, which Continue then parses and executes. This approach provides: +Instead of relying solely on native tool calling APIs (which vary between providers), Shadow Code converts tools into XML format and includes them in the system message. The model generates tool calls as structured XML within its response, which Shadow Code then parses and executes. This approach provides: - **Universal compatibility** - Any model capable of following instructions can use tools, not just those with native tool support - **Consistent behavior** - Tool calls work identically across OpenAI, Anthropic, local models, and others @@ -26,7 +26,7 @@ Instead of relying solely on native tool calling APIs (which vary between provid ### How to Configure Agent Mode -Agent mode automatically determines whether to use native or system message tools based on the model's capabilities. No additional configuration is required - simply select your model and Continue handles the rest. +Agent mode automatically determines whether to use native or system message tools based on the model's capabilities. No additional configuration is required - simply select your model and Shadow Code handles the rest. ## How to Check Model Compatibility diff --git a/docs/ide-extensions/agent/plan-mode.mdx b/docs/ide-extensions/agent/plan-mode.mdx index 89cd65c4d65..c82c09925d9 100644 --- a/docs/ide-extensions/agent/plan-mode.mdx +++ b/docs/ide-extensions/agent/plan-mode.mdx @@ -1,7 +1,7 @@ --- -title: "Plan Mode in Continue – Safe, Read-Only Code Exploration" +title: "Plan Mode in Shadow Code – Safe, Read-Only Code Exploration" sidebarTitle: "Plan Mode" -description: "Learn how to use Plan Mode in Continue to explore and understand codebases safely with read-only tools, search, and analysis before making changes" +description: "Learn how to use Plan Mode in Shadow Code to explore and understand codebases safely with read-only tools, search, and analysis before making changes" --- ## What is Plan mode? diff --git a/docs/ide-extensions/agent/quick-start.mdx b/docs/ide-extensions/agent/quick-start.mdx index 7ede50b54d3..4644eda7f30 100644 --- a/docs/ide-extensions/agent/quick-start.mdx +++ b/docs/ide-extensions/agent/quick-start.mdx @@ -1,6 +1,6 @@ --- title: "Quick Start" -description: "Get started with Continue's Agent mode to automatically implement code changes, fix bugs, and run commands using AI-powered tools that can modify your codebase based on natural language instructions" +description: "Get started with Shadow Code's Agent mode to automatically implement code changes, fix bugs, and run commands using AI-powered tools that can modify your codebase based on natural language instructions" --- Agent mode equips the Chat model with the tools needed to handle a wide range of coding tasks, allowing the model to make decisions and save you the work of manually finding context and performing actions. @@ -49,7 +49,7 @@ You can switch to `Agent` in the mode selector below the chat input box. The mod If Agent mode or Plan mode is disabled with a `Not Supported` message, the selected - model or provider doesn't support tools, or Continue doesn't yet support tools + model or provider doesn't support tools, or Shadow Code doesn't yet support tools with it. See [Model Blocks](/customize/models) for more information. @@ -71,7 +71,7 @@ Agent mode will then decide which tools to use to get the job done. ## How to Give Agent Mode Permission -By default, Agent mode will ask permission when it wants to use a tool. Click `Continue` to allow Agent mode to proceed with the tool call or `Cancel` to reject it. +By default, Agent mode will ask permission when it wants to use a tool. Click `Shadow Code` to allow Agent mode to proceed with the tool call or `Cancel` to reject it. ![agent requesting permission](/images/ide-extensions/agent/images/agent-permission-c150919a5c43eb4f55d9d4a46ef8b2d6.png) diff --git a/docs/ide-extensions/autocomplete/context-selection.mdx b/docs/ide-extensions/autocomplete/context-selection.mdx index 28775a229e0..2b6a2299f94 100644 --- a/docs/ide-extensions/autocomplete/context-selection.mdx +++ b/docs/ide-extensions/autocomplete/context-selection.mdx @@ -1,6 +1,6 @@ --- title: "Context Selection" -description: "Learn how Continue's autocomplete selects relevant code context using file content, language server definitions, imports, and recent file history." +description: "Learn how Shadow Code's autocomplete selects relevant code context using file content, language server definitions, imports, and recent file history." --- Autocomplete will automatically determine context based on the current cursor position. We use the following techniques to determine what to include in the prompt: diff --git a/docs/ide-extensions/autocomplete/how-it-works.mdx b/docs/ide-extensions/autocomplete/how-it-works.mdx index 85a3fd69ef3..914f2c53cc2 100644 --- a/docs/ide-extensions/autocomplete/how-it-works.mdx +++ b/docs/ide-extensions/autocomplete/how-it-works.mdx @@ -1,7 +1,7 @@ --- -title: "How Autocomplete Works in Continue" +title: "How Autocomplete Works in Shadow Code" sidebarTitle: "How Autocomplete Works" -description: "Understand how Continue's autocomplete works, including timing optimization, context retrieval from your codebase, and filtering to improve AI code suggestions." +description: "Understand how Shadow Code's autocomplete works, including timing optimization, context retrieval from your codebase, and filtering to improve AI code suggestions." --- ## Timing Optimization for Autocomplete @@ -13,7 +13,7 @@ In order to display suggestions quickly, without sending too many requests, we d ## Context Retrieval from Your Codebase -Continue uses a number of retrieval methods to find relevant snippets from your codebase to include in the prompt. +Shadow Code uses a number of retrieval methods to find relevant snippets from your codebase to include in the prompt. ## Filtering and Post-Processing AI Suggestions diff --git a/docs/ide-extensions/autocomplete/how-to-customize.mdx b/docs/ide-extensions/autocomplete/how-to-customize.mdx index 4fab6438321..7220bd24dbe 100644 --- a/docs/ide-extensions/autocomplete/how-to-customize.mdx +++ b/docs/ide-extensions/autocomplete/how-to-customize.mdx @@ -1,8 +1,8 @@ --- -title: “Customize Autocomplete Settings in Continue” -description: “Learn how to customize autocomplete behavior in Continue, including user settings, configuration options, and adjustments to improve AI code suggestions in your IDE.” +title: “Customize Autocomplete Settings in Shadow Code” +description: “Learn how to customize autocomplete behavior in Shadow Code, including user settings, configuration options, and adjustments to improve AI code suggestions in your IDE.” --- -Continue offers a handful of settings to customize autocomplete behavior. Visit the User Settings Page (Gear Icon) to manage these settings. +Shadow Code offers a handful of settings to customize autocomplete behavior. Visit the User Settings Page (Gear Icon) to manage these settings. For a comprehensive guide on all configuration options and their impacts, see the [Autocomplete deep dive](/customize/deep-dives/autocomplete). diff --git a/docs/ide-extensions/autocomplete/model-setup.mdx b/docs/ide-extensions/autocomplete/model-setup.mdx index 3d0a3d6d547..5a99b2b98d9 100644 --- a/docs/ide-extensions/autocomplete/model-setup.mdx +++ b/docs/ide-extensions/autocomplete/model-setup.mdx @@ -1,6 +1,6 @@ --- -title: "Recommended Models for Autocomplete in Continue" -description: "Choose the best autocomplete model for Continue, including hosted high-performance options, fast speed/quality tradeoffs, and local privacy-first models." +title: "Recommended Models for Autocomplete in Shadow Code" +description: "Choose the best autocomplete model for Shadow Code, including hosted high-performance options, fast speed/quality tradeoffs, and local privacy-first models." sidebarTitle: "Recommended Autocomplete Models" --- import { ModelRecommendations } from '/snippets/ModelRecommendations.jsx'; @@ -17,7 +17,7 @@ Setting up the right model for autocomplete is important for a smooth coding exp ## Next Edit Model -For proactive code prediction that anticipates your next edit, Continue supports specialized [Next Edit](/ide-extensions/autocomplete/next-edit) models: +For proactive code prediction that anticipates your next edit, Shadow Code supports specialized [Next Edit](/ide-extensions/autocomplete/next-edit) models: **Supported Next Edit model:** diff --git a/docs/ide-extensions/autocomplete/next-edit.mdx b/docs/ide-extensions/autocomplete/next-edit.mdx index 6c8820032ef..d41393f3758 100644 --- a/docs/ide-extensions/autocomplete/next-edit.mdx +++ b/docs/ide-extensions/autocomplete/next-edit.mdx @@ -1,13 +1,13 @@ --- title: "Next Edit" -description: "Learn how Continue's Next Edit feature predicts and suggests your next code changes using AI, going beyond traditional autocomplete to anticipate entire code modifications" +description: "Learn how Shadow Code's Next Edit feature predicts and suggests your next code changes using AI, going beyond traditional autocomplete to anticipate entire code modifications" --- Next Edit is currently an experimental feature. It requires - [Instinct](https://continue.dev/continuedev/instinct) or [Mercury Coder - model](https://continue.dev/inception/mercury-coder) configured in - your Continue autocomplete settings and is not yet available for JetBrains + Instinct or Mercury Coder + model configured in + your Shadow Code autocomplete settings and is not yet available for JetBrains use. @@ -78,7 +78,7 @@ Next Edit requires: To use Next Edit, you must have the Instinct or Mercury Coder model configured - in your Continue autocomplete model settings. This model is specifically + in your Shadow Code autocomplete model settings. This model is specifically designed for next edit predictions. Once it's been loaded, you must reload VS Code to activate it. - + If accepted, your cursor moves to the last changed line. If rejected, your workflow continues uninterrupted. @@ -117,11 +117,11 @@ Next Edit requires: Next Edit requires AI models specifically trained for code prediction: - **Mercury Coder**: Primary model optimized for next edit prediction -- **Instinct**: The leading open Next Edit model, trained by Continue +- **Instinct**: The leading open Next Edit model, trained by Shadow Code ### Automatic Detection -Continue automatically enables Next Edit when: +Shadow Code automatically enables Next Edit when: 1. Your configured autocomplete model supports next edit capabilities 2. You have development team access permissions @@ -140,15 +140,15 @@ Continue automatically enables Next Edit when: - Have feedback? We want to hear it. __[File an issue on GitHub](https://github.com/continuedev/continue/issues)__ + Have feedback? We want to hear it. __File an issue on GitHub__ to help us improve. - Use Next Edit alongside Continue's Chat and Agent modes for comprehensive AI-assisted development. + Use Next Edit alongside Shadow Code's Chat and Agent modes for comprehensive AI-assisted development. --- -_Next Edit represents Continue's vision for proactive AI coding assistance that anticipates developer needs rather than just reacting to input. As this feature evolves, it will become a powerful tool for accelerating development workflows and reducing repetitive coding tasks._ +_Next Edit represents Shadow Code's vision for proactive AI coding assistance that anticipates developer needs rather than just reacting to input. As this feature evolves, it will become a powerful tool for accelerating development workflows and reducing repetitive coding tasks._ diff --git a/docs/ide-extensions/autocomplete/quick-start.mdx b/docs/ide-extensions/autocomplete/quick-start.mdx index 8a08e553f18..96fda6ea3da 100644 --- a/docs/ide-extensions/autocomplete/quick-start.mdx +++ b/docs/ide-extensions/autocomplete/quick-start.mdx @@ -1,12 +1,12 @@ --- -title: "Quick Start with Continue Autocomplete" -description: "Learn how to quickly start using Continue's AI autocomplete in your IDE, including enabling inline code suggestions and keyboard shortcuts for accepting, rejecting, or partially accepting completions." +title: "Quick Start with Shadow Code Autocomplete" +description: "Learn how to quickly start using Shadow Code's AI autocomplete in your IDE, including enabling inline code suggestions and keyboard shortcuts for accepting, rejecting, or partially accepting completions." sidebarTitle: "Autocomplete Quick Start" --- -## How to Enable and Use Continue Autocomplete +## How to Enable and Use Shadow Code Autocomplete -Autocomplete provides inline code suggestions as you type. To enable it, simply click the "Continue" button in the status bar at the bottom right of your IDE or ensure the "Enable Tab Autocomplete" option is checked in your IDE settings. +Autocomplete provides inline code suggestions as you type. To enable it, simply click the "Shadow Code" button in the status bar at the bottom right of your IDE or ensure the "Enable Tab Autocomplete" option is checked in your IDE settings. ## Keyboard Shortcuts for Autocomplete diff --git a/docs/ide-extensions/chat/context-selection.mdx b/docs/ide-extensions/chat/context-selection.mdx index c76b43b9c34..81e54768661 100644 --- a/docs/ide-extensions/chat/context-selection.mdx +++ b/docs/ide-extensions/chat/context-selection.mdx @@ -1,7 +1,7 @@ --- title: "Chat Mode Context Selection" sidebarTitle: "Context Selection" -description: "Learn how Continue selects relevant context for your chat requests, including text input, highlighted code, active files." +description: "Learn how Shadow Code selects relevant context for your chat requests, including text input, highlighted code, active files." --- ## How to Use Text Input diff --git a/docs/ide-extensions/chat/how-it-works.mdx b/docs/ide-extensions/chat/how-it-works.mdx index 252ce28980c..a6de493c3f3 100644 --- a/docs/ide-extensions/chat/how-it-works.mdx +++ b/docs/ide-extensions/chat/how-it-works.mdx @@ -1,11 +1,11 @@ --- title: "How Chat Works" -description: "Continue's Chat feature provides a conversational interface with AI models directly in your IDE sidebar." +description: "Shadow Code's Chat feature provides a conversational interface with AI models directly in your IDE sidebar." --- ## How Chat Core Functionality Works -When you start a chat conversation, Continue: +When you start a chat conversation, Shadow Code: 1. **Gathers Context**: Uses any selected code sections and @-mentioned context 2. **Constructs Prompt**: Combines your input with relevant context diff --git a/docs/ide-extensions/chat/how-to-customize.mdx b/docs/ide-extensions/chat/how-to-customize.mdx index 75eb99508de..c1b96c68a98 100644 --- a/docs/ide-extensions/chat/how-to-customize.mdx +++ b/docs/ide-extensions/chat/how-to-customize.mdx @@ -1,6 +1,6 @@ --- title: "How to Customize Chat" -description: "Learn how to customize the Chat feature in Continue to better suit your workflow." +description: "Learn how to customize the Chat feature in Shadow Code to better suit your workflow." --- ## How to Customize Chat diff --git a/docs/ide-extensions/chat/model-setup.mdx b/docs/ide-extensions/chat/model-setup.mdx index 1d3161ff6a4..14bbbd08acb 100644 --- a/docs/ide-extensions/chat/model-setup.mdx +++ b/docs/ide-extensions/chat/model-setup.mdx @@ -1,6 +1,6 @@ --- -title: "Recommended Models for Chat in Continue" -description: "Choose the best chat model for Continue, including hosted high-performance options, fast speed/quality tradeoffs, and local privacy-first models." +title: "Recommended Models for Chat in Shadow Code" +description: "Choose the best chat model for Shadow Code, including hosted high-performance options, fast speed/quality tradeoffs, and local privacy-first models." sidebarTitle: "Recommended Chat Models" --- import { ModelRecommendations } from '/snippets/ModelRecommendations.jsx'; diff --git a/docs/ide-extensions/chat/quick-start.mdx b/docs/ide-extensions/chat/quick-start.mdx index 074efe92ae3..89b3c95b1ce 100644 --- a/docs/ide-extensions/chat/quick-start.mdx +++ b/docs/ide-extensions/chat/quick-start.mdx @@ -1,7 +1,7 @@ --- title: "Chat Mode Quick Start" sidebarTitle: "Quick Start" -description: "Get started with Continue's AI chat assistant to solve coding problems directly in your IDE, with features for code context sharing, codebase search, and applying generated solutions to your files" +description: "Get started with Shadow Code's AI chat assistant to solve coding problems directly in your IDE, with features for code context sharing, codebase search, and applying generated solutions to your files" --- Chat makes it easy to ask for help from an AI without leaving your IDE. Get explanations, generate code, and iterate on solutions conversationally. diff --git a/docs/ide-extensions/edit/context-selection.mdx b/docs/ide-extensions/edit/context-selection.mdx index beb01ce887b..73f6a66c742 100644 --- a/docs/ide-extensions/edit/context-selection.mdx +++ b/docs/ide-extensions/edit/context-selection.mdx @@ -1,7 +1,7 @@ --- title: "Context Selection in Edit Mode" sidebarTitle: "Context Selection" -description: "Learn how Continue's Edit mode selects relevant code context using file content, language server" +description: "Learn how Shadow Code's Edit mode selects relevant code context using file content, language server" --- ## How to Use Text Input diff --git a/docs/ide-extensions/edit/how-it-works.mdx b/docs/ide-extensions/edit/how-it-works.mdx index fe8cea5921c..346c603cd75 100644 --- a/docs/ide-extensions/edit/how-it-works.mdx +++ b/docs/ide-extensions/edit/how-it-works.mdx @@ -5,7 +5,7 @@ description: "Using the highlighted code, the contents of the current file conta ## How Edit Functionality Works -When you start an edit session, Continue: +When you start an edit session, Shadow Code: 1. **Gathers Context**: Uses the highlighted code and the current file contents 2. **Prompts the Model**: Sends the gathered context and your input instructions to the model diff --git a/docs/ide-extensions/edit/how-to-customize.mdx b/docs/ide-extensions/edit/how-to-customize.mdx index a9a7752a5aa..f780ffcb9ce 100644 --- a/docs/ide-extensions/edit/how-to-customize.mdx +++ b/docs/ide-extensions/edit/how-to-customize.mdx @@ -1,6 +1,6 @@ --- title: "How to Customize Edit Functionality" -description: "Learn how to customize the Edit functionality in Continue to better suit your workflow." +description: "Learn how to customize the Edit functionality in Shadow Code to better suit your workflow." sidebarTitle: "Customize Edit" --- diff --git a/docs/ide-extensions/edit/quick-start.mdx b/docs/ide-extensions/edit/quick-start.mdx index 45fa7f8f0ee..e2ca02cb1f3 100644 --- a/docs/ide-extensions/edit/quick-start.mdx +++ b/docs/ide-extensions/edit/quick-start.mdx @@ -1,10 +1,10 @@ --- -title: "Quick Start with Continue Edit" +title: "Quick Start with Shadow Code Edit" sideBarTitle: "Quick Start" -description: "Get started with Continue's Edit feature for making quick, targeted code changes directly in your file using AI suggestions, with keyboard shortcuts for accepting or rejecting modifications" +description: "Get started with Shadow Code's Edit feature for making quick, targeted code changes directly in your file using AI suggestions, with keyboard shortcuts for accepting or rejecting modifications" --- -## How to Continue Edit +## How to Shadow Code Edit Edit is a convenient way to make quick changes to specific code and files. Select code, describe your code changes, and a diff will be streamed inline to your file which you can accept or reject. diff --git a/docs/ide-extensions/install.mdx b/docs/ide-extensions/install.mdx index 143d90b348f..d9e4647ce24 100644 --- a/docs/ide-extensions/install.mdx +++ b/docs/ide-extensions/install.mdx @@ -1,13 +1,13 @@ --- title: "Install" -description: "Get Continue installed in your favorite IDE in just a few steps." +description: "Get Shadow Code installed in your favorite IDE in just a few steps." ---