feat: 添加 RSS 内存指示器并解绑 auto 权限模式与 TRANSCRIPT_CLASSIFIER

- 在 REPL 底栏添加 RSS 内存使用显示,512MB 以下 dimColor,512MB-1GB warning 色,1GB 以上 error 色
- auto 权限模式不再依赖 TRANSCRIPT_CLASSIFIER feature flag,classifier 不可用时 fallback 到 prompting
- Config 面板 defaultPermissionMode 使用类型安全的 permissionModeFromString,显示改用 shortTitle
- bypassPermissions title 缩短为 Bypass 与 shortTitle 一致

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-04-26 15:01:31 +08:00
parent e7e1f7a34d
commit de9494c0a3
5 changed files with 52 additions and 21 deletions

View File

@@ -42,7 +42,7 @@ import { usePrStatus } from '../../hooks/usePrStatus.js'
import { Byline, KeyboardShortcutHint } from '@anthropic/ink'
import { useTerminalSize } from '../../hooks/useTerminalSize.js'
import { useTasksV2 } from '../../hooks/useTasksV2.js'
import { formatDuration } from '../../utils/format.js'
import { formatDuration, formatFileSize } from '../../utils/format.js'
import { VoiceWarmupHint } from './VoiceIndicator.js'
import { useVoiceEnabled } from '../../hooks/useVoiceEnabled.js'
import { useVoiceState } from '../../context/voice.js'
@@ -63,6 +63,26 @@ const NO_OP_SUBSCRIBE = (_cb: () => void) => () => {}
const NULL = () => null
const MAX_VOICE_HINT_SHOWS = 3
const RSS_UPDATE_INTERVAL_MS = 5_000
type RssState = { text: string; level: 'normal' | 'warning' | 'error' }
function useRssDisplay(): RssState | null {
const [state, setState] = useState<RssState | null>(null)
useEffect(() => {
function update(): void {
const mb = process.memoryUsage().rss / (1024 * 1024)
const level = mb >= 1024 ? 'error' : mb >= 512 ? 'warning' : 'normal'
const text = formatFileSize(mb * 1024 * 1024)
setState(prev => (prev?.text === text ? prev : { text, level }))
}
update()
const timer = setInterval(update, RSS_UPDATE_INTERVAL_MS)
return () => clearInterval(timer)
}, [])
return state
}
type Props = {
exitMessage: {
show: boolean
@@ -315,6 +335,7 @@ function ModeIndicator({
const isKillAgentsConfirmShowing = useAppState(
s => s.notifications.current?.key === 'kill-agents-confirm',
)
const rssState = useRssDisplay()
// Derive team info from teamContext (no filesystem I/O needed)
// Match the same logic as TeamStatus to avoid trailing separator
@@ -428,6 +449,18 @@ function ModeIndicator({
/>,
]
: []),
// RSS memory indicator — always visible
...(rssState
? [
<Text
key="rss"
dimColor={rssState.level === 'normal'}
color={rssState.level === 'error' ? 'error' : rssState.level === 'warning' ? 'warning' : undefined}
>
{rssState.text}
</Text>,
]
: []),
]
// Check if any in-process teammates exist (for hint text cycling)

View File

@@ -16,6 +16,7 @@ import {
import chalk from 'chalk';
import {
permissionModeTitle,
permissionModeShortTitle,
permissionModeFromString,
toExternalPermissionMode,
isExternalPermissionMode,
@@ -167,6 +168,9 @@ export function Config({
const thinkingEnabled = useAppState(s => s.thinkingEnabled);
const isFastMode = useAppState(s => (isFastModeEnabled() ? s.fastMode : false));
const promptSuggestionEnabled = useAppState(s => s.promptSuggestionEnabled);
const currentDefaultPermissionMode = permissionModeFromString(
settingsData?.permissions?.defaultMode ?? 'default',
);
// Show auto in the default-mode dropdown when the user has opted in OR the
// config is fully 'enabled' — even if currently circuit-broken ('disabled'),
// an opted-in user should still see it in settings (it's a temporary state).
@@ -558,7 +562,7 @@ export function Config({
{
id: 'defaultPermissionMode',
label: 'Default permission mode',
value: settingsData?.permissions?.defaultMode || 'default',
value: currentDefaultPermissionMode,
options: (() => {
const priorityOrder: PermissionMode[] = ['default', 'plan'];
return [...priorityOrder, ...PERMISSION_MODES.filter(m => !priorityOrder.includes(m))];
@@ -1961,7 +1965,7 @@ export function Config({
</Text>
) : setting.id === 'defaultPermissionMode' ? (
<Text color={isSelected ? 'suggestion' : undefined}>
{permissionModeTitle(setting.value as PermissionMode)}
{permissionModeShortTitle(setting.value as PermissionMode)}
</Text>
) : setting.id === 'autoUpdatesChannel' && autoUpdaterDisabledReason ? (
<Box flexDirection="column">

View File

@@ -30,9 +30,11 @@ export type PermissionMode = InternalPermissionMode
// Runtime validation set: modes that are user-addressable (settings.json
// defaultMode, --permission-mode CLI flag, conversation recovery).
// 'auto' is always available — when TRANSCRIPT_CLASSIFIER is off, the
// classifier is unavailable and auto mode falls back to prompting.
export const INTERNAL_PERMISSION_MODES = [
...EXTERNAL_PERMISSION_MODES,
...(feature('TRANSCRIPT_CLASSIFIER') ? (['auto'] as const) : ([] as const)),
'auto' as const,
] as const satisfies readonly PermissionMode[]
export const PERMISSION_MODES = INTERNAL_PERMISSION_MODES

View File

@@ -64,7 +64,7 @@ const PERMISSION_MODE_CONFIG: Partial<
external: 'acceptEdits',
},
bypassPermissions: {
title: 'Bypass Permissions',
title: 'Bypass',
shortTitle: 'Bypass',
symbol: '⏵⏵',
color: 'error',
@@ -77,17 +77,13 @@ const PERMISSION_MODE_CONFIG: Partial<
color: 'error',
external: 'dontAsk',
},
...(feature('TRANSCRIPT_CLASSIFIER')
? {
auto: {
title: 'Auto mode',
shortTitle: 'Auto',
symbol: '⏵⏵',
color: 'warning' as ModeColorKey,
external: 'default' as ExternalPermissionMode,
},
}
: {}),
auto: {
title: 'Auto',
shortTitle: 'Auto',
symbol: '⏵⏵',
color: 'warning' as ModeColorKey,
external: 'default' as ExternalPermissionMode,
},
}
/**

View File

@@ -57,11 +57,7 @@ export const PermissionsSchema = lazySchema(() =>
'List of permission rules that should always prompt for confirmation',
),
defaultMode: z
.enum(
feature('TRANSCRIPT_CLASSIFIER')
? PERMISSION_MODES
: EXTERNAL_PERMISSION_MODES,
)
.enum(PERMISSION_MODES)
.optional()
.describe('Default permission mode when Claude Code needs access'),
disableBypassPermissionsMode: z