feat: Update opinionated CLI config to include Opus, Sonnet, and Haiku models in order#10966
feat: Update opinionated CLI config to include Opus, Sonnet, and Haiku models in order#10966
Conversation
Skills can now be invoked as slash commands in the Continue CLI: - /skills - lists all available skills - /skills <name> - invokes a specific skill by name - /<skill-name> - directly invoke any skill as a slash command Skills appear in the slash command autocomplete dropdown alongside system commands, assistant prompts, and invokable rules. The getAllSlashCommands function is now async to support loading skills from the filesystem (.continue/skills/, .claude/skills/, ~/.continue/skills/). Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
…u models in order - Modified updateAnthropicModelInYaml() to create all three Claude 4-6 models - Order: Opus (first/default), Sonnet (second), Haiku (third) - Updated filtering logic to handle all three Claude model types - Updated all test cases to verify the new three-model configuration - This affects the fallback config created when ANTHROPIC_API_KEY is set Generated with Continue (https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
Document the npm link workflow so agents remember to build and link after finishing features, making cn immediately testable. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
… selector Remove the annoying and poorly worded 'Dangerous commands will be blocked regardless of your preference' message. Replace it with a helpful tip about using Shift+Tab to switch to Auto mode (which never asks for permissions). Also clean up all related dead code: hasDynamicEvaluation prop, hasShownDangerousCommandWarning state, and unused ALL_BUILT_IN_TOOLS import. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
|
Docs Review: No documentation updates needed. This PR changes the internal default model configuration for the Anthropic API key flow—adding Opus, Sonnet, and Haiku instead of just Sonnet. The CLI documentation intentionally stays at a high level ("you can use an Anthropic API key directly") without specifying which exact models get auto-configured. This is appropriate because:
|
There was a problem hiding this comment.
2 issues found across 10 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="extensions/cli/src/ui/SlashCommandUI.tsx">
<violation number="1" location="extensions/cli/src/ui/SlashCommandUI.tsx:43">
P2: Async command loading can race with assistant changes or unmounts; the later-resolving promise will overwrite newer commands or trigger setState on an unmounted component. Add a cancellation/active flag in the effect so only the latest request updates state.</violation>
</file>
<file name="extensions/cli/src/ui/UserInput.tsx">
<violation number="1" location="extensions/cli/src/ui/UserInput.tsx:190">
P2: When `assistant`/`isRemoteMode` becomes false, the cached slash commands are never reset, so the UI can keep showing stale assistant/remote commands after switching modes. Reset the ref to the fallback list in the `else` branch.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| getAllSlashCommands(assistant || ({} as AssistantConfig), { | ||
| isRemoteMode, | ||
| }); | ||
| }).then(setAllCommands); |
There was a problem hiding this comment.
P2: Async command loading can race with assistant changes or unmounts; the later-resolving promise will overwrite newer commands or trigger setState on an unmounted component. Add a cancellation/active flag in the effect so only the latest request updates state.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At extensions/cli/src/ui/SlashCommandUI.tsx, line 43:
<comment>Async command loading can race with assistant changes or unmounts; the later-resolving promise will overwrite newer commands or trigger setState on an unmounted component. Add a cancellation/active flag in the effect so only the latest request updates state.</comment>
<file context>
@@ -29,20 +29,19 @@ const SlashCommandUI: React.FC<SlashCommandUIProps> = ({
+ getAllSlashCommands(assistant || ({} as AssistantConfig), {
isRemoteMode,
- });
+ }).then(setAllCommands);
}
-
</file context>
| useEffect(() => { | ||
| if (assistant || isRemoteMode) { | ||
| return getAllSlashCommands(assistant || ({} as AssistantConfig), { | ||
| getAllSlashCommands(assistant || ({} as AssistantConfig), { | ||
| isRemoteMode, | ||
| }).then((commands) => { | ||
| slashCommandsRef.current = commands; | ||
| }); | ||
| } | ||
| }, [assistant?.prompts, assistant?.rules, isRemoteMode]); |
There was a problem hiding this comment.
P2: When assistant/isRemoteMode becomes false, the cached slash commands are never reset, so the UI can keep showing stale assistant/remote commands after switching modes. Reset the ref to the fallback list in the else branch.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At extensions/cli/src/ui/UserInput.tsx, line 190:
<comment>When `assistant`/`isRemoteMode` becomes false, the cached slash commands are never reset, so the UI can keep showing stale assistant/remote commands after switching modes. Reset the ref to the fallback list in the `else` branch.</comment>
<file context>
@@ -180,20 +180,24 @@ const UserInput: React.FC<UserInputProps> = ({
+ { name: "exit", description: "Exit the chat", category: "system" },
+ ]);
+
+ useEffect(() => {
if (assistant || isRemoteMode) {
- return getAllSlashCommands(assistant || ({} as AssistantConfig), {
</file context>
| useEffect(() => { | |
| if (assistant || isRemoteMode) { | |
| return getAllSlashCommands(assistant || ({} as AssistantConfig), { | |
| getAllSlashCommands(assistant || ({} as AssistantConfig), { | |
| isRemoteMode, | |
| }).then((commands) => { | |
| slashCommandsRef.current = commands; | |
| }); | |
| } | |
| }, [assistant?.prompts, assistant?.rules, isRemoteMode]); | |
| useEffect(() => { | |
| if (assistant || isRemoteMode) { | |
| getAllSlashCommands(assistant || ({} as AssistantConfig), { | |
| isRemoteMode, | |
| }).then((commands) => { | |
| slashCommandsRef.current = commands; | |
| }); | |
| return; | |
| } | |
| slashCommandsRef.current = [ | |
| { name: "help", description: "Show help message", category: "system" }, | |
| { name: "clear", description: "Clear the chat history", category: "system" }, | |
| { name: "exit", description: "Exit the chat", category: "system" }, | |
| ]; | |
| }, [assistant?.prompts, assistant?.rules, isRemoteMode]); |
Previously the TUI displayed the first 4 lines of Bash output with no per-line character limit, so a single very long line could flood the terminal. Now each displayed line is capped at 200 characters with a trailing '...' when truncated. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
User messages in the chat history now render with a white background and black text instead of a dim blue dot + gray text. This makes them stand out clearly from assistant responses and tool output. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
User messages in the chat history now render with a white background and black text spanning the full terminal width, instead of a dim blue dot + gray text. Long lines are word-wrapped at word boundaries, and every line is padded to the terminal width so the block is clean and even. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
…nvocation language - Tool description now uses <available_skills> XML structure (from OpenCode) with BLOCKING REQUIREMENT language (from Claude Code) to ensure the model loads skills before responding - Tool output wraps content in <skill name="..."> tags with skill directory context and <skill_files> listing for bundled resources - Add optional when_to_use frontmatter field to SKILL.md for explicit trigger conditions (separates 'what it does' from 'when to invoke it') - Update both core/ and extensions/cli/ implementations to match - Update CLI tests for new output format Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
LLMs often start responses with leading newlines (e.g., '\n\nYou're right...'), which displayed as blank lines above the bullet point in the assistant's message. Two fixes: 1. MemoizedMessage: trimStart() on formatted content before passing to MarkdownRenderer for both regular assistant messages and tool call messages 2. processChunkContent: strip leading whitespace from the first chunk of a streaming response to prevent blank lines during live output The raw content stored in chat history is preserved unchanged to avoid affecting LLM context in multi-turn conversations. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
Plan mode now follows a structured workflow: 1. Agent investigates with read-only tools 2. Agent creates a plan using the Checklist tool 3. Agent calls ExitPlanMode to present plan for user approval 4. On approval, switches to normal mode for implementation Changes: - Add ExitPlanMode tool (src/tools/planMode.ts) - only available in plan mode - Enhance plan mode system message with structured workflow instructions - Register ExitPlanMode in tool index, built-in tool lists, and permission policies - Update existing system message tests to match new plan mode message - Add comprehensive tests for ExitPlanMode tool (9 tests) Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
…ruse - Plan mode no longer references or requires Checklist. The agent presents its plan in prose and calls ExitPlanMode when ready. - Checklist tool description updated to discourage use for simple/single-step tasks. Instructs the agent to keep items high-level (3-7 items) and avoid granular sub-steps or implementation details. - ExitPlanMode tool description simplified — no longer mentions Checklist. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
There was a problem hiding this comment.
4 issues found across 33 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="extensions/cli/src/tools/skills.ts">
<violation number="1" location="extensions/cli/src/tools/skills.ts:127">
P2: Derive the skill directory using both path separators (or a path utility). The current substring("/") logic fails on Windows paths and produces an empty skill directory.</violation>
</file>
<file name="extensions/cli/src/ui/components/MemoizedMessage.tsx">
<violation number="1" location="extensions/cli/src/ui/components/MemoizedMessage.tsx:171">
P2: Guard against non-positive terminal widths; `contentWidth` can be <= 0 (e.g., non‑TTY), which makes the wrap loop non-terminating and can hang rendering. Clamp `contentWidth` to at least 1 (or return early) before the wrapping loop.</violation>
</file>
<file name="extensions/cli/src/tools/readFile.ts">
<violation number="1" location="extensions/cli/src/tools/readFile.ts:140">
P2: Reading an image returns before markFileAsRead, so the Edit tool will still reject the same file as “not read.” If the Read tool now supports images, it should still record the file in readFilesSet to keep the “read before edit” guard consistent.</violation>
</file>
<file name="extensions/cli/src/tools/types.ts">
<violation number="1" location="extensions/cli/src/tools/types.ts:72">
P2: `typeof null === "object"` is `true` in JavaScript. If a `null` value reaches this type guard at runtime (e.g., from an untyped boundary or unexpected tool result), accessing `result.type` will throw a `TypeError`. Add a null check for defensive safety.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| content.push( | ||
| `<skill_files>${skill.files.join(",")}</skill_files>`, | ||
| `<other_instructions>Use the read file tool to access skill files as needed.</other_instructions>`, | ||
| const skillDir = skill.path.substring( |
There was a problem hiding this comment.
P2: Derive the skill directory using both path separators (or a path utility). The current substring("/") logic fails on Windows paths and produces an empty skill directory.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At extensions/cli/src/tools/skills.ts, line 127:
<comment>Derive the skill directory using both path separators (or a path utility). The current substring("/") logic fails on Windows paths and produces an empty skill directory.</comment>
<file context>
@@ -63,20 +116,40 @@ ${skills.map((skill) => `\nname: ${skill.name}\ndescription: ${skill.description
- content.push(
- `<skill_files>${skill.files.join(",")}</skill_files>`,
- `<other_instructions>Use the read file tool to access skill files as needed.</other_instructions>`,
+ const skillDir = skill.path.substring(
+ 0,
+ skill.path.lastIndexOf("/"),
</file context>
| const text = formatMessageContentForDisplay(message.content); | ||
| const termWidth = process.stdout.columns || 80; | ||
| // 1 char padding on each side | ||
| const contentWidth = termWidth - 2; |
There was a problem hiding this comment.
P2: Guard against non-positive terminal widths; contentWidth can be <= 0 (e.g., non‑TTY), which makes the wrap loop non-terminating and can hang rendering. Clamp contentWidth to at least 1 (or return early) before the wrapping loop.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At extensions/cli/src/ui/components/MemoizedMessage.tsx, line 171:
<comment>Guard against non-positive terminal widths; `contentWidth` can be <= 0 (e.g., non‑TTY), which makes the wrap loop non-terminating and can hang rendering. Clamp `contentWidth` to at least 1 (or return early) before the wrapping loop.</comment>
<file context>
@@ -162,19 +164,49 @@ export const MemoizedMessage = memo<MemoizedMessageProps>(
+ const text = formatMessageContentForDisplay(message.content);
+ const termWidth = process.stdout.columns || 80;
+ // 1 char padding on each side
+ const contentWidth = termWidth - 2;
+ // Word-wrap then pad each line to fill the terminal width
+ const wrappedLines: string[] = [];
</file context>
| const contentWidth = termWidth - 2; | |
| const contentWidth = Math.max(termWidth - 2, 1); |
| if (isImageFile(filepath)) { | ||
| return readImageFile(realPath, filepath); | ||
| } |
There was a problem hiding this comment.
P2: Reading an image returns before markFileAsRead, so the Edit tool will still reject the same file as “not read.” If the Read tool now supports images, it should still record the file in readFilesSet to keep the “read before edit” guard consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At extensions/cli/src/tools/readFile.ts, line 140:
<comment>Reading an image returns before markFileAsRead, so the Edit tool will still reject the same file as “not read.” If the Read tool now supports images, it should still record the file in readFilesSet to keep the “read before edit” guard consistent.</comment>
<file context>
@@ -81,6 +135,12 @@ export const readFileTool: Tool = {
const realPath = fs.realpathSync(filepath);
+
+ // Handle image files
+ if (isImageFile(filepath)) {
+ return readImageFile(realPath, filepath);
+ }
</file context>
| if (isImageFile(filepath)) { | |
| return readImageFile(realPath, filepath); | |
| } | |
| if (isImageFile(filepath)) { | |
| markFileAsRead(realPath); | |
| return readImageFile(realPath, filepath); | |
| } |
| export function isMultipartToolResult( | ||
| result: ToolResult, | ||
| ): result is MultipartToolResult { | ||
| return typeof result === "object" && result.type === "multipart"; |
There was a problem hiding this comment.
P2: typeof null === "object" is true in JavaScript. If a null value reaches this type guard at runtime (e.g., from an untyped boundary or unexpected tool result), accessing result.type will throw a TypeError. Add a null check for defensive safety.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At extensions/cli/src/tools/types.ts, line 72:
<comment>`typeof null === "object"` is `true` in JavaScript. If a `null` value reaches this type guard at runtime (e.g., from an untyped boundary or unexpected tool result), accessing `result.type` will throw a `TypeError`. Add a null check for defensive safety.</comment>
<file context>
@@ -41,13 +41,57 @@ export interface ToolRunContext {
+export function isMultipartToolResult(
+ result: ToolResult,
+): result is MultipartToolResult {
+ return typeof result === "object" && result.type === "multipart";
+}
+
</file context>
| return typeof result === "object" && result.type === "multipart"; | |
| return typeof result === "object" && result !== null && result.type === "multipart"; |
After compaction, the compacted history ended with an assistant message, which caused Anthropic/Claude models to reject the request with an 'assistant message prefill' error. This happened because compactChatHistory() produced [system, assistant_summary] and multiple code paths in streamChatResponse.ts could send this directly to the API without appending a user message first. Fix: Add a user 'continue' message to the compacted history so it always ends with a valid user turn: [system, assistant_summary, user_continue]. Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
Summary
Updates the CLI's opinionated default configuration to include all three Claude 4-6 models in the preferred order when using the ANTHROPIC_API_KEY fallback.
Changes
When This Gets Used
This opinionated config is created when:
Testing
Continue Tasks: ✅ 7 no changes — View all
Summary by cubic
Updates the CLI’s default Anthropic config to include Opus, Sonnet, and Haiku in order, adds skill slash commands with direct invocation, enables image-aware file reads, and introduces a structured plan mode with an ExitPlanMode tool and UI polish. Also fixes assistant prefill errors after compaction by appending a user “continue” message.
New Features
Bug Fixes
Written for commit d546f88. Summary will update on new commits.