fix: 批量修正 external 字面量

This commit is contained in:
claude-code-best
2026-04-02 17:01:39 +08:00
parent 799dacc407
commit ac1f02958c
31 changed files with 97 additions and 100 deletions

View File

@@ -6,7 +6,7 @@ import { Text, useInterval } from '../ink.js';
// Show DevBar for dev builds or all ants
function shouldShowDevBar(): boolean {
return ("production" as string) === 'development' || ("external" as string) === 'ant';
return ("production" as string) === 'development' || (process.env.USER_TYPE) === 'ant';
}
export function DevBar() {
const $ = _c(5);

View File

@@ -32,7 +32,7 @@ import TextInput from './TextInput.js';
// This value was determined experimentally by testing the URL length limit
const GITHUB_URL_LIMIT = 7250;
const GITHUB_ISSUES_REPO_URL = ("external" as string) === 'ant' ? 'https://github.com/anthropics/claude-cli-internal/issues' : 'https://github.com/anthropics/claude-code/issues';
const GITHUB_ISSUES_REPO_URL = (process.env.USER_TYPE) === 'ant' ? 'https://github.com/anthropics/claude-cli-internal/issues' : 'https://github.com/anthropics/claude-code/issues';
type Props = {
abortSignal: AbortSignal;
messages: Message[];

View File

@@ -87,7 +87,7 @@ export function useMemorySurvey(messages: Message[], isLoading: boolean, hasActi
});
}, []);
const shouldShowTranscriptPrompt = useCallback((selected_0: FeedbackSurveyResponse) => {
if (("external" as string) !== 'ant') {
if ((process.env.USER_TYPE) !== 'ant') {
return false;
}
if (selected_0 !== 'bad' && selected_0 !== 'good') {

View File

@@ -26,7 +26,7 @@ export function createRecentActivityFeed(activities: LogOption[]): FeedConfig {
}
export function createWhatsNewFeed(releaseNotes: string[]): FeedConfig {
const lines: FeedLine[] = releaseNotes.map(note => {
if (("external" as string) === 'ant') {
if ((process.env.USER_TYPE) === 'ant') {
const match = note.match(/^(\d+\s+\w+\s+ago)\s+(.+)$/);
if (match) {
return {
@@ -39,9 +39,9 @@ export function createWhatsNewFeed(releaseNotes: string[]): FeedConfig {
text: note
};
});
const emptyMessage = ("external" as string) === 'ant' ? 'Unable to fetch latest claude-cli-internal commits' : 'Check the Claude Code changelog for updates';
const emptyMessage = (process.env.USER_TYPE) === 'ant' ? 'Unable to fetch latest claude-cli-internal commits' : 'Check the Claude Code changelog for updates';
return {
title: ("external" as string) === 'ant' ? "What's new [ANT-ONLY: Latest CC commits]" : "What's new",
title: (process.env.USER_TYPE) === 'ant' ? "What's new [ANT-ONLY: Latest CC commits]" : "What's new",
lines,
footer: lines.length > 0 ? '/release-notes for more' : undefined,
emptyMessage

View File

@@ -7,7 +7,7 @@ export function MemoryUsageIndicator(): React.ReactNode {
// the hook means the 10s polling interval is never set up in external builds.
// USER_TYPE is a build-time constant, so the hook call below is either always
// reached or dead-code-eliminated — never conditional at runtime.
if (("external" as string) !== 'ant') {
if ((process.env.USER_TYPE) !== 'ant') {
return null;
}

View File

@@ -118,7 +118,7 @@ export function MessageSelector({
...summarizeInputProps,
onChange: setSummarizeFromFeedback
});
if (("external" as string) === 'ant') {
if ((process.env.USER_TYPE) === 'ant') {
baseOptions.push({
value: 'summarize_up_to',
label: 'Summarize up to here',

View File

@@ -184,7 +184,7 @@ export function NativeAutoUpdater({
{autoUpdaterResult?.status === 'install_failed' && <Text color="error" wrap="truncate">
Auto-update failed &middot; Try <Text bold>/status</Text>
</Text>}
{maxVersionIssue && ("external" as string) === 'ant' && <Text color="warning">
{maxVersionIssue && (process.env.USER_TYPE) === 'ant' && <Text color="warning">
Known issue: {maxVersionIssue} &middot; Run{' '}
<Text bold>claude rollback --safe</Text> to downgrade
</Text>}

View File

@@ -294,8 +294,8 @@ function PromptInput({
// otherwise bridge becomes an invisible selection stop.
const bridgeFooterVisible = replBridgeConnected && (replBridgeExplicit || replBridgeReconnecting);
// Tmux pill (ant-only) — visible when there's an active tungsten session
const hasTungstenSession = useAppState(s => ("external" as string) === 'ant' && s.tungstenActiveSession !== undefined);
const tmuxFooterVisible = ("external" as string) === 'ant' && hasTungstenSession;
const hasTungstenSession = useAppState(s => (process.env.USER_TYPE) === 'ant' && s.tungstenActiveSession !== undefined);
const tmuxFooterVisible = (process.env.USER_TYPE) === 'ant' && hasTungstenSession;
// WebBrowser pill — visible when a browser is open
const bagelFooterVisible = useAppState(s => false);
const teamContext = useAppState(s => s.teamContext);
@@ -391,7 +391,7 @@ function PromptInput({
// exist. When only local_agent tasks are running (coordinator/fork mode), the
// pill is absent, so the -1 sentinel would leave nothing visually selected.
// In that case, skip -1 and treat 0 as the minimum selectable index.
const hasBgTaskPill = useMemo(() => Object.values(tasks).some(t => isBackgroundTask(t) && !(("external" as string) === 'ant' && isPanelAgentTask(t))), [tasks]);
const hasBgTaskPill = useMemo(() => Object.values(tasks).some(t => isBackgroundTask(t) && !((process.env.USER_TYPE) === 'ant' && isPanelAgentTask(t))), [tasks]);
const minCoordinatorIndex = hasBgTaskPill ? -1 : 0;
// Clamp index when tasks complete and the list shrinks beneath the cursor
useEffect(() => {
@@ -455,7 +455,7 @@ function PromptInput({
// Panel shows retained-completed agents too (getVisibleAgentTasks), so the
// pill must stay navigable whenever the panel has rows — not just when
// something is running.
const tasksFooterVisible = (runningTaskCount > 0 || ("external" as string) === 'ant' && coordinatorTaskCount > 0) && !shouldHideTasksFooter(tasks, showSpinnerTree);
const tasksFooterVisible = (runningTaskCount > 0 || (process.env.USER_TYPE) === 'ant' && coordinatorTaskCount > 0) && !shouldHideTasksFooter(tasks, showSpinnerTree);
const teamsFooterVisible = cachedTeams.length > 0;
const footerItems = useMemo(() => [tasksFooterVisible && 'tasks', tmuxFooterVisible && 'tmux', bagelFooterVisible && 'bagel', teamsFooterVisible && 'teams', bridgeFooterVisible && 'bridge', companionFooterVisible && 'companion'].filter(Boolean) as FooterItem[], [tasksFooterVisible, tmuxFooterVisible, bagelFooterVisible, teamsFooterVisible, bridgeFooterVisible, companionFooterVisible]);
@@ -1742,7 +1742,7 @@ function PromptInput({
useKeybindings({
'footer:up': () => {
// ↑ scrolls within the coordinator task list before leaving the pill
if (tasksSelected && ("external" as string) === 'ant' && coordinatorTaskCount > 0 && coordinatorTaskIndex > minCoordinatorIndex) {
if (tasksSelected && (process.env.USER_TYPE) === 'ant' && coordinatorTaskCount > 0 && coordinatorTaskIndex > minCoordinatorIndex) {
setCoordinatorTaskIndex(prev => prev - 1);
return;
}
@@ -1750,7 +1750,7 @@ function PromptInput({
},
'footer:down': () => {
// ↓ scrolls within the coordinator task list, never leaves the pill
if (tasksSelected && ("external" as string) === 'ant' && coordinatorTaskCount > 0) {
if (tasksSelected && (process.env.USER_TYPE) === 'ant' && coordinatorTaskCount > 0) {
if (coordinatorTaskIndex < coordinatorTaskCount - 1) {
setCoordinatorTaskIndex(prev => prev + 1);
}
@@ -1813,7 +1813,7 @@ function PromptInput({
}
break;
case 'tmux':
if (("external" as string) === 'ant') {
if ((process.env.USER_TYPE) === 'ant') {
setAppState(prev => prev.tungstenPanelAutoHidden ? {
...prev,
tungstenPanelAutoHidden: false

View File

@@ -143,11 +143,11 @@ function PromptInputFooter({
</Box>
<Box flexShrink={1} gap={1}>
{isFullscreen ? null : <Notifications apiKeyStatus={apiKeyStatus} autoUpdaterResult={autoUpdaterResult} debug={debug} isAutoUpdating={isAutoUpdating} verbose={verbose} messages={messages} onAutoUpdaterResult={onAutoUpdaterResult} onChangeIsUpdating={onChangeIsUpdating} ideSelection={ideSelection} mcpClients={mcpClients} isInputWrapped={isInputWrapped} isNarrow={isNarrow} />}
{("external" as string) === 'ant' && isUndercover() && <Text dimColor>undercover</Text>}
{(process.env.USER_TYPE) === 'ant' && isUndercover() && <Text dimColor>undercover</Text>}
<BridgeStatusIndicator bridgeSelected={bridgeSelected} />
</Box>
</Box>
{("external" as string) === 'ant' && <CoordinatorTaskPanel />}
{(process.env.USER_TYPE) === 'ant' && <CoordinatorTaskPanel />}
</>;
}
export default memo(PromptInputFooter);

View File

@@ -260,7 +260,7 @@ function ModeIndicator({
const expandedView = useAppState(s_3 => s_3.expandedView);
const showSpinnerTree = expandedView === 'teammates';
const prStatus = usePrStatus(isLoading, isPrStatusEnabled());
const hasTmuxSession = useAppState(s_4 => ("external" as string) === 'ant' && s_4.tungstenActiveSession !== undefined);
const hasTmuxSession = useAppState(s_4 => (process.env.USER_TYPE) === 'ant' && s_4.tungstenActiveSession !== undefined);
const nextTickAt = useSyncExternalStore(proactiveModule?.subscribeToProactiveChanges ?? NO_OP_SUBSCRIBE, proactiveModule?.getNextTickAt ?? NULL, NULL);
// biome-ignore lint/correctness/useHookAtTopLevel: feature() is a compile-time constant
const voiceEnabled = feature('VOICE_MODE') ? useVoiceEnabled() : false;
@@ -274,7 +274,7 @@ function ModeIndicator({
const selGetState = useSelection().getState;
const hasNextTick = nextTickAt !== null;
const isCoordinator = feature('COORDINATOR_MODE') ? coordinatorModule?.isCoordinatorMode() === true : false;
const runningTaskCount = useMemo(() => count(Object.values(tasks), t => isBackgroundTask(t) && !(("external" as string) === 'ant' && isPanelAgentTask(t))), [tasks]);
const runningTaskCount = useMemo(() => count(Object.values(tasks), t => isBackgroundTask(t) && !((process.env.USER_TYPE) === 'ant' && isPanelAgentTask(t))), [tasks]);
const tasksV2 = useTasksV2();
const hasTaskItems = tasksV2 !== undefined && tasksV2.length > 0;
const escShortcut = useShortcutDisplay('chat:cancel', 'Chat', 'esc').toLowerCase();
@@ -365,7 +365,7 @@ function ModeIndicator({
// its click-target Box isn't nested inside the <Text wrap="truncate">
// wrapper (reconciler throws on Box-in-Text).
// Tmux pill (ant-only) — appears right after tasks in nav order
...(("external" as string) === 'ant' && hasTmuxSession ? [<TungstenPill key="tmux" selected={tmuxSelected} />] : []), ...(isAgentSwarmsEnabled() && hasTeams ? [<TeamStatus key="teams" teamsSelected={teamsSelected} showHint={showHint && !hasBackgroundTasks} />] : []), ...(shouldShowPrStatus ? [<PrBadge key="pr-status" number={prStatus.number!} url={prStatus.url!} reviewState={prStatus.reviewState!} />] : [])];
...((process.env.USER_TYPE) === 'ant' && hasTmuxSession ? [<TungstenPill key="tmux" selected={tmuxSelected} />] : []), ...(isAgentSwarmsEnabled() && hasTeams ? [<TeamStatus key="teams" teamsSelected={teamsSelected} showHint={showHint && !hasBackgroundTasks} />] : []), ...(shouldShowPrStatus ? [<PrBadge key="pr-status" number={prStatus.number!} url={prStatus.url!} reviewState={prStatus.reviewState!} />] : [])];
// Check if any in-process teammates exist (for hint text cycling)
const hasAnyInProcessTeammates = Object.values(tasks).some(t_2 => t_2.type === 'in_process_teammate' && t_2.status === 'running');
@@ -399,7 +399,7 @@ function ModeIndicator({
}
// Add "↓ to manage tasks" hint when panel has visible rows
const hasCoordinatorTasks = ("external" as string) === 'ant' && getVisibleAgentTasks(tasks).length > 0;
const hasCoordinatorTasks = (process.env.USER_TYPE) === 'ant' && getVisibleAgentTasks(tasks).length > 0;
// Tasks pill renders as a Box sibling (not a parts entry) so its
// click-target Box isn't nested inside <Text wrap="truncate"> — the

View File

@@ -392,7 +392,7 @@ export function Config({
}
}] : []),
// Speculation toggle (ant-only)
...(("external" as string) === 'ant' ? [{
...((process.env.USER_TYPE) === 'ant' ? [{
id: 'speculationEnabled',
label: 'Speculative execution',
value: globalConfig.speculationEnabled ?? true,

View File

@@ -220,7 +220,7 @@ function SpinnerWithVerbInner({
// doesn't trigger re-renders; we pick up updates on the parent's ~25x/turn
// re-render cadence, same as the old ApiMetricsLine did.
let ttftText: string | null = null;
if (("external" as string) === 'ant' && apiMetricsRef?.current && apiMetricsRef.current.length > 0) {
if ((process.env.USER_TYPE) === 'ant' && apiMetricsRef?.current && apiMetricsRef.current.length > 0) {
ttftText = computeTtftText(apiMetricsRef.current);
}

View File

@@ -512,7 +512,7 @@ function OverviewTab({
</Box>
{/* Speculation time saved (ant-only) */}
{("external" as string) === 'ant' && stats.totalSpeculationTimeSavedMs > 0 && <Box flexDirection="row" gap={4}>
{(process.env.USER_TYPE) === 'ant' && stats.totalSpeculationTimeSavedMs > 0 && <Box flexDirection="row" gap={4}>
<Box flexDirection="column" width={28}>
<Text wrap="truncate">
Speculation saved:{' '}
@@ -1151,7 +1151,7 @@ function renderOverviewToAnsi(stats: ClaudeCodeStats): string[] {
lines.push(row('Active days', activeDaysVal, 'Peak hour', peakHourVal));
// Speculation time saved (ant-only)
if (("external" as string) === 'ant' && stats.totalSpeculationTimeSavedMs > 0) {
if ((process.env.USER_TYPE) === 'ant' && stats.totalSpeculationTimeSavedMs > 0) {
const label = 'Speculation saved:'.padEnd(COL1_LABEL_WIDTH);
lines.push(label + h(formatDuration(stats.totalSpeculationTimeSavedMs)));
}

View File

@@ -58,7 +58,7 @@ function getToolBuckets(): ToolBuckets {
},
EXECUTION: {
name: 'Execution tools',
toolNames: new Set([BashTool.name, ("external" as string) === 'ant' ? TungstenTool.name : undefined].filter(n => n !== undefined))
toolNames: new Set([BashTool.name, (process.env.USER_TYPE) === 'ant' ? TungstenTool.name : undefined].filter(n => n !== undefined))
},
MCP: {
name: 'MCP tools',

View File

@@ -114,7 +114,7 @@ export function AttachmentMessage({
// names — shortId is undefined outside ant builds anyway.
const names = attachment.skills.map(s => s.shortId ? `${s.name} [${s.shortId}]` : s.name).join(', ');
const firstId = attachment.skills[0]?.shortId;
const hint = ("external" as string) === 'ant' && !isDemoEnv && firstId ? ` · /skill-feedback ${firstId} 1=wrong 2=noisy 3=good [comment]` : '';
const hint = (process.env.USER_TYPE) === 'ant' && !isDemoEnv && firstId ? ` · /skill-feedback ${firstId} 1=wrong 2=noisy 3=good [comment]` : '';
return <Line>
<Text bold>{attachment.skills.length}</Text> relevant{' '}
{plural(attachment.skills.length, 'skill')}: {names}

View File

@@ -112,7 +112,7 @@ export function bashToolUseOptions({
// Skip when the editable prefix option is already shown — they serve the
// same role and having two identical-looking "don't ask again" inputs is confusing.
const editablePrefixShown = options.some(o => o.value === 'yes-prefix-edited');
if (("external" as string) === 'ant' && !editablePrefixShown && isClassifierPermissionsEnabled() && onClassifierDescriptionChange && !initialClassifierDescriptionEmpty && !descriptionAlreadyExists(classifierDescription ?? '', existingAllowDescriptions) && decisionReason?.type !== 'classifier') {
if ((process.env.USER_TYPE) === 'ant' && !editablePrefixShown && isClassifierPermissionsEnabled() && onClassifierDescriptionChange && !initialClassifierDescriptionEmpty && !descriptionAlreadyExists(classifierDescription ?? '', existingAllowDescriptions) && decisionReason?.type !== 'classifier') {
options.push({
type: 'input',
label: 'Yes, and don\u2019t ask again for',

View File

@@ -96,7 +96,7 @@ export function shouldHideTasksFooter(tasks: {
if (!showSpinnerTree) return false;
let hasVisibleTask = false;
for (const t of Object.values(tasks) as TaskState[]) {
if (!isBackgroundTask(t) || ("external" as string) === 'ant' && isPanelAgentTask(t)) {
if (!isBackgroundTask(t) || (process.env.USER_TYPE) === 'ant' && isPanelAgentTask(t)) {
continue;
}
hasVisibleTask = true;