Skip to content

Commit a83b25a

Browse files
committed
refactor(cli): consolidate agent mode definitions into single source of
truth - Export AGENT_MODES and AGENT_MODE_TO_AGENT from constants.ts - Remove duplicate mode lists from settings.ts, agent-mode-toggle.tsx - Generate slash commands dynamically from AGENT_MODES - Simplify toggleAgentMode to use array index cycling - Use AGENT_MODE_TO_AGENT mapping in resolveAgent
1 parent 67523db commit a83b25a

File tree

6 files changed

+36
-60
lines changed

6 files changed

+36
-60
lines changed

cli/src/components/agent-mode-toggle.tsx

Lines changed: 5 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,12 @@ import React, { useEffect, useRef, useState } from 'react'
33
import { Button } from './button'
44
import { SegmentedControl } from './segmented-control'
55
import { useTheme } from '../hooks/use-theme'
6+
import { AGENT_MODES } from '../utils/constants'
67
import { BORDER_CHARS } from '../utils/ui-constants'
78

89
import type { Segment } from './segmented-control'
910
import type { AgentMode } from '../utils/constants'
1011

11-
const MODE_LABELS: Record<AgentMode, string> = {
12-
DEFAULT: 'DEFAULT',
13-
LITE: 'LITE',
14-
MAX: 'MAX',
15-
PLAN: 'PLAN',
16-
}
17-
18-
const ALL_MODES = Object.keys(MODE_LABELS) as AgentMode[]
19-
2012
export const OPEN_DELAY_MS = 0 // Delay before expanding on hover
2113
export const CLOSE_DELAY_MS = 250 // Delay before collapsing when mouse leaves
2214
export const REOPEN_SUPPRESS_MS = 250 // Time to block reopening after explicit close (prevents flicker)
@@ -108,16 +100,16 @@ export function useHoverToggle() {
108100
export function buildExpandedSegments(currentMode: AgentMode): Segment[] {
109101
return [
110102
// All mode options (disabled for current mode)
111-
...ALL_MODES.map((m) => ({
103+
...AGENT_MODES.map((m) => ({
112104
id: m,
113-
label: MODE_LABELS[m],
105+
label: m,
114106
isBold: false,
115107
disabled: m === currentMode,
116108
})),
117109
// Active mode indicator with reversed arrow
118110
{
119111
id: `active-${currentMode}`,
120-
label: `> ${MODE_LABELS[currentMode]}`,
112+
label: `> ${currentMode}`,
121113
isSelected: true,
122114
defaultHighlighted: true,
123115
},
@@ -220,11 +212,7 @@ export const AgentModeToggle = ({
220212
wrapMode="none"
221213
fg={isCollapsedHovered ? theme.foreground : theme.muted}
222214
>
223-
{isCollapsedHovered ? (
224-
<b>{`< ${MODE_LABELS[mode]}`}</b>
225-
) : (
226-
`< ${MODE_LABELS[mode]}`
227-
)}
215+
{isCollapsedHovered ? <b>{`< ${mode}`}</b> : `< ${mode}`}
228216
</text>
229217
</Button>
230218
)

cli/src/data/slash-commands.ts

Lines changed: 10 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,19 @@
1+
import { AGENT_MODES } from '../utils/constants'
2+
13
export interface SlashCommand {
24
id: string
35
label: string
46
description: string
57
aliases?: string[]
68
}
79

10+
// Generate mode commands from the AGENT_MODES constant
11+
const MODE_COMMANDS: SlashCommand[] = AGENT_MODES.map((mode) => ({
12+
id: `mode:${mode.toLowerCase()}`,
13+
label: `mode:${mode.toLowerCase()}`,
14+
description: `Switch to ${mode} mode`,
15+
}))
16+
817
export const SLASH_COMMANDS: SlashCommand[] = [
918
// {
1019
// id: 'help',
@@ -79,24 +88,5 @@ export const SLASH_COMMANDS: SlashCommand[] = [
7988
description: 'Attach an image file (or Ctrl+V to paste from clipboard)',
8089
aliases: ['img', 'attach'],
8190
},
82-
{
83-
id: 'mode:default',
84-
label: 'mode:default',
85-
description: 'Switch to DEFAULT mode',
86-
},
87-
{
88-
id: 'mode:lite',
89-
label: 'mode:lite',
90-
description: 'Switch to LITE mode',
91-
},
92-
{
93-
id: 'mode:max',
94-
label: 'mode:max',
95-
description: 'Switch to MAX mode',
96-
},
97-
{
98-
id: 'mode:plan',
99-
label: 'mode:plan',
100-
description: 'Switch to PLAN mode',
101-
},
91+
...MODE_COMMANDS,
10292
]

cli/src/hooks/use-send-message.ts

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import { useQueryClient } from '@tanstack/react-query'
22
import { useCallback, useEffect, useRef } from 'react'
3-
import { match } from 'ts-pattern'
43

54
import { setCurrentChatId } from '../project-files'
65
import { createStreamController } from './stream-state'
76
import { useChatStore } from '../state/chat-store'
87
import { getCodebuffClient } from '../utils/codebuff-client'
8+
import { AGENT_MODE_TO_ID } from '../utils/constants'
99
import { createEventHandlerState } from '../utils/create-event-handler-state'
1010
import { createRunConfig } from '../utils/create-run-config'
1111
import { loadAgentDefinitions } from '../utils/load-agent-definitions'
@@ -76,14 +76,7 @@ const resolveAgent = (
7676
? agentDefinitions.find((definition) => definition.id === agentId)
7777
: undefined
7878

79-
const fallbackAgent = match(agentMode)
80-
.with('MAX', () => 'base2-max')
81-
.with('DEFAULT', () => 'base2')
82-
.with('LITE', () => 'base2-lite')
83-
.with('PLAN', () => 'base2-plan')
84-
.exhaustive()
85-
86-
return selectedAgentDefinition ?? agentId ?? fallbackAgent
79+
return selectedAgentDefinition ?? agentId ?? AGENT_MODE_TO_ID[agentMode]
8780
}
8881

8982
// Respect bash context, but avoid sending empty prompts when only images are attached.

cli/src/state/chat-store.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { castDraft } from 'immer'
22
import { create } from 'zustand'
33
import { immer } from 'zustand/middleware/immer'
44

5+
import { AGENT_MODES } from '../utils/constants'
56
import { clamp } from '../utils/math'
67
import { loadModePreference, saveModePreference } from '../utils/settings'
78

@@ -251,15 +252,9 @@ export const useChatStore = create<ChatStore>()(
251252

252253
toggleAgentMode: () =>
253254
set((state) => {
254-
if (state.agentMode === 'DEFAULT') {
255-
state.agentMode = 'LITE'
256-
} else if (state.agentMode === 'LITE') {
257-
state.agentMode = 'MAX'
258-
} else if (state.agentMode === 'MAX') {
259-
state.agentMode = 'PLAN'
260-
} else {
261-
state.agentMode = 'DEFAULT'
262-
}
255+
const currentIndex = AGENT_MODES.indexOf(state.agentMode)
256+
const nextIndex = (currentIndex + 1) % AGENT_MODES.length
257+
state.agentMode = AGENT_MODES[nextIndex]
263258
saveModePreference(state.agentMode)
264259
}),
265260

cli/src/utils/constants.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,16 @@ export const shouldRenderAsSimpleText = (agentType: string): boolean => {
4646
*/
4747
export const MAIN_AGENT_ID = 'main-agent'
4848

49-
const agentModes = ['DEFAULT', 'LITE', 'MAX', 'PLAN'] as const
50-
export type AgentMode = (typeof agentModes)[number]
49+
/**
50+
* Mapping from agent mode to agent ID.
51+
* Single source of truth for all agent modes (order = cycling order).
52+
*/
53+
export const AGENT_MODE_TO_ID = {
54+
DEFAULT: 'base2',
55+
LITE: 'base2-lite',
56+
MAX: 'base2-max',
57+
PLAN: 'base2-plan',
58+
} as const
59+
60+
export type AgentMode = keyof typeof AGENT_MODE_TO_ID
61+
export const AGENT_MODES = Object.keys(AGENT_MODE_TO_ID) as AgentMode[]

cli/src/utils/settings.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import fs from 'fs'
22
import path from 'path'
33

44
import { getConfigDir } from './auth'
5+
import { AGENT_MODES } from './constants'
56
import { logger } from './logger'
67

78
import type { AgentMode } from './constants'
@@ -14,8 +15,6 @@ export interface Settings {
1415
// Add new settings here over time
1516
}
1617

17-
const VALID_MODES: AgentMode[] = ['DEFAULT', 'LITE', 'MAX', 'PLAN']
18-
1918
/**
2019
* Get the settings file path
2120
*/
@@ -63,7 +62,7 @@ const validateSettings = (parsed: unknown): Settings => {
6362
// Validate mode
6463
if (
6564
typeof obj.mode === 'string' &&
66-
VALID_MODES.includes(obj.mode as AgentMode)
65+
AGENT_MODES.includes(obj.mode as AgentMode)
6766
) {
6867
settings.mode = obj.mode as AgentMode
6968
}

0 commit comments

Comments
 (0)