diff --git a/core/config/profile/doLoadConfig.ts b/core/config/profile/doLoadConfig.ts index b3047e780dc..7f85aeaffba 100644 --- a/core/config/profile/doLoadConfig.ts +++ b/core/config/profile/doLoadConfig.ts @@ -28,6 +28,7 @@ import { getControlPlaneEnv } from "../../control-plane/env.js"; import { PolicySingleton } from "../../control-plane/PolicySingleton"; import { TeamAnalytics } from "../../control-plane/TeamAnalytics.js"; import ContinueProxy from "../../llm/llms/stubs/ContinueProxy"; +import { guideSlashCommand } from "../../promptFiles/guidePrompt"; import { initSlashCommand } from "../../promptFiles/initPrompt"; import { getConfigDependentToolDefinitions } from "../../tools"; import { encodeMCPToolUri } from "../../tools/callTool"; @@ -189,6 +190,7 @@ export default async function doLoadConfig(options: { } newConfig.slashCommands.push(initSlashCommand); + newConfig.slashCommands.push(guideSlashCommand); const proxyContextProvider = newConfig.contextProviders?.find( (cp) => cp.description.title === "continue-proxy", diff --git a/core/config/profile/doLoadConfig.vitest.ts b/core/config/profile/doLoadConfig.vitest.ts index 845a6fc7031..74eb69c7c1b 100644 --- a/core/config/profile/doLoadConfig.vitest.ts +++ b/core/config/profile/doLoadConfig.vitest.ts @@ -95,6 +95,9 @@ vi.mock("../../control-plane/TeamAnalytics", () => ({ vi.mock("../../promptFiles/initPrompt", () => ({ initSlashCommand: { name: "init", description: "init" }, })); +vi.mock("../../promptFiles/guidePrompt", () => ({ + guideSlashCommand: { name: "guide", description: "guide" }, +})); // Mock fs.existsSync to simulate missing file on disk vi.mock("fs", async (importOriginal) => { @@ -133,26 +136,29 @@ const mockControlPlaneClient = { const mockLlmLogger = {} as any; +function createPackageIdentifier(withContent = true): PackageIdentifier { + return { + uriType: "file", + fileUri: "vscode-remote://wsl+Ubuntu/home/user/.continue/agents/test.yaml", + ...(withContent + ? { content: "name: Test\nversion: 1.0.0\nschema: v1\n" } + : {}), + }; +} + describe("doLoadConfig pre-read content bypass", () => { it("should use YAML loading when packageIdentifier has pre-read content, even if file does not exist on disk", async () => { mockLoadYaml.mockClear(); mockLoadJson.mockClear(); - const packageIdentifier: PackageIdentifier = { - uriType: "file", - fileUri: - "vscode-remote://wsl+Ubuntu/home/user/.continue/agents/test.yaml", - content: "name: Test\nversion: 1.0.0\nschema: v1\n", - }; - await doLoadConfig({ ide: mockIde, controlPlaneClient: mockControlPlaneClient, llmLogger: mockLlmLogger, profileId: "test-profile", - overrideConfigYamlByPath: packageIdentifier.fileUri, + overrideConfigYamlByPath: createPackageIdentifier(true).fileUri, orgScopeId: null, - packageIdentifier, + packageIdentifier: createPackageIdentifier(true), }); expect(mockLoadYaml).toHaveBeenCalled(); @@ -163,23 +169,33 @@ describe("doLoadConfig pre-read content bypass", () => { mockLoadYaml.mockClear(); mockLoadJson.mockClear(); - const packageIdentifier: PackageIdentifier = { - uriType: "file", - fileUri: - "vscode-remote://wsl+Ubuntu/home/user/.continue/agents/test.yaml", - }; - await doLoadConfig({ ide: mockIde, controlPlaneClient: mockControlPlaneClient, llmLogger: mockLlmLogger, profileId: "test-profile", - overrideConfigYamlByPath: packageIdentifier.fileUri, + overrideConfigYamlByPath: createPackageIdentifier(false).fileUri, orgScopeId: null, - packageIdentifier, + packageIdentifier: createPackageIdentifier(false), }); expect(mockLoadYaml).not.toHaveBeenCalled(); expect(mockLoadJson).toHaveBeenCalled(); }); + + it("should always include built-in init and guide slash commands", async () => { + const result = await doLoadConfig({ + ide: mockIde, + controlPlaneClient: mockControlPlaneClient, + llmLogger: mockLlmLogger, + profileId: "test-profile", + overrideConfigYamlByPath: createPackageIdentifier(true).fileUri, + orgScopeId: null, + packageIdentifier: createPackageIdentifier(true), + }); + + const commandNames = result.config?.slashCommands.map((cmd) => cmd.name); + expect(commandNames).toContain("init"); + expect(commandNames).toContain("guide"); + }); }); diff --git a/core/promptFiles/guidePrompt.ts b/core/promptFiles/guidePrompt.ts new file mode 100644 index 00000000000..6a662080671 --- /dev/null +++ b/core/promptFiles/guidePrompt.ts @@ -0,0 +1,52 @@ +import { SlashCommandWithSource } from ".."; + +export const GUIDE_PROMPT_CONTENT = ` +You are Continue Guide Mode, an interactive mentor for developers who need help turning a rough idea into a concrete implementation plan. + +Your job is not to jump straight into coding. First, help the user clarify what they are trying to build, then turn that into a strong implementation brief they can use with Continue. + +## Goals +- Help beginners describe their project clearly. +- Ask discovery questions that improve the next coding step. +- Teach the user what information helps AI produce better results. +- End with a concrete, structured specification and a suggested next prompt. + +## How to behave +- Be encouraging, practical, and concise. +- Ask only for information that materially improves the plan. +- Prefer plain language over jargon. +- If the user already answered some questions, do not ask them again. +- If the user gives a vague idea, ask targeted follow-up questions. +- Ask at most 2 questions per response so the interaction stays lightweight. + +## Discovery Areas +Collect enough detail to cover these five areas: +1. What they want to build. +2. Who it is for. +3. What problem it solves. +4. Their experience level. +5. Requirements, constraints, or preferred technologies. + +## Response strategy +- If key information is missing, ask the next most important question or two. +- Once you have enough information, stop asking questions and produce a structured specification. +- If the user asks for direct help before the discovery is complete, give a short answer and then continue the discovery flow. + +## When you have enough information +Produce these sections in order: +1. **Project Summary**: one short paragraph. +2. **Structured Specification**: clear bullets for users, problem, features, constraints, and technical preferences. +3. **Implementation Plan**: 3-6 concrete build steps. +4. **Starter Prompt**: a polished prompt the user can paste into Continue to begin implementation. +5. **What To Clarify Next**: optional, only if meaningful gaps remain. + +## If the user has not provided any project idea yet +Start by asking: "What are you trying to build? Describe your idea in your own words." +`.trim(); + +export const guideSlashCommand: SlashCommandWithSource = { + name: "guide", + description: "Turn a rough idea into a structured build plan", + source: "built-in", + prompt: GUIDE_PROMPT_CONTENT, +}; diff --git a/docs/guides/cli.mdx b/docs/guides/cli.mdx index 797092845b5..cf7c3051945 100644 --- a/docs/guides/cli.mdx +++ b/docs/guides/cli.mdx @@ -4,8 +4,8 @@ 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" --- -import { OSAutoDetect } from '/snippets/OSAutoDetect.jsx' -import CLIInstall from '/snippets/cli-install.mdx' +import { OSAutoDetect } from "/snippets/OSAutoDetect.jsx"; +import CLIInstall from "/snippets/cli-install.mdx"; @@ -39,7 +39,7 @@ Out of the box, `cn` comes with tools that let it understand your codebase, edit - Write a new feature - And a lot more -Use '@' to give it file context, or '/' to run slash commands. +Use '@' to give it file context, or '/' to run slash commands. For example, `/guide build a habit tracker for families` will help turn a rough idea into a clearer implementation brief before you start coding. If you want to resume a previous conversation, run `cn --resume`. diff --git a/docs/reference/json-reference.mdx b/docs/reference/json-reference.mdx index 85ae97e4b42..a8b4fd79719 100644 --- a/docs/reference/json-reference.mdx +++ b/docs/reference/json-reference.mdx @@ -399,6 +399,25 @@ config.json } ``` +### `/guide` + +The guide slash command helps a user turn a rough idea into a more actionable build plan. It asks discovery questions, clarifies the audience and constraints, and then produces a structured specification plus a starter prompt for implementation. + +config.json + +```json +{ + "slashCommands": [ + { + "name": "guide", + "description": "Turn a rough idea into a structured build plan" + } + ] +} +``` + +Example: `/guide build a portfolio site for junior developers` + Example: config.json @@ -480,7 +499,6 @@ Several experimental config parameters are available, as described below: - `defaultContext`: Defines the default context for the LLM. Uses the same format as `contextProviders` but includes an additional `query` property to specify custom query parameters.= - `modelRoles`: - - `inlineEdit`: Model title for inline edits. - `applyCodeBlock`: Model title for applying code blocks. - `repoMapFileSelection`: Model title for repo map selections. @@ -521,18 +539,15 @@ Some deprecated `config.json` settings are no longer stored in config and have b - `disableSessionTitles`/`ui.getChatTitles`: This value will be migrated to the safest merged value (`true` if either are `true`). `getChatTitles` takes precedence if set to false - `tabAutocompleteOptions` - - `useCache`: This value will override during migration. - `disableInFiles`: This value will be migrated to the safest merged value (arrays of file matches merged/deduplicated) - `multilineCompletions`: This value will override during migration. - `experimental` - - `useChromiumForDocsCrawling`: This value will override during migration. - `readResponseTTS`: This value will override during migration. - `ui` - all will override during migration - - `codeBlockToolbarPosition` - `fontSize` - `codeWrap` diff --git a/extensions/cli/src/commands/commands.integration.test.ts b/extensions/cli/src/commands/commands.integration.test.ts index c33ddb632fc..8953c84447b 100644 --- a/extensions/cli/src/commands/commands.integration.test.ts +++ b/extensions/cli/src/commands/commands.integration.test.ts @@ -28,6 +28,7 @@ describe("Slash Commands Integration", () => { expect(commandNames).toContain("login"); expect(commandNames).toContain("logout"); expect(commandNames).toContain("whoami"); + expect(commandNames).toContain("guide"); expect(commandNames).toContain("model"); expect(commandNames).toContain("config"); @@ -50,6 +51,7 @@ describe("Slash Commands Integration", () => { "login", "logout", "whoami", + "guide", "model", "config", ].includes(cmd.name), diff --git a/extensions/cli/src/commands/commands.ts b/extensions/cli/src/commands/commands.ts index 9f057af4483..132f0ef4c25 100644 --- a/extensions/cli/src/commands/commands.ts +++ b/extensions/cli/src/commands/commands.ts @@ -81,6 +81,11 @@ export const SYSTEM_SLASH_COMMANDS: SystemCommand[] = [ description: "Create an AGENTS.md file", category: "system", }, + { + name: "guide", + description: "Turn a rough idea into a structured build plan", + category: "system", + }, { name: "compact", description: "Summarize chat history into a compact form", diff --git a/extensions/cli/src/slashCommands.test.ts b/extensions/cli/src/slashCommands.test.ts index 1d3675b2645..8e32cfe604c 100644 --- a/extensions/cli/src/slashCommands.test.ts +++ b/extensions/cli/src/slashCommands.test.ts @@ -136,6 +136,20 @@ describe("slashCommands", () => { expect(result?.exit).toBeUndefined(); }); + it("should handle /guide command", async () => { + const result = await handleSlashCommands( + "/guide Build a portfolio site for junior developers", + mockAssistant, + ); + + expect(result).toBeDefined(); + expect(result?.newInput).toContain("Continue Guide Mode"); + expect(result?.newInput).toContain( + "Build a portfolio site for junior developers", + ); + expect(result?.output).toBeUndefined(); + }); + it("should handle /info command when not authenticated", async () => { const { isAuthenticated } = await import("./auth/workos.js"); const { services } = await import("./services/index.js"); diff --git a/extensions/cli/src/slashCommands.ts b/extensions/cli/src/slashCommands.ts index 5b472ca7517..45149dd5096 100644 --- a/extensions/cli/src/slashCommands.ts +++ b/extensions/cli/src/slashCommands.ts @@ -25,6 +25,50 @@ import { loadMarkdownSkills, } from "./util/loadMarkdownSkills.js"; +const GUIDE_PROMPT = ` +You are Continue Guide Mode, an interactive mentor for developers who need help turning a rough idea into a concrete implementation plan. + +Your job is not to jump straight into coding. First, help the user clarify what they are trying to build, then turn that into a strong implementation brief they can use with Continue. + +## Goals +- Help beginners describe their project clearly. +- Ask discovery questions that improve the next coding step. +- Teach the user what information helps AI produce better results. +- End with a concrete, structured specification and a suggested next prompt. + +## How to behave +- Be encouraging, practical, and concise. +- Ask only for information that materially improves the plan. +- Prefer plain language over jargon. +- If the user already answered some questions, do not ask them again. +- If the user gives a vague idea, ask targeted follow-up questions. +- Ask at most 2 questions per response so the interaction stays lightweight. + +## Discovery Areas +Collect enough detail to cover these five areas: +1. What they want to build. +2. Who it is for. +3. What problem it solves. +4. Their experience level. +5. Requirements, constraints, or preferred technologies. + +## Response strategy +- If key information is missing, ask the next most important question or two. +- Once you have enough information, stop asking questions and produce a structured specification. +- If the user asks for direct help before the discovery is complete, give a short answer and then continue the discovery flow. + +## When you have enough information +Produce these sections in order: +1. **Project Summary**: one short paragraph. +2. **Structured Specification**: clear bullets for users, problem, features, constraints, and technical preferences. +3. **Implementation Plan**: 3-6 concrete build steps. +4. **Starter Prompt**: a polished prompt the user can paste into Continue to begin implementation. +5. **What To Clarify Next**: optional, only if meaningful gaps remain. + +## If the user has not provided any project idea yet +Start by asking: "What are you trying to build? Describe your idea in your own words." +`.trim(); + type CommandHandler = ( args: string[], assistant: AssistantConfig, @@ -63,6 +107,17 @@ async function handleHelp(_args: string[], _assistant: AssistantConfig) { return { output: helpMessage }; } +async function handleGuide(args: string[]): Promise { + const idea = args.join(" ").trim(); + return { + newInput: idea + ? `${GUIDE_PROMPT} + +${idea}` + : GUIDE_PROMPT, + }; +} + async function handleLogin() { try { const newAuthState = await services.auth.login(); @@ -361,6 +416,7 @@ const commandHandlers: Record = { init: (args, assistant) => { return handleInit(args, assistant); }, + guide: (args) => handleGuide(args), update: () => { return { openUpdateSelector: true }; },