Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { context, contextNonInteractive } from './commands/context/index.js'
import diff from './commands/diff/index.js'
import doctor from './commands/doctor/index.js'
import memory from './commands/memory/index.js'
import mode from './commands/mode/index.js'
import help from './commands/help/index.js'
import ide from './commands/ide/index.js'
import init from './commands/init.js'
Expand Down Expand Up @@ -327,6 +328,7 @@ const COMMANDS = memoize((): Command[] => [
mcp,
memory,
mobile,
mode,
model,
outputStyle,
remoteEnv,
Expand Down
13 changes: 13 additions & 0 deletions src/commands/mode/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { Command } from '../../commands.js'

const mode = {
type: 'local-jsx',
name: 'mode',
description:
'Switch interaction mode (default, gentle, sharp, workhorse, token-saver, super-ai)',
isEnabled: () => true,
argumentHint: '<mode-slug>',
load: () => import('./mode.js'),
} satisfies Command

export default mode
79 changes: 79 additions & 0 deletions src/commands/mode/mode.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import { useMemo } from 'react';
import { Box, Text } from '@anthropic/ink';
import { Select } from '../../components/CustomSelect/select.js';
import type { LocalJSXCommandCall, LocalJSXCommandOnDone } from '../../types/command.js';
import { getCurrentModeSlug, listModes, setCurrentMode } from '../../modes/store.js';

function ModePicker({ onDone }: { onDone: LocalJSXCommandOnDone }) {
const modes = listModes();
const currentSlug = getCurrentModeSlug();

const options = useMemo(
() =>
modes.map(m => ({
label: (
<Text>
{m.icon} {m.name}{' '}
<Text dimColor>
({m.slug}) β€” {m.description}
</Text>
</Text>
),
value: m.slug,
})),
[modes],
);

function handleSelect(slug: string) {
setCurrentMode(slug);
const target = modes.find(m => m.slug === slug);
onDone(`${target?.icon} Mode switched to: ${target?.name} (${target?.slug}) β€” ${target?.description}`, {
display: 'system',
});
}

function handleCancel() {
onDone('Mode selection cancelled.', { display: 'system' });
}

return (
<Box flexDirection="column">
<Box marginBottom={1} flexDirection="column">
<Text color="remember" bold>
Select mode
</Text>
<Text dimColor>Arrow keys to navigate, Enter to select, Esc to cancel.</Text>
</Box>
<Select
defaultValue={currentSlug}
options={options}
onChange={handleSelect}
onCancel={handleCancel}
visibleOptionCount={modes.length}
/>
</Box>
);
}

export const call: LocalJSXCommandCall = async (onDone, _context, args) => {
const slug = args?.trim().toLowerCase();

if (slug) {
const modes = listModes();
const target = modes.find(m => m.slug === slug);
if (!target) {
Comment on lines +59 to +64

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟑 Minor | ⚑ Quick win

Fix case-normalization mismatch for direct custom mode selection.

Line 59 lowercases user input, but custom slugs are stored as-is. A custom slug like MyMode won’t match via /mode MyMode after normalization. Match case-insensitively and pass canonical slug to setCurrentMode.

Suggested fix
 export const call: LocalJSXCommandCall = async (onDone, _context, args) => {
-  const slug = args?.trim().toLowerCase();
+  const rawSlug = args?.trim();
+  const normalizedSlug = rawSlug?.toLowerCase();
 
-  if (slug) {
+  if (normalizedSlug) {
     const modes = listModes();
-    const target = modes.find(m => m.slug === slug);
+    const target = modes.find(m => m.slug.toLowerCase() === normalizedSlug);
     if (!target) {
       const available = modes.map(m => `${m.icon} ${m.slug} β€” ${m.description}`).join('\n');
-      onDone(`Unknown mode: "${slug}"\n\nAvailable modes:\n${available}`, {
+      onDone(`Unknown mode: "${rawSlug}"\n\nAvailable modes:\n${available}`, {
         display: 'system',
       });
       return;
     }
-    setCurrentMode(slug);
+    setCurrentMode(target.slug);
πŸ€– Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/mode/mode.tsx` around lines 59 - 64, The code lowercases the
user input into slug then compares it directly to stored custom mode slugs, so
case-variants like "MyMode" won't match; change the lookup in listModes() to
compare case-insensitively (e.g., compare slug.toLowerCase() against
m.slug.toLowerCase()) and when calling setCurrentMode use the canonical stored
slug (target.slug) rather than the lowercased input; update the block around the
slug variable, the modes.find(...) call, and the setCurrentMode invocation
accordingly.

const available = modes.map(m => `${m.icon} ${m.slug} β€” ${m.description}`).join('\n');
onDone(`Unknown mode: "${slug}"\n\nAvailable modes:\n${available}`, {
display: 'system',
});
return;
}
setCurrentMode(slug);
onDone(`${target.icon} Mode switched to: ${target.name} (${target.slug}) β€” ${target.description}`, {
display: 'system',
});
return;
}

return <ModePicker onDone={onDone} />;
};
181 changes: 181 additions & 0 deletions src/modes/defaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
import type { CCBMode } from './types.js'

const DR_SHARP_SYSTEM_PROMPT = `You are Dr. Sharp, a meticulous code reviewer and diagnostician.

## Core Principles

1. **Diagnose before acting.** Never jump to a fix. Understand the root cause first.
2. **Minimal effective change.** The smallest diff that fully solves the problem wins.
3. **Evidence-based.** Every claim must be backed by code, logs, or behavior you can point to.
4. **No assumptions.** If you're unsure, ask. Never guess about behavior you haven't verified.

## Three-Phase Workflow

### Phase 1: Deep Diagnosis
- Read the relevant code paths end-to-end
- Trace the execution flow from input to output
- Identify the exact point where behavior diverges from expectation
- State your diagnosis clearly before proceeding

### Phase 2: Action Strategy
- List 2-3 possible approaches with trade-offs
- Recommend the minimal effective approach
- Consider: side effects, edge cases, regression risks
- Explain WHY this approach over alternatives

### Phase 3: Mirror Self
- After implementing, re-read the original problem statement
- Verify your fix addresses the root cause, not just the symptom
- Check for related issues the same root cause might trigger
- Run relevant tests to confirm

## Communication Style

- Be direct and specific. No filler.
- Use code references (file:line) when pointing to issues.
- When reviewing: "This will break when X because Y. Fix: Z."
- When diagnosing: "The bug is at X:42. The condition Y evaluates to Z because..."
- Never apologize for finding problems β€” that's the job.

## Red Flags to Always Check

- Error handling: are errors caught, logged, and propagated correctly?
- Edge cases: null, empty, boundary values, concurrent access
- Security: injection, auth bypass, data leaks
- Performance: N+1 queries, unnecessary allocations, missing indexes
- Type safety: any \`as any\` casts, missing null checks, loose types`

export const DEFAULT_MODES: CCBMode[] = [
{
name: 'Default',
slug: 'default',
description: 'Balanced mode for everyday development',
icon: '⚑',
systemPrompt: '',
ui: {
accentColor: '#D77757',
promptPrefix: '',
},
companionSpecies: 'duck',
permissions: {
defaultMode: 'default',
memoryExtract: true,
},
responseStyle: {
verbosity: 'normal',
},
},
{
name: 'Gentle',
slug: 'gentle',
description: 'Patient explanations, great for learning',
icon: '🌸',
companionSpecies: 'cat',
systemPrompt:
'You are in gentle learning mode. Explain concepts clearly with examples. ' +
'When correcting mistakes, be encouraging and explain why. ' +
'Offer to show alternatives before making changes. ' +
'Use analogies to help understand complex concepts.',
ui: {
accentColor: '#E8A0BF',
promptPrefix: 'gentle',
},
permissions: {
defaultMode: 'default',
memoryExtract: true,
},
responseStyle: {
verbosity: 'verbose',
},
},
{
name: 'Dr. Sharp',
slug: 'sharp',
description: 'Strict review, focused on code quality',
icon: 'πŸ”',
companionSpecies: 'owl',
systemPrompt: DR_SHARP_SYSTEM_PROMPT,
ui: {
accentColor: '#5769F7',
promptPrefix: 'sharp',
},
permissions: {
defaultMode: 'default',
memoryExtract: true,
},
responseStyle: {
verbosity: 'normal',
},
},
{
name: 'Workhorse',
slug: 'workhorse',
description: 'Auto-execute, minimal confirmations',
icon: '🐴',
companionSpecies: 'capybara',
systemPrompt:
'You are in workhorse mode. Execute tasks efficiently with minimal back-and-forth. ' +
'Make reasonable assumptions and proceed. ' +
'Only ask for clarification when truly ambiguous. ' +
'Batch related changes together.',
ui: {
accentColor: '#8B7355',
promptPrefix: 'work',
},
permissions: {
defaultMode: 'acceptEdits',
memoryExtract: false,
},
responseStyle: {
verbosity: 'minimal',
},
},
{
name: 'Token Saver',
slug: 'token-saver',
description: 'Minimal replies, save tokens',
icon: 'πŸ’°',
companionSpecies: 'snail',
systemPrompt:
'You are in token-saving mode. ' +
'Give the shortest correct answer. ' +
'Skip explanations unless asked. ' +
'Use code blocks directly without preamble. ' +
'No pleasantries or filler.',
ui: {
accentColor: '#4A7C59',
promptPrefix: 'save',
},
permissions: {
defaultMode: 'acceptEdits',
memoryExtract: false,
},
responseStyle: {
verbosity: 'minimal',
},
},
{
name: 'Super AI',
slug: 'super-ai',
description: 'Deep thinking, comprehensive analysis',
icon: '🧠',
companionSpecies: 'dragon',
systemPrompt:
'You are in super AI mode. Think deeply before responding. ' +
'Consider multiple approaches and explain trade-offs. ' +
'Proactively identify related issues and suggest improvements. ' +
'Use structured analysis for complex problems. ' +
'Reference relevant best practices and patterns.',
ui: {
accentColor: '#9B59B6',
promptPrefix: 'super',
},
permissions: {
defaultMode: 'default',
memoryExtract: true,
},
responseStyle: {
verbosity: 'verbose',
},
},
]
Loading